下面的代碼實現了一個簡陋的自旋鎖。
由於僅僅是用於實驗的原型代碼,
所以可能包含許多錯誤和不適當的地方。
如果在信號處理函數中使用該鎖,
可能會死鎖。
該代碼用於Linux環境,在fedora11中測試過。
spin_lock.h:
=========================================================================
// 2011年 09月 13日 星期二 10:43:03 CST
// author: 李小丹(Li Shao Dan) 字 殊恆(shuheng)
// K.I.S.S
// S.P.O.T
#ifndef MEM_LOCK_H
#define MEM_LOCK_H
#include <unistd.h>
#include <sched.h>
#include <sys/syscall.h>
#if ((defined(__GNUC__) && \
((__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)))))
//#if 0
#pragma message "builtin on intel"
#define atomic_test_and_set(a) __sync_lock_test_and_set(a, 1)
#define atomic_clear(a) __sync_lock_release(a);
#elif (defined(__i386__) || defined(__x86_64__))
#pragma message "not builtin on intel"
inline static unsigned long
atomic_test_and_set(volatile unsigned long *a)
{
const unsigned long n = 1, old = 0;
unsigned long ret;
__asm__ __volatile__ (
" lock; "
" cmpxchgl %3, %1; "
: "=a" (ret)
: "m" (*a), "a" (old), "r" (n)
: "memory", "cc"
);
return ret;
}
inline static void atomic_clear(volatile unsigned long *a)
{
assert(*a);
unsigned long ret, n = 0;
__asm__ __volatile__ (
" lock; "
"xchgl %0, %1"
: "=r" (ret)
: "m" (*a), "0" (n)
: "memory"
);
}
#endif
typedef struct {
volatile unsigned long tid;
volatile unsigned long lock;
volatile unsigned long recur;
} spin_lock_t;
inline static void spin_lock_init(spin_lock_t *);
inline static int spin_lock_lock(spin_lock_t *);
inline static int spin_lock_unlock(spin_lock_t *);
inline static void spin_lock_init(spin_lock_t *lk)
{
lk->tid = 0;
lk->lock = 0;
lk->recur = 0;
}
#define SPIN_YIELD_INTERVAL 63U
inline static int spin_lock_lock(spin_lock_t *lk)
{
pid_t now = syscall(SYS_gettid);
int c = 0;
for(;;) {
if(!atomic_test_and_set(&lk->lock)) {
// XXX: at the moment,
// receive a signal and signal handler use the spin lock,
// dead lock;
lk->tid = (unsigned long)now;
lk->recur = 1;
return 0;
}
if(lk->tid == (unsigned long)now)
return ++lk->recur;
if(!(++c & SPIN_YIELD_INTERVAL))
sched_yield();
}
return 0;
}
inline static int spin_lock_unlock(spin_lock_t *lk)
{
unsigned long now =
(unsigned long)syscall(SYS_gettid);
if(lk->tid != now)
return -1;
if(!--lk->recur) {
lk->tid = 0;
atomic_clear(&lk->lock);
}
return 0;
}
#endif