xref: /unit/src/nxt_spinlock.c (revision 765:7b63756a81a4)
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