- 利用原子操作控制变量 uint32_t futex ,控制不同的线程抢占。
- futex = 0 ,说明无人抢占。
- futex = 1 ,只有一个线程抢占(拿到了锁)。
- 其它线程看到 futex 不等于 0 时 ,需要原子设置 futex=2,标记抢占;同时检查设置后获取的 old value ,如果不为0,说明有人先一步抢占,自己需要 wait 。
- futex = 2 ,有线程抢占和等待。
- 此时也需要原子设置 futex=2,检查设置后获取的 old value ,判断自己是不是先一步抢占到。如果不为0,需要wait。
- 抢占锁的线程 释放锁时,调用 futex_wake 唤醒 futex 变量上的线程。
- 利用 futex_wait / futex_wake 控制线程等待和唤醒。
atomic_compare_and_exchange_bool_acq(mem, newval, oldval) : 如果交换成功,返回0,失败返回非0/* Atomically store NEWVAL in *MEM if *MEM is equal to OLDVAL. Return zero if *MEM was changed or non-zero if no exchange happened. */atomic_exchange_acq(mem, newvalue):/* Store NEWVALUE in *MEM and return the old value. */futex_wait, futex_wake 是系统调用。// uaddr指向一个地址,val代表这个地址期待的值,当*uaddr==val时,才会进行waitint futex_wait(int *uaddr, int val);// 唤醒n个在uaddr指向的锁变量上挂起等待的进程int futex_wake(int *uaddr, int n);# define LLL_MUTEX_LOCK(mutex) \ lll_lock ((mutex) ->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex))lock(mutex){ /* Normal mutex. */ LLL_MUTEX_LOCK (mutex); /* 等价于: lll_lock ((mutex) ->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex)) */ /* Record the ownership. */ mutex->__data.__owner = id;#ifndef NO_INCR ++mutex->__data.__nusers;#endif}void lll_lock(uint32_t* futex){ /* 成功设置为 1 时返回 0 ,表示第一次有人 lock;失败返回非0,说明有人用了 1. 期望是 old value = 0, 设置 new value = 1,抢占 futex。 2. 如果有人用了(value=1, 或者 2) ,就需要进入 wait */ if (atomic_compare_and_exchange_bool_acq(futex, 1, 0/* old value */)) { lock_wait(futex); }}void lock_wait(uint32_t*futex){ if (*futex == 2) { /* 小优化: 快速进入 wait*/ futex_wait(futex, 2); } /* 有3种返回值: =0 时,无人占用。 =1 时,有其它人抢占。 =2 时,有其它人抢占和等待。 如果 atomic_exchange_acq 返回值是0,说明自己是第一个设置 futex=2,可以推出 wait。 1. 设置 futex = 2, atomic_exchange_acq 返回非0(1,2),进入 wait;否则推出循环。 2. 每次唤醒时,重复设置 futex = 2,尝试抢占。 */ while (atomic_exchange_acq(futex, 2)) { futex_wait(futex, 2); }}void unlock(mutex) { mutex->__data.__owner = 0; if (decr) /* One less user. */ --mutex->__data.__nusers; /* Unlock. */ lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));}void lll_unlock(uint32_t* futex){ int oldval = atomic_exchange_acq(futex, 0); /* 如果仍旧为 1 ,说明之前没人来 lock,只有自己在用,可以不调用 wake 。 1. 解锁时,直接设置为0,同时获取 old value。 2. 如果 old value > 1 (实际是等于2),说明有人在 wait */ if (__unlikely(oldval > 1)) { futex_wake(futex, 1/* number of waiters */); }}