标签:
Spinlock
an non-scalable implementation in C++
# if defined __GNUC__
# include <pthread.h>
class Spinlock {
# if (__GNUC__ >= 4)
private:
pthread_spinlock_t _spinlock;
public:
Spinlock() {
pthread_spin_init(&_spinlock, PTHREAD_PROCESS_PRIVATE);
}
~Spinlock() {
pthread_spin_destroy(&_spinlock);
}
void lock() {
pthread_spin_lock(&_spinlock);
}
void unlock() {
pthread_spin_unlock(&_spinlock);
}
# else
private:
volatile int _inter_lock;
public:
Spinlock() : _inter_lock(0) { }
void lock() {
register int temp;
__asm__ __volatile__ (
"1: \n"
" cmp $0, %0 \n" // cmp(0, _inter_lock)
" je 2f \n" // if _inter_lock == 0, goto 2
" pause \n"
" jmp 1b \n" // goto 1
"2: \n"
" movl $1, %1 \n" // temp = 1
" xchg %0, %1 \n" // exchange(_inter_lock, temp)
" test %1, %1 \n" // test(temp, temp), do AND
" jne 1b \n" // if temp == 1, goto 1
: "=m"(_inter_lock), "=r"(temp)
);
// A note on ‘pause‘:
// ‘pause‘ gives hint to processor that improves performance
// of spin-wait loops
}
void unlock() {
// _inter_lock = 0;
__asm__ __volatile__ (
" movl $0, %0 \n"
: "=m"(_inter_lock)
);
}
# endif // __GNUC__ >= 4
private:
// non-copyable
Spinlock(const Spinlock&);
Spinlock& operator=(const Spinlock&);
};
# endif // __GNUC__
Pseudocode for ticket locks in Linux (The ticket lock is the default lock since kernel version 2.6.25 , released in April 2008)
struct spinlock_t { int current_ticket; int next_ticket; }; void spin_lock (spinlock_t *lock) { int t = atomic_fetch_and_inc(&lock->next_ticket); while (t != lock->current_ticket) ; /* spin */ } void spin_unlock (spinlock_t *lock) { lock->current_ticket++; }
MCS spinlock (scalable, for implementing Linux mutex)
// atomic exchange: // *ptr, val = val, *ptr // return val static inline long xchg(long *ptr, long val) { __asm__ volatile( "lock; xchgq %0, %1\n\t" : "+m" (*ptr), "+r" (val) : : "memory", "cc"); return val; } // cmpxchg: // if (*ptr == old) { // *ptr = new; // return old; // } else // return *ptr static inline long cmpxchg(long *ptr, long old, long val) { uint64_t out; __asm__ volatile( "lock; cmpxchgq %2, %1" : "=a" (out), "+m" (*ptr) : "q" (val), "0"(old) : "memory"); return out; } struct qnode { volatile void *next; volatile char locked; // 1 if lock acquired char __pad[0] __attribute__((aligned(CACHELINE))); }; typedef struct { // tail of queue of threads holding or waiting the lock struct qnode *tail __attribute__((aligned(64))); int lock_idx __attribute__((aligned(64))); } mcslock_t; // initialize main lock static inline void mcs_init(mcslock_t *l) { l->tail = NULL; } static inline void mcs_lock(mcslock_t *l, volatile struct qnode *mynode) { struct qnode *predecessor; mynode->next = NULL; predecessor = (struct qnode *)xchg((long *)&l->tail, (long)mynode); if (predecessor) { mynode->locked = 1; // mark the lock as taken asm volatile("":::"memory") // barrier predecessor->next = mynode; while (mynode->locked) // busy waiting nop_pause(); } } static inline void mcs_unlock(mcslock_t *l, volatile struct qnode *mynode) { if (!mynode->next) { // if no cores waiting // atomic lock-free pop: // if we are the only node in the queue, reset l->tail and return if (cmpxchg((long *)&l->tail, (long)mynode, 0) == (long)mynode) return; while (!mynode->next) nop_pause(); } ((struct qnode *)mynode->next)->locked = 0; // handoff lock to the successor }
For scalable locks
Non-scalable locks are dangerous
my notes on scalable spinlocks
Mutex
Linux kernel bug: A surprise with mutexes and reference counts
Russ Cox on debugging an interesting concurrency bug
adaptive mutex:
标签:
原文地址:http://www.cnblogs.com/william-cheung/p/5878764.html