]>
Commit | Line | Data |
---|---|---|
282113ba CV |
1 | /* bsd_mutex.cc |
2 | ||
7131554a | 3 | Copyright 2003, 2004, 2005, 2007 Red Hat Inc. |
282113ba CV |
4 | |
5 | This file is part of Cygwin. | |
6 | ||
7 | This software is a copyrighted work licensed under the terms of the | |
8 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
9 | details. */ | |
10 | #ifdef __OUTSIDE_CYGWIN__ | |
11 | #include "woutsup.h" | |
1dcd520b | 12 | #include <errno.h> |
282113ba CV |
13 | #define _KERNEL 1 |
14 | #define __BSD_VISIBLE 1 | |
15 | #include <sys/smallprint.h> | |
f312634c | 16 | #include <limits.h> |
dafef5e2 CV |
17 | #include <stdlib.h> |
18 | #include <sys/msg.h> | |
19 | #include <sys/sem.h> | |
282113ba CV |
20 | |
21 | #include "process.h" | |
22 | #include "cygserver_ipc.h" | |
23 | ||
24 | /* A BSD kernel global mutex. */ | |
25 | struct mtx Giant; | |
26 | ||
27 | void | |
28 | mtx_init (mtx *m, const char *name, const void *, int) | |
29 | { | |
30 | m->name = name; | |
31 | m->owner = 0; | |
dafef5e2 | 32 | m->cnt = 0; |
282113ba CV |
33 | /* Can't use Windows Mutexes here since Windows Mutexes are only |
34 | unlockable by the lock owner. */ | |
35 | m->h = CreateSemaphore (NULL, 1, 1, NULL); | |
36 | if (!m->h) | |
62688407 | 37 | panic ("couldn't allocate %s mutex, %lu\n", name, GetLastError ()); |
282113ba CV |
38 | } |
39 | ||
40 | void | |
41 | _mtx_lock (mtx *m, DWORD winpid, const char *file, int line) | |
42 | { | |
dafef5e2 CV |
43 | _log (file, line, LOG_DEBUG, "Try locking mutex %s (%u) (hold: %u)", |
44 | m->name, winpid, m->owner); | |
282113ba | 45 | if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0) |
62688407 CV |
46 | _panic (file, line, "wait for %s in %d failed, %lu", m->name, winpid, |
47 | GetLastError ()); | |
282113ba | 48 | m->owner = winpid; |
dafef5e2 CV |
49 | _log (file, line, LOG_DEBUG, "Locked mutex %s/%u (%u)", |
50 | m->name, ++m->cnt, winpid); | |
282113ba CV |
51 | } |
52 | ||
53 | int | |
dafef5e2 | 54 | mtx_owned (mtx *m, DWORD winpid) |
282113ba | 55 | { |
dafef5e2 | 56 | return m->owner == winpid; |
282113ba CV |
57 | } |
58 | ||
59 | void | |
dafef5e2 | 60 | _mtx_assert (mtx *m, int what, DWORD winpid, const char *file, int line) |
282113ba CV |
61 | { |
62 | switch (what) | |
63 | { | |
64 | case MA_OWNED: | |
dafef5e2 | 65 | if (!mtx_owned (m, winpid)) |
96e949de | 66 | _panic (file, line, "Mutex %s not owned", m->name); |
282113ba CV |
67 | break; |
68 | case MA_NOTOWNED: | |
dafef5e2 | 69 | if (mtx_owned (m, winpid)) |
96e949de | 70 | _panic (file, line, "Mutex %s is owned", m->name); |
282113ba CV |
71 | break; |
72 | default: | |
73 | break; | |
74 | } | |
75 | } | |
76 | ||
77 | void | |
78 | _mtx_unlock (mtx *m, const char *file, int line) | |
79 | { | |
dafef5e2 CV |
80 | DWORD owner = m->owner; |
81 | unsigned long cnt = m->cnt; | |
282113ba CV |
82 | m->owner = 0; |
83 | /* Cautiously check if mtx_destroy has been called (shutdown). | |
84 | In that case, m->h is NULL. */ | |
85 | if (m->h && !ReleaseSemaphore (m->h, 1, NULL)) | |
86 | { | |
7131554a CV |
87 | /* Check if the semaphore was already on it's max value. */ |
88 | if (GetLastError () != ERROR_TOO_MANY_POSTS) | |
62688407 CV |
89 | _panic (file, line, "release of mutex %s failed, %lu", m->name, |
90 | GetLastError ()); | |
282113ba | 91 | } |
dafef5e2 CV |
92 | _log (file, line, LOG_DEBUG, "Unlocked mutex %s/%u (owner: %u)", |
93 | m->name, cnt, owner); | |
282113ba CV |
94 | } |
95 | ||
96 | void | |
97 | mtx_destroy (mtx *m) | |
98 | { | |
99 | HANDLE tmp = m->h; | |
100 | m->h = NULL; | |
101 | if (tmp) | |
102 | CloseHandle (tmp); | |
103 | } | |
104 | ||
105 | /* | |
106 | * Helper functions for msleep/wakeup. | |
107 | */ | |
f312634c | 108 | |
282113ba CV |
109 | static int |
110 | win_priority (int priority) | |
111 | { | |
f8e58afb | 112 | int p = (int)((priority) & PRIO_MASK) - PZERO; |
282113ba | 113 | /* Generating a valid priority value is a bit tricky. The only valid |
7131554a | 114 | values on NT4 are -15, -2, -1, 0, 1, 2, 15. */ |
282113ba CV |
115 | switch (p) |
116 | { | |
117 | case -15: case -14: case -13: case -12: case -11: | |
118 | return THREAD_PRIORITY_IDLE; | |
119 | case -10: case -9: case -8: case -7: case -6: | |
120 | return THREAD_PRIORITY_LOWEST; | |
121 | case -5: case -4: case -3: case -2: case -1: | |
122 | return THREAD_PRIORITY_BELOW_NORMAL; | |
123 | case 0: | |
124 | return THREAD_PRIORITY_NORMAL; | |
125 | case 1: case 2: case 3: case 4: case 5: | |
126 | return THREAD_PRIORITY_ABOVE_NORMAL; | |
127 | case 6: case 7: case 8: case 9: case 10: | |
128 | return THREAD_PRIORITY_HIGHEST; | |
129 | case 11: case 12: case 13: case 14: case 15: | |
130 | return THREAD_PRIORITY_TIME_CRITICAL; | |
131 | } | |
132 | return THREAD_PRIORITY_NORMAL; | |
133 | } | |
134 | ||
135 | /* | |
136 | * Sets the thread priority, returns the old priority. | |
137 | */ | |
138 | static int | |
139 | set_priority (int priority) | |
140 | { | |
141 | int old_prio = GetThreadPriority (GetCurrentThread ()); | |
f5133f95 | 142 | if (!SetThreadPriority (GetCurrentThread (), win_priority (priority))) |
282113ba CV |
143 | log (LOG_WARNING, |
144 | "Warning: Setting thread priority to %d failed with error %lu\n", | |
f5133f95 | 145 | win_priority (priority), GetLastError ()); |
282113ba CV |
146 | return old_prio; |
147 | } | |
148 | ||
f5133f95 CV |
149 | /* |
150 | * Original description from BSD code: | |
151 | * | |
152 | * General sleep call. Suspends the current process until a wakeup is | |
153 | * performed on the specified identifier. The process will then be made | |
154 | * runnable with the specified priority. Sleeps at most timo/hz seconds | |
155 | * (0 means no timeout). If pri includes PCATCH flag, signals are checked | |
156 | * before and after sleeping, else signals are not checked. Returns 0 if | |
157 | * awakened, EWOULDBLOCK if the timeout expires. If PCATCH is set and a | |
158 | * signal needs to be delivered, ERESTART is returned if the current system | |
159 | * call should be restarted if possible, and EINTR is returned if the system | |
160 | * call should be interrupted by the signal (return EINTR). | |
161 | * | |
162 | * The mutex argument is exited before the caller is suspended, and | |
163 | * entered before msleep returns. If priority includes the PDROP | |
164 | * flag the mutex is not entered before returning. | |
165 | */ | |
166 | static HANDLE msleep_glob_evt; | |
c4004194 CV |
167 | |
168 | class msleep_sync_array | |
169 | { | |
170 | enum msleep_action { | |
171 | MSLEEP_ENTER = 0, | |
172 | MSLEEP_LEAVE, | |
173 | MSLEEP_WAKEUP | |
174 | }; | |
175 | ||
176 | CRITICAL_SECTION cs; | |
177 | long cnt; | |
178 | long max_cnt; | |
179 | struct msleep_record { | |
180 | void *ident; | |
181 | HANDLE wakeup_evt; | |
182 | LONG threads; | |
183 | } *a; | |
184 | ||
185 | int find_ident (void *ident, msleep_action action) | |
186 | { | |
187 | int i; | |
188 | for (i = 0; i < cnt; ++i) | |
189 | if (a[i].ident == ident) | |
190 | return i; | |
191 | if (i >= max_cnt) | |
192 | panic ("ident %x not found and run out of slots.", ident); | |
193 | if (i >= cnt && action == MSLEEP_LEAVE) | |
194 | panic ("ident %x not found (%d).", ident, action); | |
195 | return i; | |
196 | } | |
197 | ||
198 | HANDLE first_entry (int i, void *ident) | |
199 | { | |
200 | debug ("New ident %x, index %d", ident, i); | |
201 | a[i].ident = ident; | |
202 | a[i].wakeup_evt = CreateEvent (NULL, TRUE, FALSE, NULL); | |
203 | if (!a[i].wakeup_evt) | |
62688407 | 204 | panic ("CreateEvent failed: %lu", GetLastError ()); |
c4004194 CV |
205 | debug ("i = %d, CreateEvent: %x", i, a[i].wakeup_evt); |
206 | a[i].threads = 1; | |
207 | ++cnt; | |
208 | return a[i].wakeup_evt; | |
209 | } | |
210 | ||
211 | HANDLE next_entry (int i) | |
212 | { | |
213 | if (a[i].ident && WaitForSingleObject (a[i].wakeup_evt, 0) != WAIT_OBJECT_0) | |
214 | { | |
215 | ++a[i].threads; | |
216 | return a[i].wakeup_evt; | |
217 | } | |
218 | return NULL; | |
219 | } | |
220 | ||
221 | public: | |
222 | ||
223 | msleep_sync_array (int count) : cnt (0), max_cnt (count) | |
224 | { | |
225 | InitializeCriticalSection (&cs); | |
226 | if (!(a = new msleep_record[count])) | |
227 | panic ("Allocating msleep records failed: %d", errno); | |
228 | } | |
229 | ||
230 | HANDLE enter (void *ident) | |
231 | { | |
232 | HANDLE evt = NULL; | |
233 | while (!evt) | |
234 | { | |
235 | EnterCriticalSection (&cs); | |
236 | int i = find_ident (ident, MSLEEP_ENTER); | |
237 | if (i >= cnt) | |
238 | evt = first_entry (i, ident); | |
239 | else if (!(evt = next_entry (i))) | |
240 | { | |
241 | /* wakeup has been called, so sleep to wait until all | |
242 | formerly waiting threads have left and retry. */ | |
243 | LeaveCriticalSection (&cs); | |
244 | Sleep (1L); | |
245 | } | |
246 | } | |
247 | LeaveCriticalSection (&cs); | |
248 | return evt; | |
249 | } | |
250 | ||
251 | void leave (void *ident) | |
252 | { | |
253 | EnterCriticalSection (&cs); | |
254 | int i = find_ident (ident, MSLEEP_LEAVE); | |
255 | if (--a[i].threads == 0) | |
256 | { | |
257 | debug ("i = %d, CloseEvent: %x", i, a[i].wakeup_evt); | |
258 | CloseHandle (a[i].wakeup_evt); | |
259 | a[i].ident = NULL; | |
260 | --cnt; | |
261 | if (i < cnt) | |
262 | a[i] = a[cnt]; | |
263 | } | |
264 | LeaveCriticalSection (&cs); | |
265 | } | |
266 | ||
267 | void wakeup (void *ident) | |
268 | { | |
269 | EnterCriticalSection (&cs); | |
270 | int i = find_ident (ident, MSLEEP_WAKEUP); | |
271 | if (i < cnt && a[i].ident) | |
272 | SetEvent (a[i].wakeup_evt); | |
273 | LeaveCriticalSection (&cs); | |
274 | } | |
275 | }; | |
276 | ||
277 | static msleep_sync_array *msleep_sync; | |
f5133f95 CV |
278 | |
279 | void | |
280 | msleep_init (void) | |
281 | { | |
dafef5e2 CV |
282 | extern struct msginfo msginfo; |
283 | extern struct seminfo seminfo; | |
284 | ||
f5133f95 CV |
285 | msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL); |
286 | if (!msleep_glob_evt) | |
62688407 | 287 | panic ("CreateEvent in msleep_init failed: %lu", GetLastError ()); |
dafef5e2 CV |
288 | long msgmni = support_msgqueues ? msginfo.msgmni : 0; |
289 | long semmni = support_semaphores ? seminfo.semmni : 0; | |
290 | TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni); | |
291 | TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni); | |
292 | debug ("Try allocating msgmni (%d) + semmni (%d) msleep records", | |
293 | msgmni, semmni); | |
c4004194 CV |
294 | msleep_sync = new msleep_sync_array (msgmni + semmni); |
295 | if (!msleep_sync) | |
dafef5e2 | 296 | panic ("Allocating msleep records in msleep_init failed: %d", errno); |
f5133f95 CV |
297 | } |
298 | ||
282113ba CV |
299 | int |
300 | _msleep (void *ident, struct mtx *mtx, int priority, | |
301 | const char *wmesg, int timo, struct thread *td) | |
302 | { | |
303 | int ret = -1; | |
f312634c | 304 | |
c4004194 | 305 | HANDLE evt = msleep_sync->enter (ident); |
f312634c | 306 | |
282113ba CV |
307 | if (mtx) |
308 | mtx_unlock (mtx); | |
309 | int old_priority = set_priority (priority); | |
f5133f95 CV |
310 | HANDLE obj[4] = |
311 | { | |
c4004194 | 312 | evt, |
f5133f95 CV |
313 | msleep_glob_evt, |
314 | td->client->handle (), | |
315 | td->client->signal_arrived () | |
316 | }; | |
373a036f CV |
317 | /* PCATCH handling. If PCATCH is given and signal_arrived is a valid |
318 | handle, then it's used in the WaitFor call and EINTR is returned. */ | |
319 | int obj_cnt = 3; | |
320 | if ((priority & PCATCH) | |
321 | && td->client->signal_arrived () != INVALID_HANDLE_VALUE) | |
322 | obj_cnt = 4; | |
323 | switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE)) | |
282113ba CV |
324 | { |
325 | case WAIT_OBJECT_0: /* wakeup() has been called. */ | |
326 | ret = 0; | |
c4004194 | 327 | debug ("msleep wakeup called for %d", td->td_proc->winpid); |
282113ba | 328 | break; |
f5133f95 | 329 | case WAIT_OBJECT_0 + 1: /* Shutdown event (triggered by wakeup_all). */ |
282113ba CV |
330 | priority |= PDROP; |
331 | /*FALLTHRU*/ | |
f5133f95 | 332 | case WAIT_OBJECT_0 + 2: /* The dependent process has exited. */ |
c4004194 | 333 | debug ("msleep process exit or shutdown for %d", td->td_proc->winpid); |
282113ba CV |
334 | ret = EIDRM; |
335 | break; | |
373a036f | 336 | case WAIT_OBJECT_0 + 3: /* Signal for calling process arrived. */ |
c4004194 | 337 | debug ("msleep process got signal for %d", td->td_proc->winpid); |
373a036f CV |
338 | ret = EINTR; |
339 | break; | |
282113ba CV |
340 | case WAIT_TIMEOUT: |
341 | ret = EWOULDBLOCK; | |
342 | break; | |
343 | default: | |
dafef5e2 CV |
344 | /* There's a chance that a process has been terminated before |
345 | WaitForMultipleObjects has been called. In this case the handles | |
346 | might be invalid. The error code returned is ERROR_INVALID_HANDLE. | |
347 | Since we can trust the values of these handles otherwise, we | |
348 | treat an ERROR_INVALID_HANDLE as a normal process termination and | |
349 | hope for the best. */ | |
350 | if (GetLastError () != ERROR_INVALID_HANDLE) | |
62688407 CV |
351 | panic ("wait in msleep (%s) failed, %lu", wmesg, GetLastError ()); |
352 | debug ("wait in msleep (%s) failed for %d, %lu", wmesg, | |
353 | td->td_proc->winpid, GetLastError ()); | |
dafef5e2 | 354 | ret = EIDRM; |
282113ba CV |
355 | break; |
356 | } | |
f312634c | 357 | |
c4004194 | 358 | msleep_sync->leave (ident); |
f312634c | 359 | |
282113ba | 360 | set_priority (old_priority); |
f312634c | 361 | |
a9185492 | 362 | if (mtx && !(priority & PDROP)) |
282113ba | 363 | mtx_lock (mtx); |
282113ba CV |
364 | return ret; |
365 | } | |
366 | ||
367 | /* | |
368 | * Make all threads sleeping on the specified identifier runnable. | |
369 | */ | |
370 | int | |
371 | wakeup (void *ident) | |
372 | { | |
c4004194 | 373 | msleep_sync->wakeup (ident); |
282113ba CV |
374 | return 0; |
375 | } | |
376 | ||
377 | /* | |
378 | * Wakeup all sleeping threads. Only called in the context of cygserver | |
379 | * shutdown. | |
380 | */ | |
381 | void | |
382 | wakeup_all (void) | |
383 | { | |
c4004194 | 384 | SetEvent (msleep_glob_evt); |
282113ba CV |
385 | } |
386 | #endif /* __OUTSIDE_CYGWIN__ */ |