最近有空看了一下nginx的源码,看到了其读写锁的实现比较有意思,所以记录一下。
首先看下nginx 对cas操作的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static ngx_inline ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old, ngx_atomic_uint_t set) { u_char res; __asm__ volatile (
NGX_SMP_LOCK " cmpxchgq %3, %1; " " sete %0; "
: "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "cc", "memory");
return res; }
|
接下来是nginx对写锁的实现,nginx对锁的等待主要使用自旋等待
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #define NGX_RWLOCK_SPIN 2048 #define NGX_RWLOCK_WLOCK ((ngx_atomic_uint_t) -1)
void ngx_rwlock_wlock(ngx_atomic_t *lock) { ngx_uint_t i, n; for ( ;; ) { if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) { return; }
if (ngx_ncpu > 1) { for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {
for (i = 0; i < n; i++) { ngx_cpu_pause(); }
if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) { return; } } } ngx_sched_yield(); } }
|
下面是nginx对读锁的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| void ngx_rwlock_rlock(ngx_atomic_t *lock) { ngx_uint_t i, n; ngx_atomic_uint_t readers;
for ( ;; ) { readers = *lock; if (readers != NGX_RWLOCK_WLOCK && ngx_atomic_cmp_set(lock, readers, readers + 1)) { return; }
if (ngx_ncpu > 1) {
for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {
for (i = 0; i < n; i++) { ngx_cpu_pause(); }
readers = *lock;
if (readers != NGX_RWLOCK_WLOCK && ngx_atomic_cmp_set(lock, readers, readers + 1)) { return; } } }
ngx_sched_yield(); } }
|
以下是取消锁定的代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| void ngx_rwlock_unlock(ngx_atomic_t *lock) { ngx_atomic_uint_t readers;
readers = *lock;
if (readers == NGX_RWLOCK_WLOCK) { (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0); return; }
for ( ;; ) {
if (ngx_atomic_cmp_set(lock, readers, readers - 1)) { return; }
readers = *lock; } }
|
总结
nginx读写锁主要是使用CAS操作和自旋来实现的,通过锁的数值改变来进行加锁和解锁。