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
nxt_thread_spin_init(nxt_uint_t ncpu,nxt_uint_t count)35 nxt_thread_spin_init(nxt_uint_t ncpu, nxt_uint_t count)
36 {
37 switch (ncpu) {
38
39 case 0:
40 /* Explicit spinlock count. */
41 nxt_spinlock_count = count;
42 break;
43
44 case 1:
45 /* Spinning is useless on UP. */
46 nxt_spinlock_count = 0;
47 break;
48
49 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 nxt_spinlock_count = 1000;
57 break;
58 }
59 }
60
61
62 void
nxt_thread_spin_lock(nxt_thread_spinlock_t * lock)63 nxt_thread_spin_lock(nxt_thread_spinlock_t *lock)
64 {
65 nxt_uint_t n;
66
67 nxt_thread_log_debug("spin_lock(%p) enter", lock);
68
69 for ( ;; ) {
70
71 again:
72
73 if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
74 return;
75 }
76
77 for (n = nxt_spinlock_count; n != 0; n--) {
78
79 nxt_cpu_pause();
80
81 if (*lock == 0) {
82 goto again;
83 }
84 }
85
86 nxt_thread_yield();
87 }
88 }
89
90
91 nxt_bool_t
nxt_thread_spin_trylock(nxt_thread_spinlock_t * lock)92 nxt_thread_spin_trylock(nxt_thread_spinlock_t *lock)
93 {
94 nxt_thread_log_debug("spin_trylock(%p) enter", lock);
95
96 if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
97 return 1;
98 }
99
100 nxt_thread_log_debug("spin_trylock(%p) failed", lock);
101
102 return 0;
103 }
104
105
106 void
nxt_thread_spin_unlock(nxt_thread_spinlock_t * lock)107 nxt_thread_spin_unlock(nxt_thread_spinlock_t *lock)
108 {
109 nxt_atomic_release(lock);
110
111 nxt_thread_log_debug("spin_unlock(%p) exit", lock);
112 }
113