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