Mercurial > hg > nginx
comparison src/core/ngx_shmtx.c @ 4309:3f6040cd731e
Added shmtx interface to forcibly unlock mutexes.
It is currently used from master process on abnormal worker termination to
unlock accept mutex (unlocking of accept mutex was broken in 1.0.2). It is
expected to be used in the future to unlock other mutexes as well.
Shared mutex code was rewritten to make this possible in a safe way, i.e.
with a check if lock was actually held by the exited process. We again use
pid to lock mutex, and use separate atomic variable for a count of processes
waiting in sem_wait().
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Wed, 23 Nov 2011 13:55:38 +0000 |
parents | 0daede16d68b |
children | 13f108b9f3cf |
comparison
equal
deleted
inserted
replaced
4308:39982fa69482 | 4309:3f6040cd731e |
---|---|
9 | 9 |
10 | 10 |
11 #if (NGX_HAVE_ATOMIC_OPS) | 11 #if (NGX_HAVE_ATOMIC_OPS) |
12 | 12 |
13 | 13 |
14 static void ngx_shmtx_wakeup(ngx_shmtx_t *mtx); | |
15 | |
16 | |
14 ngx_int_t | 17 ngx_int_t |
15 ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name) | 18 ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) |
16 { | 19 { |
17 mtx->lock = addr; | 20 mtx->lock = &addr->lock; |
18 | 21 |
19 if (mtx->spin == (ngx_uint_t) -1) { | 22 if (mtx->spin == (ngx_uint_t) -1) { |
20 return NGX_OK; | 23 return NGX_OK; |
21 } | 24 } |
22 | 25 |
23 mtx->spin = 2048; | 26 mtx->spin = 2048; |
24 | 27 |
25 #if (NGX_HAVE_POSIX_SEM) | 28 #if (NGX_HAVE_POSIX_SEM) |
29 | |
30 mtx->wait = &addr->wait; | |
26 | 31 |
27 if (sem_init(&mtx->sem, 1, 0) == -1) { | 32 if (sem_init(&mtx->sem, 1, 0) == -1) { |
28 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, | 33 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, |
29 "sem_init() failed"); | 34 "sem_init() failed"); |
30 } else { | 35 } else { |
54 | 59 |
55 | 60 |
56 ngx_uint_t | 61 ngx_uint_t |
57 ngx_shmtx_trylock(ngx_shmtx_t *mtx) | 62 ngx_shmtx_trylock(ngx_shmtx_t *mtx) |
58 { | 63 { |
59 ngx_atomic_uint_t val; | 64 return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)); |
60 | |
61 val = *mtx->lock; | |
62 | |
63 return ((val & 0x80000000) == 0 | |
64 && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)); | |
65 } | 65 } |
66 | 66 |
67 | 67 |
68 void | 68 void |
69 ngx_shmtx_lock(ngx_shmtx_t *mtx) | 69 ngx_shmtx_lock(ngx_shmtx_t *mtx) |
70 { | 70 { |
71 ngx_uint_t i, n; | 71 ngx_uint_t i, n; |
72 ngx_atomic_uint_t val; | |
73 | 72 |
74 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock"); | 73 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock"); |
75 | 74 |
76 for ( ;; ) { | 75 for ( ;; ) { |
77 | 76 |
78 val = *mtx->lock; | 77 if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { |
79 | |
80 if ((val & 0x80000000) == 0 | |
81 && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)) | |
82 { | |
83 return; | 78 return; |
84 } | 79 } |
85 | 80 |
86 if (ngx_ncpu > 1) { | 81 if (ngx_ncpu > 1) { |
87 | 82 |
89 | 84 |
90 for (i = 0; i < n; i++) { | 85 for (i = 0; i < n; i++) { |
91 ngx_cpu_pause(); | 86 ngx_cpu_pause(); |
92 } | 87 } |
93 | 88 |
94 val = *mtx->lock; | 89 if (*mtx->lock == 0 |
95 | 90 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) |
96 if ((val & 0x80000000) == 0 | |
97 && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000)) | |
98 { | 91 { |
99 return; | 92 return; |
100 } | 93 } |
101 } | 94 } |
102 } | 95 } |
103 | 96 |
104 #if (NGX_HAVE_POSIX_SEM) | 97 #if (NGX_HAVE_POSIX_SEM) |
105 | 98 |
106 if (mtx->semaphore) { | 99 if (mtx->semaphore) { |
107 val = *mtx->lock; | 100 ngx_atomic_fetch_add(mtx->wait, 1); |
108 | 101 |
109 if ((val & 0x80000000) | 102 if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) { |
110 && ngx_atomic_cmp_set(mtx->lock, val, val + 1)) | 103 return; |
111 { | 104 } |
112 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | 105 |
113 "shmtx wait %XA", val); | 106 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, |
114 | 107 "shmtx wait %uA", *mtx->wait); |
115 while (sem_wait(&mtx->sem) == -1) { | 108 |
116 ngx_err_t err; | 109 while (sem_wait(&mtx->sem) == -1) { |
117 | 110 ngx_err_t err; |
118 err = ngx_errno; | 111 |
119 | 112 err = ngx_errno; |
120 if (err != NGX_EINTR) { | 113 |
121 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, | 114 if (err != NGX_EINTR) { |
122 "sem_wait() failed while waiting on shmtx"); | 115 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, |
123 break; | 116 "sem_wait() failed while waiting on shmtx"); |
124 } | 117 break; |
125 } | 118 } |
126 | 119 |
127 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | 120 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, |
128 "shmtx awoke"); | 121 "shmtx awoke"); |
129 } | 122 } |
139 | 132 |
140 | 133 |
141 void | 134 void |
142 ngx_shmtx_unlock(ngx_shmtx_t *mtx) | 135 ngx_shmtx_unlock(ngx_shmtx_t *mtx) |
143 { | 136 { |
144 ngx_atomic_uint_t val, old, wait; | |
145 | |
146 if (mtx->spin != (ngx_uint_t) -1) { | 137 if (mtx->spin != (ngx_uint_t) -1) { |
147 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock"); | 138 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock"); |
148 } | 139 } |
149 | 140 |
141 if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) { | |
142 ngx_shmtx_wakeup(mtx); | |
143 } | |
144 } | |
145 | |
146 | |
147 ngx_uint_t | |
148 ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) | |
149 { | |
150 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | |
151 "shmtx forced unlock"); | |
152 | |
153 if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) { | |
154 ngx_shmtx_wakeup(mtx); | |
155 return 1; | |
156 } | |
157 | |
158 return 0; | |
159 } | |
160 | |
161 | |
162 static void | |
163 ngx_shmtx_wakeup(ngx_shmtx_t *mtx) | |
164 { | |
165 #if (NGX_HAVE_POSIX_SEM) | |
166 ngx_atomic_uint_t wait; | |
167 | |
168 if (!mtx->semaphore) { | |
169 return; | |
170 } | |
171 | |
150 for ( ;; ) { | 172 for ( ;; ) { |
151 | 173 |
152 old = *mtx->lock; | 174 wait = *mtx->wait; |
153 wait = old & 0x7fffffff; | 175 |
154 val = wait ? wait - 1 : 0; | 176 if (wait == 0) { |
155 | 177 return; |
156 if (ngx_atomic_cmp_set(mtx->lock, old, val)) { | 178 } |
179 | |
180 if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) { | |
157 break; | 181 break; |
158 } | 182 } |
159 } | 183 } |
160 | 184 |
161 #if (NGX_HAVE_POSIX_SEM) | |
162 | |
163 if (wait == 0 || !mtx->semaphore) { | |
164 return; | |
165 } | |
166 | |
167 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, | 185 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, |
168 "shmtx wake %XA", old); | 186 "shmtx wake %uA", wait); |
169 | 187 |
170 if (sem_post(&mtx->sem) == -1) { | 188 if (sem_post(&mtx->sem) == -1) { |
171 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, | 189 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno, |
172 "sem_post() failed while wake shmtx"); | 190 "sem_post() failed while wake shmtx"); |
173 } | 191 } |
178 | 196 |
179 #else | 197 #else |
180 | 198 |
181 | 199 |
182 ngx_int_t | 200 ngx_int_t |
183 ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name) | 201 ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name) |
184 { | 202 { |
185 if (mtx->name) { | 203 if (mtx->name) { |
186 | 204 |
187 if (ngx_strcmp(name, mtx->name) == 0) { | 205 if (ngx_strcmp(name, mtx->name) == 0) { |
188 mtx->name = name; | 206 mtx->name = name; |
278 } | 296 } |
279 | 297 |
280 ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); | 298 ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); |
281 } | 299 } |
282 | 300 |
283 #endif | 301 |
302 void | |
303 ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid) | |
304 { | |
305 /* void */ | |
306 } | |
307 | |
308 #endif |