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