3 Copyright 2003, 2004, 2005, 2007, 2012, 2014, 2015 Red Hat Inc.
5 This file is part of Cygwin.
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
10 #ifdef __OUTSIDE_CYGWIN__
14 #define __BSD_VISIBLE 1
15 #include <sys/smallprint.h>
22 #include "cygserver_ipc.h"
24 /* A BSD kernel global mutex. */
28 mtx_init (mtx
*m
, const char *name
, const void *, int)
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
);
37 panic ("couldn't allocate %s mutex, %u\n", name
, GetLastError ());
41 _mtx_lock (mtx
*m
, DWORD winpid
, const char *file
, int line
)
43 _log (file
, line
, LOG_DEBUG
, "Try locking mutex %s (%u) (hold: %u)",
44 m
->name
, winpid
, m
->owner
);
45 if (WaitForSingleObject (m
->h
, INFINITE
) != WAIT_OBJECT_0
)
46 _panic (file
, line
, "wait for %s in %d failed, %u", m
->name
, winpid
,
49 _log (file
, line
, LOG_DEBUG
, "Locked mutex %s/%u (%u)",
50 m
->name
, ++m
->cnt
, winpid
);
54 mtx_owned (mtx
*m
, DWORD winpid
)
56 return m
->owner
== winpid
;
60 _mtx_assert (mtx
*m
, int what
, DWORD winpid
, const char *file
, int line
)
65 if (!mtx_owned (m
, winpid
))
66 _panic (file
, line
, "Mutex %s not owned", m
->name
);
69 if (mtx_owned (m
, winpid
))
70 _panic (file
, line
, "Mutex %s is owned", m
->name
);
78 _mtx_unlock (mtx
*m
, const char *file
, int line
)
80 DWORD owner
= m
->owner
;
81 unsigned long cnt
= m
->cnt
;
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
))
87 /* Check if the semaphore was already on it's max value. */
88 if (GetLastError () != ERROR_TOO_MANY_POSTS
)
89 _panic (file
, line
, "release of mutex %s failed, %u", m
->name
,
92 _log (file
, line
, LOG_DEBUG
, "Unlocked mutex %s/%u (owner: %u)",
106 * Helper functions for msleep/wakeup.
110 win_priority (int priority
)
112 int p
= (int)((priority
) & PRIO_MASK
) - PZERO
;
113 /* Generating a valid priority value is a bit tricky. The only valid
114 values on NT4 are -15, -2, -1, 0, 1, 2, 15. */
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
;
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
;
132 return THREAD_PRIORITY_NORMAL
;
136 * Sets the thread priority, returns the old priority.
139 set_priority (int priority
)
141 int old_prio
= GetThreadPriority (GetCurrentThread ());
142 if (!SetThreadPriority (GetCurrentThread (), win_priority (priority
)))
144 "Warning: Setting thread priority to %d failed with error %u\n",
145 win_priority (priority
), GetLastError ());
150 * Original description from BSD code:
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).
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.
166 static HANDLE msleep_glob_evt
;
168 class msleep_sync_array
179 struct msleep_record
{
185 int find_ident (void *ident
, msleep_action action
)
188 for (i
= 0; i
< cnt
; ++i
)
189 if (a
[i
].ident
== ident
)
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
);
198 HANDLE
first_entry (int i
, void *ident
)
200 debug ("New ident %x, index %d", ident
, i
);
202 a
[i
].wakeup_evt
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
203 if (!a
[i
].wakeup_evt
)
204 panic ("CreateEvent failed: %u", GetLastError ());
205 debug ("i = %d, CreateEvent: %x", i
, a
[i
].wakeup_evt
);
208 return a
[i
].wakeup_evt
;
211 HANDLE
next_entry (int i
)
213 if (a
[i
].ident
&& WaitForSingleObject (a
[i
].wakeup_evt
, 0) != WAIT_OBJECT_0
)
216 return a
[i
].wakeup_evt
;
223 msleep_sync_array (int count
) : cnt (0), max_cnt (count
)
225 InitializeCriticalSection (&cs
);
226 if (!(a
= new msleep_record
[count
]))
227 panic ("Allocating msleep records failed: %d", errno
);
230 ~msleep_sync_array () { delete a
; }
232 HANDLE
enter (void *ident
)
237 EnterCriticalSection (&cs
);
238 int i
= find_ident (ident
, MSLEEP_ENTER
);
240 evt
= first_entry (i
, ident
);
241 else if (!(evt
= next_entry (i
)))
243 /* wakeup has been called, so sleep to wait until all
244 formerly waiting threads have left and retry. */
245 LeaveCriticalSection (&cs
);
249 LeaveCriticalSection (&cs
);
253 void leave (void *ident
)
255 EnterCriticalSection (&cs
);
256 int i
= find_ident (ident
, MSLEEP_LEAVE
);
257 if (--a
[i
].threads
== 0)
259 debug ("i = %d, CloseEvent: %x", i
, a
[i
].wakeup_evt
);
260 CloseHandle (a
[i
].wakeup_evt
);
266 LeaveCriticalSection (&cs
);
269 void wakeup (void *ident
)
271 EnterCriticalSection (&cs
);
272 int i
= find_ident (ident
, MSLEEP_WAKEUP
);
273 if (i
< cnt
&& a
[i
].ident
)
274 SetEvent (a
[i
].wakeup_evt
);
275 LeaveCriticalSection (&cs
);
279 static msleep_sync_array
*msleep_sync
;
284 extern struct msginfo msginfo
;
285 extern struct seminfo seminfo
;
287 msleep_glob_evt
= CreateEvent (NULL
, TRUE
, FALSE
, NULL
);
288 if (!msleep_glob_evt
)
289 panic ("CreateEvent in msleep_init failed: %u", GetLastError ());
290 int32_t msgmni
= support_msgqueues
? msginfo
.msgmni
: 0;
291 int32_t semmni
= support_semaphores
? seminfo
.semmni
: 0;
292 TUNABLE_INT_FETCH ("kern.ipc.msgmni", &msgmni
);
293 TUNABLE_INT_FETCH ("kern.ipc.semmni", &semmni
);
294 debug ("Try allocating msgmni (%d) + semmni (%d) msleep records",
296 msleep_sync
= new msleep_sync_array (msgmni
+ semmni
);
298 panic ("Allocating msleep records in msleep_init failed: %d", errno
);
302 _msleep (void *ident
, struct mtx
*mtx
, int priority
,
303 const char *wmesg
, int timo
, struct thread
*td
)
307 HANDLE evt
= msleep_sync
->enter (ident
);
311 int old_priority
= set_priority (priority
);
316 td
->client
->handle (),
317 td
->ipcblk
->signal_arrived
319 /* PCATCH handling. If PCATCH is given and signal_arrived is a valid
320 handle, then it's used in the WaitFor call and EINTR is returned. */
322 if ((priority
& PCATCH
) && obj
[3])
324 switch (WaitForMultipleObjects (obj_cnt
, obj
, FALSE
, timo
?: INFINITE
))
326 case WAIT_OBJECT_0
: /* wakeup() has been called. */
328 debug ("msleep wakeup called for %d", td
->td_proc
->winpid
);
330 case WAIT_OBJECT_0
+ 1: /* Shutdown event (triggered by wakeup_all). */
333 case WAIT_OBJECT_0
+ 2: /* The dependent process has exited. */
334 debug ("msleep process exit or shutdown for %d", td
->td_proc
->winpid
);
337 case WAIT_OBJECT_0
+ 3: /* Signal for calling process arrived. */
338 debug ("msleep process got signal for %d", td
->td_proc
->winpid
);
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, %u", wmesg
, GetLastError ());
353 debug ("wait in msleep (%s) failed for %d, %u", wmesg
,
354 td
->td_proc
->winpid
, GetLastError ());
359 msleep_sync
->leave (ident
);
361 set_priority (old_priority
);
363 if (mtx
&& !(priority
& PDROP
))
369 * Make all threads sleeping on the specified identifier runnable.
374 msleep_sync
->wakeup (ident
);
379 * Wakeup all sleeping threads. Only called in the context of cygserver
385 SetEvent (msleep_glob_evt
);
387 #endif /* __OUTSIDE_CYGWIN__ */