OSDN Git Service

* bsd_mutex.cc (_msleep): Handle PCATCH using signal_arrived event.
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygserver / bsd_mutex.cc
1 /* bsd_mutex.cc
2
3    Copyright 2003 Red Hat Inc.
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"
12 #include "cygerrno.h"
13 #define _KERNEL 1
14 #define __BSD_VISIBLE 1
15 #include <sys/smallprint.h>
16
17 #include "process.h"
18 #include "cygserver_ipc.h"
19
20 /* A BSD kernel global mutex. */
21 struct mtx Giant;
22
23 void
24 mtx_init (mtx *m, const char *name, const void *, int)
25 {
26   m->name = name;
27   m->owner = 0;
28   /* Can't use Windows Mutexes here since Windows Mutexes are only
29      unlockable by the lock owner. */
30   m->h = CreateSemaphore (NULL, 1, 1, NULL);
31   if (!m->h)
32     panic ("couldn't allocate %s mutex, %E\n", name);
33 }
34
35 void
36 _mtx_lock (mtx *m, DWORD winpid, const char *file, int line)
37 {
38   _log (file, line, LOG_DEBUG, "Try locking mutex %s", m->name);
39   if (WaitForSingleObject (m->h, INFINITE) != WAIT_OBJECT_0)
40     _panic (file, line, "wait for %s in %d failed, %E", m->name, winpid);
41   m->owner = winpid;
42   _log (file, line, LOG_DEBUG, "Locked      mutex %s", m->name);
43 }
44
45 int
46 mtx_owned (mtx *m)
47 {
48   return m->owner > 0;
49 }
50
51 void
52 _mtx_assert(mtx *m, int what, const char *file, int line)
53 {
54   switch (what)
55     {
56       case MA_OWNED:
57         if (!mtx_owned (m))
58           _panic(file, line, "Mutex %s not owned", m->name);
59         break;
60       case MA_NOTOWNED:
61         if (mtx_owned (m))
62           _panic(file, line, "Mutex %s is owned", m->name);
63         break;
64       default:
65         break;
66     }
67 }
68
69 void
70 _mtx_unlock (mtx *m, const char *file, int line)
71 {
72   m->owner = 0;
73   /* Cautiously check if mtx_destroy has been called (shutdown).
74      In that case, m->h is NULL. */
75   if (m->h && !ReleaseSemaphore (m->h, 1, NULL))
76     {
77       /* Check if the semaphore was already on it's max value.  In this case,
78          ReleaseSemaphore returns FALSE with an error code which *sic* depends
79          on the OS. */
80       if (  (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_PARAMETER)
81           || (wincap.is_winnt () && GetLastError () != ERROR_TOO_MANY_POSTS))
82         _panic (file, line, "release of mutex %s failed, %E", m->name);
83     }
84   _log (file, line, LOG_DEBUG, "Unlocked    mutex %s", m->name);
85 }
86
87 void
88 mtx_destroy (mtx *m)
89 {
90   HANDLE tmp = m->h;
91   m->h = NULL;
92   if (tmp)
93     CloseHandle (tmp);
94 }
95
96 /*
97  * Helper functions for msleep/wakeup.
98  */
99 static char *
100 msleep_event_name (void *ident, char *name)
101 {
102   if (wincap.has_terminal_services ())
103     __small_sprintf (name, "Global\\cygserver.msleep.evt.%08x", ident);
104   else
105     __small_sprintf (name, "cygserver.msleep.evt.%08x", ident);
106   return name;
107 }
108
109 /*
110  * Original description from BSD code:
111  *
112  * General sleep call.  Suspends the current process until a wakeup is
113  * performed on the specified identifier.  The process will then be made
114  * runnable with the specified priority.  Sleeps at most timo/hz seconds
115  * (0 means no timeout).  If pri includes PCATCH flag, signals are checked
116  * before and after sleeping, else signals are not checked.  Returns 0 if
117  * awakened, EWOULDBLOCK if the timeout expires.  If PCATCH is set and a
118  * signal needs to be delivered, ERESTART is returned if the current system
119  * call should be restarted if possible, and EINTR is returned if the system
120  * call should be interrupted by the signal (return EINTR).
121  *
122  * The mutex argument is exited before the caller is suspended, and
123  * entered before msleep returns.  If priority includes the PDROP
124  * flag the mutex is not entered before returning.
125  */
126 static HANDLE msleep_glob_evt;
127
128 void
129 msleep_init (void)
130 {
131   msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL);
132   if (!msleep_glob_evt)
133     panic ("CreateEvent in msleep_init failed: %E");
134 }
135
136 static int
137 win_priority (int priority)
138 {
139   int p = (int)((p) & PRIO_MASK) - PZERO;
140   /* Generating a valid priority value is a bit tricky.  The only valid
141      values on 9x and NT4 are -15, -2, -1, 0, 1, 2, 15. */
142   switch (p)
143     {
144       case -15: case -14: case -13: case -12: case -11:
145         return THREAD_PRIORITY_IDLE;
146       case -10: case -9: case -8: case -7: case -6:
147         return THREAD_PRIORITY_LOWEST;
148       case -5: case -4: case -3: case -2: case -1:
149         return THREAD_PRIORITY_BELOW_NORMAL;
150       case 0:
151         return THREAD_PRIORITY_NORMAL;
152       case 1: case 2: case 3: case 4: case 5:
153         return THREAD_PRIORITY_ABOVE_NORMAL;
154       case 6: case 7: case 8: case 9: case 10:
155         return THREAD_PRIORITY_HIGHEST;
156       case 11: case 12: case 13: case 14: case 15:
157         return THREAD_PRIORITY_TIME_CRITICAL;
158     }
159   return THREAD_PRIORITY_NORMAL;
160 }
161
162 /*
163  * Sets the thread priority, returns the old priority.
164  */
165 static int
166 set_priority (int priority)
167 {
168   int old_prio = GetThreadPriority (GetCurrentThread ());
169   if (!SetThreadPriority (GetCurrentThread (), win_priority(priority)))
170     log (LOG_WARNING,
171           "Warning: Setting thread priority to %d failed with error %lu\n",
172           win_priority(priority), GetLastError ());
173   return old_prio;
174 }
175
176 int
177 _msleep (void *ident, struct mtx *mtx, int priority,
178         const char *wmesg, int timo, struct thread *td)
179 {
180   int ret = -1;
181   char name[64];
182   msleep_event_name (ident, name);
183   HANDLE evt = OpenEvent (EVENT_ALL_ACCESS, FALSE, name);
184   if (!evt)
185     evt = CreateEvent (NULL, TRUE, FALSE, name);
186   if (!evt)
187     panic ("CreateEvent in msleep (%s) failed: %E", wmesg);
188   if (mtx)
189     mtx_unlock (mtx);
190   int old_priority = set_priority (priority);
191   HANDLE obj[4] = { evt, td->client->handle (), msleep_glob_evt, td->client->signal_arrived () };
192   /* PCATCH handling.  If PCATCH is given and signal_arrived is a valid
193      handle, then it's used in the WaitFor call and EINTR is returned. */
194   int obj_cnt = 3;
195   if ((priority & PCATCH)
196       && td->client->signal_arrived () != INVALID_HANDLE_VALUE)
197     obj_cnt = 4;
198   switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
199     {
200       case WAIT_OBJECT_0:       /* wakeup() has been called. */
201         ret = 0;
202         break;
203       case WAIT_OBJECT_0 + 2:   /* Shutdown event (triggered by wakeup_all). */
204         priority |= PDROP;
205         /*FALLTHRU*/
206       case WAIT_OBJECT_0 + 1:   /* The dependent process has exited. */
207         ret = EIDRM;
208         break;
209       case WAIT_OBJECT_0 + 3:   /* Signal for calling process arrived. */
210         ret = EINTR;
211         break;
212       case WAIT_TIMEOUT:
213         ret = EWOULDBLOCK;
214         break;
215       default:
216         panic ("wait in msleep (%s) failed, %E", wmesg);
217         break;
218     }
219   set_priority (old_priority);
220   if (!(priority & PDROP) && mtx)
221     mtx_lock (mtx);
222   CloseHandle (evt);
223   return ret;
224 }
225
226 /*
227  * Make all threads sleeping on the specified identifier runnable.
228  */
229 int
230 wakeup (void *ident)
231 {
232   char name[64];
233   msleep_event_name (ident, name);
234   HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name);
235   if (!evt)
236     {
237       /* Another round of different error codes returned by 9x and NT
238          systems. Oh boy... */
239       if (  (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
240           || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
241         panic ("OpenEvent (%s) in wakeup failed: %E", name);
242     }
243   if (evt)
244     {
245       if (!SetEvent (evt))
246         panic ("SetEvent (%s) in wakeup failed, %E", name);
247       CloseHandle (evt);
248     }
249   return 0;
250 }
251
252 /*
253  * Wakeup all sleeping threads.  Only called in the context of cygserver
254  * shutdown.
255  */
256 void
257 wakeup_all (void)
258 {
259     SetEvent (msleep_glob_evt);
260 }
261 #endif /* __OUTSIDE_CYGWIN__ */