Cainiao nginx source code analysis data structure article (10) spin locks ngx_spinlock and nginxngx_spinlock
Cainiao nginx source code analysis data structure article (10) spin lock ngx_spinlock
Date: Nov 11th, 2014
The spin lock is an underlying synchronization mechanism widely used in Linux kernel. A spin lock is a special lock that works in a multi-processor environment. In a single processing environment, the spin lock operation is replaced with a null operation. When a kernel execution thread on a processor applies for a spin lock, if the lock is available, the lock is obtained, and then the critical zone operation is executed to release the lock. If the lock is occupied, the thread is not going to sleep, but is waiting for the lock. Once the lock is released, the first thread that perceives this information will get the lock.
1. Source Code Location
Source File: http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_spinlock.c
2. Related Structure Definition
Atomic Lock Structure ngx_atomic_t:
1: typedef unsigned long ngx_atomic_uint_t;
2: typedef volatile ngx_atomic_uint_t ngx_atomic_t;
Atomic lock Value Type ngx_atomic_int_t:
1: typedef long ngx_atomic_int_t;
Atomic comparison and exchange. If the lock is equal to the old one, the set writes the lock.
1: #define ngx_atomic_cmp_set(lock, old, set) \
2: __sync_bool_compare_and_swap(lock, old, set)
Note:
Bool _ sync_bool_compare_and_swap (type * ptr, type oldval type newval ,...)
Type _ sync_val_compare_and_swap (type * ptr, type oldval type newval ,...)
These two functions are the comparison and exchange of atoms provided by GCC. If * ptr = oldval, newval is written to * ptr.
The process proactively grants execution permission, ngx_sched_yeld
1: #define ngx_sched_yield() sched_yield()
3. Source Code Analysis
1: void
2: ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
3: {
4:
5: #if (NGX_HAVE_ATOMIC_OPS)
6:
7: ngx_uint_t i, n;
8:
9:
10: for ( ;; ) {
11:
12: // * lock = 0. If no lock is available, the ngx_atomic_cmp_set lock is called, Set * lock = value, and then return
13: if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
14: return;
15: }
16:
17: // multi-core
18: if (ngx_ncpu > 1) {
19:
20: // If spin is 80, wait for one ngx_cpu_pause () operation for the first time and check whether the lock is available again. Next, wait for 2, 4, 8, 16, 32, and 64 ngx_cpu_pause () operations in each round and try again.
21: // If the lock is released and can be used during the intermediate process, the loop will be suspended and the spinlock function will return a value. If the Retry is still unsuccessful, run ngx_sched_yield and then repeat the preceding operation.
22: for (n = 1; n < spin; n <<= 1) {
23:
24: for (i = 0; i < n; i++) {
25: ngx_cpu_pause();
26: }
27:
28: // check whether the lock is enabled. If * lock = 0, the lock is quickly returned.
29: if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
30: return;
31: }
32: }
33: }
34:
35: // give the CPU execution right
36: ngx_sched_yield();
37: }
38:
39: #else
40:
41: #if (NGX_THREADS)
42:
43: #error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
44:
45: #endif
46:
47: #endif
48:
49: }