Tue Apr 16 12:58:12 CEST 2002 Paolo Molaro <lupus@ximian.com>
[mono.git] / mono / io-layer / semaphores.c
1 #include <config.h>
2 #include <glib.h>
3 #include <pthread.h>
4 #include <semaphore.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <sys/time.h>
8
9 #include "mono/io-layer/wapi.h"
10 #include "wapi-private.h"
11 #include "wait-private.h"
12 #include "misc-private.h"
13 #include "handles-private.h"
14
15 #include "mono-mutex.h"
16
17 #undef DEBUG
18
19 /* emulate sem_t, so that we can prod the internal state more easily */
20 struct _WapiHandle_sem
21 {
22         WapiHandle handle;
23         guint32 val;
24         gint32 max;
25 };
26
27 /* This mutex controls access to _all_ semaphores and should not be
28  * locked for long periods.
29  *
30  * This global mutex and cond is really for wait_multiple, so we dont
31  * have to try and lock multiple handle mutexes and conditions.
32  */
33 static mono_mutex_t sem_mutex=MONO_MUTEX_INITIALIZER;
34 static pthread_cond_t sem_cond=PTHREAD_COND_INITIALIZER;
35
36 static void sema_close(WapiHandle *handle);
37 static gboolean sema_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms);
38 static guint32 sema_wait_multiple(gpointer data);
39 static void sema_signal(WapiHandle *handle);
40
41 static struct _WapiHandleOps sem_ops = {
42         sema_close,             /* close */
43         NULL,                   /* getfiletype */
44         NULL,                   /* readfile */
45         NULL,                   /* writefile */
46         NULL,                   /* flushfile */
47         NULL,                   /* seek */
48         NULL,                   /* setendoffile */
49         NULL,                   /* getfilesize */
50         NULL,                   /* getfiletime */
51         NULL,                   /* setfiletime */
52         sema_wait,              /* wait */
53         sema_wait_multiple,     /* wait_multiple */
54         sema_signal,            /* signal */
55 };
56
57 static void sema_close(WapiHandle *handle G_GNUC_UNUSED)
58 {
59         /* Not really much to do here */
60 #ifdef DEBUG
61         g_message(G_GNUC_PRETTY_FUNCTION ": closing sem handle %p", handle);
62 #endif
63 }
64
65 static gboolean sema_wait(WapiHandle *handle, WapiHandle *signal, guint32 ms)
66 {
67         struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle;
68         gboolean waited;
69         int ret;
70         
71         mono_mutex_lock(&sem_mutex);
72
73 #ifdef DEBUG
74         g_message(G_GNUC_PRETTY_FUNCTION ": Sem %p val %d ms %d", handle,
75                   sem_handle->val, ms);
76 #endif
77         
78         /* Signal this handle after we have obtained the semaphore
79          * global lock
80          */
81         if(signal!=NULL) {
82 #ifdef DEBUG
83                 g_message(G_GNUC_PRETTY_FUNCTION ": signalling %p", signal);
84 #endif
85                 signal->ops->signal(signal);
86         }
87         
88         /* Shortcut when ms==0 */
89         if(ms==0) {
90                 /* Just poll */
91 #ifdef DEBUG
92                 g_message(G_GNUC_PRETTY_FUNCTION ": Polling");
93 #endif
94                 if(sem_handle->val>0) {
95                         waited=TRUE;
96                 } else {
97                         waited=FALSE;
98                 }
99                 goto end;
100         }
101         
102         /* Check state first */
103         if(sem_handle->val>0) {
104                 waited=TRUE;
105                 goto end;
106         }
107         
108         if(ms==INFINITE) {
109 #ifdef DEBUG
110                 g_message(G_GNUC_PRETTY_FUNCTION ": wait for %p INFINITE",
111                           sem_handle);
112 #endif
113         try_again_infinite:
114                 ret=mono_cond_wait(&sem_cond, &sem_mutex);
115                 if(ret==0) {
116                         /* See if we were signalled (it might have been
117                          * another semaphore)
118                          */
119                         if(sem_handle->val>0) {
120 #ifdef DEBUG
121                                 g_message(G_GNUC_PRETTY_FUNCTION
122                                           ": sem %p has been signalled",
123                                           sem_handle);
124 #endif
125                                 waited=TRUE;
126                         } else {
127 #ifdef DEBUG
128                                 g_message(G_GNUC_PRETTY_FUNCTION
129                                           ": sem %p not signalled",
130                                           sem_handle);
131 #endif
132                                 goto try_again_infinite;
133                         }
134                 }
135         } else {
136                 struct timespec timeout;
137
138 #ifdef DEBUG
139                 g_message(G_GNUC_PRETTY_FUNCTION ": wait for %p for %d ms",
140                           sem_handle, ms);
141 #endif
142
143                 _wapi_calc_timeout(&timeout, ms);
144                 
145         try_again_timed:
146                 ret=mono_cond_timedwait(&sem_cond, &sem_mutex, &timeout);
147                 if(ret==0) {
148                         /* See if we were signalled (it might have been
149                          * another semaphore)
150                          */
151                         if(sem_handle->val>0) {
152 #ifdef DEBUG
153                                 g_message(G_GNUC_PRETTY_FUNCTION
154                                           ": sem %p has been signalled",
155                                           sem_handle);
156 #endif
157                                 waited=TRUE;
158                         } else {
159 #ifdef DEBUG
160                                 g_message(G_GNUC_PRETTY_FUNCTION
161                                           ": sem %p not signalled",
162                                           sem_handle);
163 #endif
164                                 goto try_again_timed;
165                         }
166                 } else {
167                         /* ret might be ETIMEDOUT for timeout, or
168                          * other for error */
169                         waited=FALSE;
170                 }
171         }
172         
173 end:
174         if(waited==TRUE) {
175                 sem_handle->val--;
176 #ifdef DEBUG
177                 g_message(G_GNUC_PRETTY_FUNCTION
178                           ": Waited TRUE, sem %p val now %d", sem_handle,
179                           sem_handle->val);
180 #endif
181         }
182 #ifdef DEBUG
183         else {
184                 g_message(G_GNUC_PRETTY_FUNCTION ": Waited FALSE, sem %p",
185                           sem_handle);
186         }
187 #endif
188         
189         mono_mutex_unlock(&sem_mutex);
190         return(waited);
191 }
192
193 static guint32 sema_wait_multiple(gpointer data G_GNUC_UNUSED)
194 {
195         WaitQueueItem *item=(WaitQueueItem *)data;
196         guint32 numhandles, count;
197         struct timespec timeout;
198         guint32 i;
199         int ret;
200         
201         numhandles=item->handles[WAPI_HANDLE_SEM]->len;
202         
203 #ifdef DEBUG
204         g_message(G_GNUC_PRETTY_FUNCTION
205                   ": waiting on %d sem handles for %d ms", numhandles,
206                   item->timeout);
207 #endif
208
209         mono_mutex_lock(&sem_mutex);
210         
211         /* First, check if any of the handles are already signalled.
212          * If waitall is specified we only return if all handles have
213          * been signalled.
214          */
215         for(count=0, i=0; i<numhandles; i++) {
216                 struct _WapiHandle_sem *sem_handle;
217                 
218                 sem_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_SEM],
219                                              i);
220                 if(sem_handle->val>0) {
221                         count++;
222                 }
223         }
224         
225 #ifdef DEBUG
226         g_message(G_GNUC_PRETTY_FUNCTION
227                   ": Preliminary check found %d handles signalled", count);
228 #endif
229
230         if((item->waitall==TRUE && count==numhandles) ||
231            (item->waitall==FALSE && count>0)) {
232                 goto success;
233         }
234         
235         /* OK, we need to wait for some */
236         if(item->timeout!=INFINITE) {
237                 _wapi_calc_timeout(&timeout, item->timeout);
238         }
239         
240         /* We can restart from here without resetting the timeout,
241          * because it is calculated from absolute time, not an offset.
242          */
243 again:
244         if(item->timeout==INFINITE) {
245                 ret=mono_cond_wait(&sem_cond, &sem_mutex);
246         } else {
247                 ret=mono_cond_timedwait(&sem_cond, &sem_mutex, &timeout);
248         }
249         
250         if(ret==ETIMEDOUT) {
251 #ifdef DEBUG
252                 g_message(G_GNUC_PRETTY_FUNCTION ": Wait timed out");
253 #endif
254
255                 goto success;
256         }
257
258 #ifdef DEBUG
259         g_message(G_GNUC_PRETTY_FUNCTION ": Sem posted, checking status");
260 #endif
261
262         /* A semaphore was posted, so see if it was one we are
263          * interested in
264          */
265         for(count=0, i=0; i<numhandles; i++) {
266                 struct _WapiHandle_sem *sem_handle;
267                 
268                 sem_handle=g_ptr_array_index(item->handles[WAPI_HANDLE_SEM],
269                                              i);
270                 if(sem_handle->val>0) {
271                         count++;
272                 }
273         }
274
275 #ifdef DEBUG
276         g_message(G_GNUC_PRETTY_FUNCTION
277                   ": Check after sem post found %d handles signalled", count);
278 #endif
279
280         if((item->waitall==TRUE && count==numhandles) ||
281            (item->waitall==FALSE && count>0)) {
282                 goto success;
283         }
284         
285         /* Either we have waitall set with more handles to wait for, or
286          * the sem that was posted wasn't interesting to us
287          */
288 #ifdef DEBUG
289         g_message(G_GNUC_PRETTY_FUNCTION ": Waiting a bit longer");
290 #endif
291
292         goto again;
293         
294 success:
295         item->waited[WAPI_HANDLE_SEM]=TRUE;
296         item->waitcount[WAPI_HANDLE_SEM]=count;
297
298         if((item->waitall==TRUE && count==numhandles) ||
299            (item->waitall==FALSE && count>0)) {
300                 /* Decrease all waited semaphores */
301                 for(i=0; i<numhandles; i++) {
302                         struct _WapiHandle_sem *sem_handle;
303                         guint32 idx;
304                         
305                         sem_handle=g_ptr_array_index(
306                                 item->handles[WAPI_HANDLE_SEM], i);
307
308                         idx=g_array_index(item->waitindex[WAPI_HANDLE_SEM],
309                                           guint32, i);
310                         _wapi_handle_set_lowest(item, idx);
311                         
312                         if(sem_handle->val>0) {
313                                 sem_handle->val--;
314                         }
315                 }
316         }
317
318         mono_mutex_unlock(&sem_mutex);
319
320         return(count);
321 }
322
323 static void sema_signal(WapiHandle *handle)
324 {
325         ReleaseSemaphore(handle, 1, NULL);
326 }
327
328 /**
329  * CreateSemaphore:
330  * @security: Ignored for now.
331  * @initial: The initial count for the semaphore.  The value must be
332  * greater than or equal to zero, and less than or equal to @max.
333  * @max: The maximum count for this semaphore.  The value must be
334  * greater than zero.
335  * @name: Pointer to a string specifying the name of this semaphore,
336  * or %NULL.  Currently ignored.
337  *
338  * Creates a new semaphore handle.  A semaphore is signalled when its
339  * count is greater than zero, and unsignalled otherwise.  The count
340  * is decreased by one whenever a wait function releases a thread that
341  * was waiting for the semaphore.  The count is increased by calling
342  * ReleaseSemaphore().
343  *
344  * Return value: a new handle, or NULL
345  */
346 WapiHandle *CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const guchar *name G_GNUC_UNUSED)
347 {
348         struct _WapiHandle_sem *sem_handle;
349         WapiHandle *handle;
350         
351         if(max<=0) {
352 #ifdef DEBUG
353                 g_message(G_GNUC_PRETTY_FUNCTION ": max <= 0");
354 #endif
355
356                 return(NULL);
357         }
358         
359         if(initial>max || initial<0) {
360 #ifdef DEBUG
361                 g_message(G_GNUC_PRETTY_FUNCTION ": initial>max or < 0");
362 #endif
363
364                 return(NULL);
365         }
366         
367         sem_handle=(struct _WapiHandle_sem *)g_new0(struct _WapiHandle_sem, 1);
368         handle=(WapiHandle *)sem_handle;
369         _WAPI_HANDLE_INIT(handle, WAPI_HANDLE_SEM, sem_ops);
370
371         sem_handle->val=initial;
372         sem_handle->max=max;
373
374 #ifdef DEBUG
375         g_message(G_GNUC_PRETTY_FUNCTION ": Created semaphore handle %p",
376                   handle);
377 #endif
378         
379         return(handle);
380 }
381
382 /**
383  * ReleaseSemaphore:
384  * @handle: The semaphore handle to release.
385  * @count: The amount by which the semaphore's count should be
386  * increased.
387  * @prevcount: Pointer to a location to store the previous count of
388  * the semaphore, or %NULL.
389  *
390  * Increases the count of semaphore @handle by @count.
391  *
392  * Return value: %TRUE on success, %FALSE otherwise.
393  */
394 gboolean ReleaseSemaphore(WapiHandle *handle, gint32 count, gint32 *prevcount)
395 {
396         struct _WapiHandle_sem *sem_handle=(struct _WapiHandle_sem *)handle;
397         gboolean ret=FALSE;
398         
399         
400         mono_mutex_lock(&sem_mutex);
401
402 #ifdef DEBUG
403         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val %d count %d",
404                   sem_handle, sem_handle->val, count);
405 #endif
406         
407         /* Do this before checking for count overflow, because overflowing max
408          * is a listed technique for finding the current value
409          */
410         if(prevcount!=NULL) {
411                 *prevcount=sem_handle->val;
412         }
413         
414         /* No idea why max is signed, but thats the spec :-( */
415         if(sem_handle->val+count > (guint32)sem_handle->max) {
416 #ifdef DEBUG
417                 g_message(G_GNUC_PRETTY_FUNCTION ": sem %p max value would be exceeded: max %d current %d count %d",
418                           handle, sem_handle->max, sem_handle->val, count);
419 #endif
420
421                 goto end;
422         }
423         
424         sem_handle->val+=count;
425         pthread_cond_broadcast(&sem_cond);
426         ret=TRUE;
427
428 #ifdef DEBUG
429         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", sem_handle,
430                   sem_handle->val);
431 #endif
432         
433 end:
434         mono_mutex_unlock(&sem_mutex);
435         return(ret);
436 }