fix RW_locks for x86 (code from Erlang).
This commit is contained in:
parent
fa42de1025
commit
bd502a39ac
@ -7,7 +7,7 @@
|
|||||||
** OPTYap extends the Yap Prolog engine to support or-parallel tabling **
|
** OPTYap extends the Yap Prolog engine to support or-parallel tabling **
|
||||||
** **
|
** **
|
||||||
** **
|
** **
|
||||||
** Yap Prolog was developed at University of Porto, Portugal **
|
** YAP Prolog was developed at University of Porto, Portugal **
|
||||||
** **
|
** **
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
@ -34,18 +34,18 @@ static inline void
|
|||||||
spin_unlock(spinlock_t *lock)
|
spin_unlock(spinlock_t *lock)
|
||||||
{
|
{
|
||||||
/* To unlock we move 0 to the lock.
|
/* To unlock we move 0 to the lock.
|
||||||
* On i386 this needs to be a locked operation
|
* On i386 this needs to be a locked operation
|
||||||
* to avoid Pentium Pro errata 66 and 92.
|
* to avoid Pentium Pro errata 66 and 92.
|
||||||
*/
|
*/
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__)
|
||||||
__asm__ __volatile__("" : : : "memory");
|
__asm__ __volatile__("" : : : "memory");
|
||||||
*(unsigned char*)&lock->lock = 0;
|
*(unsigned char*)&lock->lock = 0;
|
||||||
#else
|
#else
|
||||||
char tmp = 0;
|
char tmp = 0;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"xchgb %b0, %1"
|
"xchgb %b0, %1"
|
||||||
: "=q"(tmp), "=m"(lock->lock)
|
: "=q"(tmp), "=m"(lock->lock)
|
||||||
: "0"(tmp) : "memory");
|
: "0"(tmp) : "memory");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,68 +60,111 @@ spin_unlock(spinlock_t *lock)
|
|||||||
#define IS_UNLOCKED(LOCK_VAR) ((LOCK_VAR) == 0)
|
#define IS_UNLOCKED(LOCK_VAR) ((LOCK_VAR) == 0)
|
||||||
#define UNLOCK(LOCK_VAR) spin_unlock((spinlock_t *)&(LOCK_VAR))
|
#define UNLOCK(LOCK_VAR) spin_unlock((spinlock_t *)&(LOCK_VAR))
|
||||||
|
|
||||||
/* This code has been copied from the sources of the Linux kernel */
|
/* the code that follows has been adapted from the Erlang sources */
|
||||||
|
|
||||||
/*
|
typedef struct {
|
||||||
* On x86, we implement read-write locks as a 32-bit counter
|
volatile int lock;
|
||||||
* with the high bit (sign) being the "contended" bit.
|
} rwlock_t;
|
||||||
*
|
|
||||||
* The inline assembly is non-obvious. Think about it.
|
|
||||||
*
|
|
||||||
* Changed to use the same technique as rw semaphores. See
|
|
||||||
* semaphore.h for details. -ben
|
|
||||||
*/
|
|
||||||
/* the spinlock helpers are in arch/i386/kernel/semaphore.S */
|
|
||||||
|
|
||||||
typedef struct { unsigned long a[100]; } __dummy_lock_t;
|
#define RWLOCK_OFFSET (1<<24)
|
||||||
#define __dummy_lock(lock) (*(__dummy_lock_t *)(lock))
|
|
||||||
|
|
||||||
typedef struct { volatile unsigned int lock; } rwlock_t;
|
static inline void
|
||||||
|
init_rwlock(rwlock_t *lock)
|
||||||
#define RW_LOCK_BIAS 0x01000000
|
|
||||||
#define RW_LOCK_BIAS_STR "0x01000000"
|
|
||||||
|
|
||||||
#define RW_LOCK_UNLOCKED RW_LOCK_BIAS
|
|
||||||
|
|
||||||
#define __build_read_lock(rw, helper) \
|
|
||||||
asm volatile("lock\n" \
|
|
||||||
"subl $1,(%0)\n\t" \
|
|
||||||
"js 2f\n" \
|
|
||||||
"1:\n" \
|
|
||||||
".section .text.lock,\"ax\"\n" \
|
|
||||||
"2:\tcall __read_lock_failed\n\t" \
|
|
||||||
"jmp 1b\n" \
|
|
||||||
".previous" \
|
|
||||||
::"a" (rw) : "memory")
|
|
||||||
|
|
||||||
#define __build_write_lock(rw, helper) \
|
|
||||||
asm volatile("lock\n"\
|
|
||||||
"subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
|
|
||||||
"jnz 2f\n" \
|
|
||||||
"1:\n" \
|
|
||||||
".section .text.lock,\"ax\"\n" \
|
|
||||||
"2:\tcall __write_lock_failed\n\t" \
|
|
||||||
"jmp 1b\n" \
|
|
||||||
".previous" \
|
|
||||||
::"a" (rw) : "memory")
|
|
||||||
|
|
||||||
static inline void read_lock(rwlock_t *rw)
|
|
||||||
{
|
{
|
||||||
__build_read_lock(rw, "__read_lock_failed");
|
lock->lock = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void write_lock(rwlock_t *rw)
|
static inline void
|
||||||
|
read_unlock(rwlock_t *lock)
|
||||||
{
|
{
|
||||||
__build_write_lock(rw, "__write_lock_failed");
|
__asm__ __volatile__(
|
||||||
|
"lock; decl %0"
|
||||||
|
: "=m"(lock->lock)
|
||||||
|
: "m"(lock->lock)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
read_trylock(rwlock_t *lock)
|
||||||
|
{
|
||||||
|
int tmp;
|
||||||
|
|
||||||
#define READ_LOCK(X) read_lock(&(X))
|
tmp = 1;
|
||||||
#define WRITE_LOCK(X) write_lock(&(X))
|
__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;
|
||||||
|
}
|
||||||
|
|
||||||
#define READ_UNLOCK(rw) asm volatile("lock ; incl %0" :"=m" (__dummy_lock(&(rw))))
|
static inline int
|
||||||
#define WRITE_UNLOCK(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" (__dummy_lock(&(rw))))
|
read_is_locked(rwlock_t *lock)
|
||||||
|
{
|
||||||
|
return lock->lock < 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define INIT_RWLOCK(RW) (RW).lock = RW_LOCK_UNLOCKED
|
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))
|
||||||
|
Reference in New Issue
Block a user