xref: /unit/src/nxt_spinlock.c (revision 0:a63ceefd6ab0)
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 
20 #if (NXT_HAVE_MACOSX_SPINLOCK)
21 
22 /*
23  * OSSpinLockLock() tries to acquire a lock atomically.  If the lock is
24  * busy, on SMP system it tests the lock 1000 times in a tight loop with
25  * "pause" instruction.  If the lock has been released, OSSpinLockLock()
26  * tries to acquire it again.  On failure it goes again in the tight loop.
27  * If the lock has not been released during spinning in the loop or
28  * on UP system, OSSpinLockLock() calls thread_switch() to run 1ms
29  * with depressed (the lowest) priority.
30  */
31 
32 void
33 nxt_thread_spin_lock(nxt_thread_spinlock_t *lock)
34 {
35     nxt_thread_log_debug("OSSpinLockLock(%p) enter", lock);
36 
37     OSSpinLockLock(lock);
38 }
39 
40 
41 nxt_bool_t
42 nxt_thread_spin_trylock(nxt_thread_spinlock_t *lock)
43 {
44     nxt_thread_log_debug("OSSpinLockTry(%p) enter", lock);
45 
46     if (OSSpinLockTry(lock)) {
47         return 1;
48     }
49 
50     nxt_thread_log_debug("OSSpinLockTry(%p) failed", lock);
51 
52     return 0;
53 }
54 
55 
56 void
57 nxt_thread_spin_unlock(nxt_thread_spinlock_t *lock)
58 {
59     OSSpinLockUnlock(lock);
60 
61     nxt_thread_log_debug("OSSpinLockUnlock(%p) exit", lock);
62 }
63 
64 
65 #else
66 
67 
68 /* It should be adjusted with the "spinlock_count" directive. */
69 static nxt_uint_t  nxt_spinlock_count = 1000;
70 
71 
72 void
73 nxt_thread_spin_init(nxt_uint_t ncpu, nxt_uint_t count)
74 {
75     switch (ncpu) {
76 
77     case 0:
78         /* Explicit spinlock count. */
79         nxt_spinlock_count = count;
80         break;
81 
82     case 1:
83         /* Spinning is useless on UP. */
84         nxt_spinlock_count = 0;
85         break;
86 
87     default:
88         /*
89          * SMP.
90          *
91          * TODO: The count should be 10 on a virtualized system
92          * since virtualized CPUs may share the same physical CPU.
93          */
94         nxt_spinlock_count = 1000;
95         break;
96     }
97 }
98 
99 
100 void
101 nxt_thread_spin_lock(nxt_thread_spinlock_t *lock)
102 {
103     nxt_uint_t  n;
104 
105     nxt_thread_log_debug("spin_lock(%p) enter", lock);
106 
107     for ( ;; ) {
108 
109     again:
110 
111         if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
112             return;
113         }
114 
115         for (n = nxt_spinlock_count; n != 0; n--) {
116 
117             nxt_cpu_pause();
118 
119             if (*lock == 0) {
120                 goto again;
121             }
122         }
123 
124         nxt_thread_yield();
125     }
126 }
127 
128 
129 nxt_bool_t
130 nxt_thread_spin_trylock(nxt_thread_spinlock_t *lock)
131 {
132     nxt_thread_log_debug("spin_trylock(%p) enter", lock);
133 
134     if (nxt_fast_path(nxt_atomic_try_lock(lock))) {
135         return 1;
136     }
137 
138     nxt_thread_log_debug("spin_trylock(%p) failed", lock);
139 
140     return 0;
141 }
142 
143 
144 void
145 nxt_thread_spin_unlock(nxt_thread_spinlock_t *lock)
146 {
147     nxt_atomic_release(lock);
148 
149     nxt_thread_log_debug("spin_unlock(%p) exit", lock);
150 }
151 
152 #endif
153