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