/************************************************************************
**                                                                     **
**                   The YapTab/YapOr/OPTYap systems                   **
**                                                                     **
** YapTab extends the Yap Prolog engine to support sequential tabling  **
** YapOr extends the Yap Prolog engine to support or-parallelism       **
** OPTYap extends the Yap Prolog engine to support or-parallel tabling **
**                                                                     **
**                                                                     **
**      YAP Prolog was developed at University of Porto, Portugal      **
**                                                                     **
************************************************************************/

/************************************************************************
**                        Atomic locks for X86                         **
************************************************************************/

typedef struct {
    volatile unsigned int lock;
} spinlock_t;

static inline int
spin_trylock(spinlock_t *lock)
{
    char tmp = 1;
    __asm__ __volatile__(
			 "xchgb %b0, %1"
			 : "=q"(tmp), "=m"(lock->lock)
			 : "0"(tmp) : "memory");
    return tmp == 0;
}

static inline void
spin_unlock(spinlock_t *lock)
{
    /* To unlock we move 0 to the lock.
     * On i386 this needs to be a locked operation
     * to avoid Pentium Pro errata 66 and 92.
     */
#if defined(__x86_64__)
    __asm__ __volatile__("" : : : "memory");
    *(unsigned char*)&lock->lock = 0;
#else
    char tmp = 0;
    __asm__ __volatile__(
			 "xchgb %b0, %1"
			 : "=q"(tmp), "=m"(lock->lock)
			 : "0"(tmp) : "memory");
#endif
}


#define INIT_LOCK(LOCK_VAR)    ((LOCK_VAR) = 0)
#define TRY_LOCK(LOCK_VAR)  spin_trylock((spinlock_t *)(LOCK_VAR))

//#define DEBUG_LOCKS 1
#if DEBUG_LOCKS

extern int debug_locks;
#define LOCK(LOCK_VAR)         do {	\
    if (debug_locks) fprintf(stderr,"[%d] %s:%d: LOCK(%p)\n",	\
	    (int)pthread_self(),		     \
	    __BASE_FILE__, __LINE__,&(LOCK_VAR));    \
             if (TRY_LOCK(&(LOCK_VAR))) break;      \
		                 while (IS_LOCKED(LOCK_VAR)) continue;  \
                               } while (1)
#define IS_LOCKED(LOCK_VAR)    ((LOCK_VAR) != 0)
#define IS_UNLOCKED(LOCK_VAR)  ((LOCK_VAR) == 0)
#define UNLOCK(LOCK_VAR)       if (debug_locks) fprintf(stderr,"[%d] %s:%d: UNLOCK(%p)\n", \
				       (int)pthread_self(),		\
		      __BASE_FILE__, __LINE__,&(LOCK_VAR)); \
	     spin_unlock((spinlock_t *)&(LOCK_VAR))
#else
#define LOCK(LOCK_VAR)         { do {					\
                                 if (TRY_LOCK(&(LOCK_VAR))) break;      \
		                 while (IS_LOCKED(LOCK_VAR)) continue;  \
				 } while (1); }
#define IS_LOCKED(LOCK_VAR)    ((LOCK_VAR) != 0)
#define IS_UNLOCKED(LOCK_VAR)  ((LOCK_VAR) == 0)
#define UNLOCK(LOCK_VAR)       spin_unlock((spinlock_t *)&(LOCK_VAR))
#endif

/* the code that follows has been adapted from the Erlang sources */

typedef struct {
    volatile int lock;
} rwlock_t;

#define RWLOCK_OFFSET (1<<24)

static inline void
init_rwlock(rwlock_t *lock)
{
    lock->lock = 0;
}

static inline void
read_unlock(rwlock_t *lock)
{
    __asm__ __volatile__(
			 "lock; decl %0"
			 : "=m"(lock->lock)
			 : "m"(lock->lock)
			 );
}

static inline int
read_trylock(rwlock_t *lock)
{
    int tmp;

    tmp = 1;
    __asm__ __volatile__(
			 "lock; xaddl %0, %1"
			 : "=r"(tmp)
			 : "m"(lock->lock), "0"(tmp));
    /* tmp is now the lock's previous value */
    if (__builtin_expect(tmp >= 0, 1))
      return 1;
    read_unlock(lock);
    return 0;
}

static inline int
read_is_locked(rwlock_t *lock)
{
    return lock->lock < 0;
}

static inline void
read_lock(rwlock_t *lock)
{
    for(;;) {
      if (__builtin_expect(read_trylock(lock) != 0, 1))
	break;
      do {
	__asm__ __volatile__("rep;nop" : "=m"(lock->lock) : : "memory");
      } while (read_is_locked(lock));
    }
}

static inline void
write_unlock(rwlock_t *lock)
{
    __asm__ __volatile__(
			 "lock; addl %2,%0"
			 : "=m"(lock->lock)
			 : "m"(lock->lock), "i"(RWLOCK_OFFSET));
}

static inline int
write_trylock(rwlock_t *lock)
{
    int tmp;

    tmp = -RWLOCK_OFFSET;
    __asm__ __volatile__(
			 "lock; xaddl %0, %1"
			 : "=r"(tmp)
			 : "m"(lock->lock), "0"(tmp));
    /* tmp is now the lock's previous value */
    if (__builtin_expect(tmp == 0, 1))
      return 1;
    write_unlock(lock);
    return 0;
}

static inline int
write_is_locked(rwlock_t *lock)
{
    return lock->lock != 0;
}

static inline void
write_lock(rwlock_t *lock)
{
    for(;;) {
      if (__builtin_expect(write_trylock(lock) != 0, 1))
	break;
      do {
	__asm__ __volatile__("rep;nop" : "=m"(lock->lock) : : "memory");
      } while (write_is_locked(lock));
    }
}

#define INIT_RWLOCK(lock) init_rwlock(&(lock))
#define READ_LOCK(lock) read_lock(&(lock))
#define READ_UNLOCK(lock) read_unlock(&(lock))
#define WRITE_LOCK(lock) write_lock(&(lock))
#define WRITE_UNLOCK(lock) write_unlock(&(lock))


#if THREADS

/* pthread mutex */

#if DEBUG_LOCKS

#define MUTEX_LOCK(LOCK_VAR)     ((debug_locks ?  fprintf(stderr,"[%d] %s:%d: MULOCK(%p)\n",  (int)pthread_self(), \
							  __BASE_FILE__, __LINE__,(LOCK_VAR)) : 1), \
				                   pthread_mutex_lock((LOCK_VAR)) )
#define MUTEX_TRYLOCK(LOCK_VAR) pthread_mutex_trylock(LOCK_VAR)
#define MUTEX_UNLOCK(LOCK_VAR) if       ((debug_locks ?  fprintf(stderr,"[%d] %s:%d: MUNLOCK(%p)\n",  (int)pthread_self(), \
							  __BASE_FILE__, __LINE__,(LOCK_VAR)) : 1),     \
					 pthread_mutex_unlock((LOCK_VAR)) )
#else
#define MUTEX_LOCK(LOCK_VAR) pthread_mutex_lock(LOCK_VAR)
#define MUTEX_TRYLOCK(LOCK_VAR) pthread_mutex_trylock(LOCK_VAR)
#define MUTEX_UNLOCK(LOCK_VAR) pthread_mutex_unlock(LOCK_VAR)
#endif

#else

#define MUTEX_LOCK(LOCK_VAR) 
#define MUTEX_TRYLOCK(LOCK_VAR) 
#define MUTEX_UNLOCK(LOCK_VAR) 


#endif