OSDN Git Service

* bsd_mutex.cc (win_priority): Actually calculate p from priority.
[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 static int
110 win_priority (int priority)
111 {
112   int p = (int)((priority) & PRIO_MASK) - PZERO;
113   /* Generating a valid priority value is a bit tricky.  The only valid
114      values on 9x and NT4 are -15, -2, -1, 0, 1, 2, 15. */
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  */
138 static int
139 set_priority (int priority)
140 {
141   int old_prio = GetThreadPriority (GetCurrentThread ());
142   if (!SetThreadPriority (GetCurrentThread (), win_priority (priority)))
143     log (LOG_WARNING,
144           "Warning: Setting thread priority to %d failed with error %lu\n",
145           win_priority (priority), GetLastError ());
146   return old_prio;
147 }
148
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  */
166 static HANDLE msleep_glob_evt;
167
168 void
169 msleep_init (void)
170 {
171   msleep_glob_evt = CreateEvent (NULL, TRUE, FALSE, NULL);
172   if (!msleep_glob_evt)
173     panic ("CreateEvent in msleep_init failed: %E");
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] =
192     {
193       evt,
194       msleep_glob_evt,
195       td->client->handle (),
196       td->client->signal_arrived ()
197     };
198   /* PCATCH handling.  If PCATCH is given and signal_arrived is a valid
199      handle, then it's used in the WaitFor call and EINTR is returned. */
200   int obj_cnt = 3;
201   if ((priority & PCATCH)
202       && td->client->signal_arrived () != INVALID_HANDLE_VALUE)
203     obj_cnt = 4;
204   switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
205     {
206       case WAIT_OBJECT_0:       /* wakeup() has been called. */
207         ret = 0;
208         break;
209       case WAIT_OBJECT_0 + 1:   /* Shutdown event (triggered by wakeup_all). */
210         priority |= PDROP;
211         /*FALLTHRU*/
212       case WAIT_OBJECT_0 + 2:   /* The dependent process has exited. */
213         ret = EIDRM;
214         break;
215       case WAIT_OBJECT_0 + 3:   /* Signal for calling process arrived. */
216         ret = EINTR;
217         break;
218       case WAIT_TIMEOUT:
219         ret = EWOULDBLOCK;
220         break;
221       default:
222         panic ("wait in msleep (%s) failed, %E", wmesg);
223         break;
224     }
225   set_priority (old_priority);
226   if (!(priority & PDROP) && mtx)
227     mtx_lock (mtx);
228   CloseHandle (evt);
229   return ret;
230 }
231
232 /*
233  * Make all threads sleeping on the specified identifier runnable.
234  */
235 int
236 wakeup (void *ident)
237 {
238   char name[64];
239   msleep_event_name (ident, name);
240   HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name);
241   if (!evt)
242     {
243       /* Another round of different error codes returned by 9x and NT
244          systems. Oh boy... */
245       if (  (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
246           || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
247         panic ("OpenEvent (%s) in wakeup failed: %E", name);
248     }
249   if (evt)
250     {
251       if (!SetEvent (evt))
252         panic ("SetEvent (%s) in wakeup failed, %E", name);
253       CloseHandle (evt);
254     }
255   return 0;
256 }
257
258 /*
259  * Wakeup all sleeping threads.  Only called in the context of cygserver
260  * shutdown.
261  */
262 void
263 wakeup_all (void)
264 {
265     SetEvent (msleep_glob_evt);
266 }
267 #endif /* __OUTSIDE_CYGWIN__ */