]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygserver/bsd_mutex.cc
* cygserver.cc (print_usage): Align output to utilities in utils
[newlib-cygwin.git] / winsup / cygserver / bsd_mutex.cc
CommitLineData
282113ba
CV
1/* bsd_mutex.cc
2
7131554a 3 Copyright 2003, 2004, 2005, 2007 Red Hat Inc.
282113ba
CV
4
5This file is part of Cygwin.
6
7This software is a copyrighted work licensed under the terms of the
8Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9details. */
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. */
25struct mtx Giant;
26
27void
28mtx_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
40void
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
53int
dafef5e2 54mtx_owned (mtx *m, DWORD winpid)
282113ba 55{
dafef5e2 56 return m->owner == winpid;
282113ba
CV
57}
58
59void
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
77void
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
96void
97mtx_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
109static int
110win_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 */
138static int
139set_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 */
166static HANDLE msleep_glob_evt;
c4004194
CV
167
168class 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
221public:
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
277static msleep_sync_array *msleep_sync;
f5133f95
CV
278
279void
280msleep_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
299int
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 */
370int
371wakeup (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 */
381void
382wakeup_all (void)
383{
c4004194 384 SetEvent (msleep_glob_evt);
282113ba
CV
385}
386#endif /* __OUTSIDE_CYGWIN__ */
This page took 0.180902 seconds and 5 git commands to generate.