/src/unit/src/nxt_spinlock.c
Line | Count | Source |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) NGINX, Inc. |
5 | | */ |
6 | | |
7 | | #include <nxt_main.h> |
8 | | |
9 | | |
10 | | /* |
11 | | * Linux supports pthread spinlocks since glibc 2.3. Spinlock is an |
12 | | * atomic integer with zero initial value. On i386/amd64 however the |
13 | | * initial value is one. Spinlock never yields control. |
14 | | * |
15 | | * FreeBSD 5.2 and Solaris 10 support pthread spinlocks. Spinlock is a |
16 | | * structure and uses mutex implementation so it must be initialized by |
17 | | * by pthread_spin_init() and destroyed by pthread_spin_destroy(). |
18 | | * |
19 | | * MacOSX supported OSSpinLockLock(), it was deprecated in 10.12 (Sierra). |
20 | | * OSSpinLockLock() tries to acquire a lock atomically. If the lock is |
21 | | * busy, on SMP system it tests the lock 1000 times in a tight loop with |
22 | | * "pause" instruction. If the lock has been released, OSSpinLockLock() |
23 | | * tries to acquire it again. On failure it goes again in the tight loop. |
24 | | * If the lock has not been released during spinning in the loop or |
25 | | * on UP system, OSSpinLockLock() calls thread_switch() to run 1ms |
26 | | * with depressed (the lowest) priority. |
27 | | */ |
28 | | |
29 | | |
30 | | /* It should be adjusted with the "spinlock_count" directive. */ |
31 | | static nxt_uint_t nxt_spinlock_count = 1000; |
32 | | |
33 | | |
34 | | void |
35 | | nxt_thread_spin_init(nxt_uint_t ncpu, nxt_uint_t count) |
36 | 2 | { |
37 | 2 | switch (ncpu) { |
38 | | |
39 | 0 | case 0: |
40 | | /* Explicit spinlock count. */ |
41 | 0 | nxt_spinlock_count = count; |
42 | 0 | break; |
43 | | |
44 | 0 | case 1: |
45 | | /* Spinning is useless on UP. */ |
46 | 0 | nxt_spinlock_count = 0; |
47 | 0 | break; |
48 | | |
49 | 2 | default: |
50 | | /* |
51 | | * SMP. |
52 | | * |
53 | | * TODO: The count should be 10 on a virtualized system |
54 | | * since virtualized CPUs may share the same physical CPU. |
55 | | */ |
56 | 2 | nxt_spinlock_count = 1000; |
57 | 2 | break; |
58 | 2 | } |
59 | 2 | } |
60 | | |
61 | | |
62 | | void |
63 | | nxt_thread_spin_lock(nxt_thread_spinlock_t *lock) |
64 | 0 | { |
65 | 0 | nxt_uint_t n; |
66 | |
|
67 | 0 | nxt_thread_log_debug("spin_lock(%p) enter", lock); |
68 | |
|
69 | 0 | for ( ;; ) { |
70 | |
|
71 | 0 | again: |
72 | |
|
73 | 0 | if (nxt_fast_path(nxt_atomic_try_lock(lock))) { |
74 | 0 | return; |
75 | 0 | } |
76 | | |
77 | 0 | for (n = nxt_spinlock_count; n != 0; n--) { |
78 | |
|
79 | 0 | nxt_cpu_pause(); |
80 | |
|
81 | 0 | if (*lock == 0) { |
82 | 0 | goto again; |
83 | 0 | } |
84 | 0 | } |
85 | | |
86 | 0 | nxt_thread_yield(); |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | | |
91 | | nxt_bool_t |
92 | | nxt_thread_spin_trylock(nxt_thread_spinlock_t *lock) |
93 | 0 | { |
94 | 0 | nxt_thread_log_debug("spin_trylock(%p) enter", lock); |
95 | |
|
96 | 0 | if (nxt_fast_path(nxt_atomic_try_lock(lock))) { |
97 | 0 | return 1; |
98 | 0 | } |
99 | | |
100 | 0 | nxt_thread_log_debug("spin_trylock(%p) failed", lock); |
101 | |
|
102 | 0 | return 0; |
103 | 0 | } |
104 | | |
105 | | |
106 | | void |
107 | | nxt_thread_spin_unlock(nxt_thread_spinlock_t *lock) |
108 | 0 | { |
109 | 0 | nxt_atomic_release(lock); |
110 | |
|
111 | 0 | nxt_thread_log_debug("spin_unlock(%p) exit", lock); |
112 | 0 | } |