Improve the reliability and error reporting of the TPL Dataflow test suite.
[mono-project.git] / mono / io-layer / semaphores.c
blob229e313070fafd1dca012819feedce3f8864e04a
1 /*
2 * semaphores.c: Semaphore handles
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 */
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #ifdef HAVE_SEMAPHORE_H
14 #include <semaphore.h>
15 #endif
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/time.h>
20 #include <mono/io-layer/wapi.h>
21 #include <mono/io-layer/wapi-private.h>
22 #include <mono/io-layer/misc-private.h>
23 #include <mono/io-layer/handles-private.h>
24 #include <mono/io-layer/semaphore-private.h>
26 #include <mono/utils/mono-mutex.h>
28 #if 0
29 #define DEBUG(...) g_message(__VA_ARGS__)
30 #else
31 #define DEBUG(...)
32 #endif
34 static void sema_signal(gpointer handle);
35 static gboolean sema_own (gpointer handle);
37 static void namedsema_signal (gpointer handle);
38 static gboolean namedsema_own (gpointer handle);
40 struct _WapiHandleOps _wapi_sem_ops = {
41 NULL, /* close */
42 sema_signal, /* signal */
43 sema_own, /* own */
44 NULL, /* is_owned */
45 NULL, /* special_wait */
46 NULL /* prewait */
49 void _wapi_sem_details (gpointer handle_info)
51 struct _WapiHandle_sem *sem = (struct _WapiHandle_sem *)handle_info;
53 g_print ("val: %5u, max: %5d", sem->val, sem->max);
56 struct _WapiHandleOps _wapi_namedsem_ops = {
57 NULL, /* close */
58 namedsema_signal, /* signal */
59 namedsema_own, /* own */
60 NULL, /* is_owned */
61 NULL, /* special_wait */
62 NULL /* prewait */
65 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prev);
66 static gboolean namedsem_release (gpointer handle, gint32 count, gint32 *prev);
68 static struct
70 gboolean (*release)(gpointer handle, gint32 count, gint32 *prev);
71 } sem_ops[WAPI_HANDLE_COUNT] = {
72 {NULL},
73 {NULL},
74 {NULL},
75 {NULL},
76 {sem_release},
77 {NULL},
78 {NULL},
79 {NULL},
80 {NULL},
81 {NULL},
82 {NULL},
83 {NULL},
84 {namedsem_release},
87 static mono_once_t sem_ops_once=MONO_ONCE_INIT;
89 static void sem_ops_init (void)
91 _wapi_handle_register_capabilities (WAPI_HANDLE_SEM,
92 WAPI_HANDLE_CAP_WAIT |
93 WAPI_HANDLE_CAP_SIGNAL);
94 _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDSEM,
95 WAPI_HANDLE_CAP_WAIT |
96 WAPI_HANDLE_CAP_SIGNAL);
99 static void sema_signal(gpointer handle)
101 ReleaseSemaphore(handle, 1, NULL);
104 static gboolean sema_own (gpointer handle)
106 struct _WapiHandle_sem *sem_handle;
107 gboolean ok;
109 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
110 (gpointer *)&sem_handle);
111 if(ok==FALSE) {
112 g_warning ("%s: error looking up sem handle %p", __func__,
113 handle);
114 return(FALSE);
117 DEBUG("%s: owning sem handle %p", __func__, handle);
119 sem_handle->val--;
121 DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
123 if(sem_handle->val==0) {
124 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
127 return(TRUE);
130 static void namedsema_signal (gpointer handle)
132 ReleaseSemaphore (handle, 1, NULL);
135 /* NB, always called with the shared handle lock held */
136 static gboolean namedsema_own (gpointer handle)
138 struct _WapiHandle_namedsem *namedsem_handle;
139 gboolean ok;
141 DEBUG ("%s: owning named sem handle %p", __func__, handle);
143 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
144 (gpointer *)&namedsem_handle);
145 if (ok == FALSE) {
146 g_warning ("%s: error looking up named sem handle %p",
147 __func__, handle);
148 return (FALSE);
151 namedsem_handle->val--;
153 DEBUG ("%s: named sem %p val now %d", __func__, handle,
154 namedsem_handle->val);
156 if (namedsem_handle->val == 0) {
157 _wapi_shared_handle_set_signal_state (handle, FALSE);
160 return (TRUE);
162 static gpointer sem_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
163 gint32 initial, gint32 max)
165 struct _WapiHandle_sem sem_handle = {0};
166 gpointer handle;
167 int thr_ret;
169 /* Need to blow away any old errors here, because code tests
170 * for ERROR_ALREADY_EXISTS on success (!) to see if a
171 * semaphore was freshly created
173 SetLastError (ERROR_SUCCESS);
175 sem_handle.val = initial;
176 sem_handle.max = max;
178 handle = _wapi_handle_new (WAPI_HANDLE_SEM, &sem_handle);
179 if (handle == _WAPI_HANDLE_INVALID) {
180 g_warning ("%s: error creating semaphore handle", __func__);
181 SetLastError (ERROR_GEN_FAILURE);
182 return(NULL);
185 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
186 handle);
187 thr_ret = _wapi_handle_lock_handle (handle);
188 g_assert (thr_ret == 0);
190 if (initial != 0) {
191 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
194 DEBUG ("%s: Created semaphore handle %p initial %d max %d",
195 __func__, handle, initial, max);
197 thr_ret = _wapi_handle_unlock_handle (handle);
198 g_assert (thr_ret == 0);
199 pthread_cleanup_pop (0);
201 return(handle);
204 static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
206 struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0};
207 gpointer handle;
208 gchar *utf8_name;
209 int thr_ret;
210 gpointer ret = NULL;
211 guint32 namelen;
212 gint32 offset;
214 /* w32 seems to guarantee that opening named objects can't
215 * race each other
217 thr_ret = _wapi_namespace_lock ();
218 g_assert (thr_ret == 0);
220 /* Need to blow away any old errors here, because code tests
221 * for ERROR_ALREADY_EXISTS on success (!) to see if a
222 * semaphore was freshly created
224 SetLastError (ERROR_SUCCESS);
226 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
228 DEBUG ("%s: Creating named sem [%s]", __func__, utf8_name);
230 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
231 utf8_name);
232 if (offset == -1) {
233 /* The name has already been used for a different
234 * object.
236 SetLastError (ERROR_INVALID_HANDLE);
237 goto cleanup;
238 } else if (offset != 0) {
239 /* Not an error, but this is how the caller is
240 * informed that the semaphore wasn't freshly created
242 SetLastError (ERROR_ALREADY_EXISTS);
244 /* Fall through to create the semaphore handle */
246 if (offset == 0) {
247 /* A new named semaphore, so create both the private
248 * and shared parts
250 if (strlen (utf8_name) < MAX_PATH) {
251 namelen = strlen (utf8_name);
252 } else {
253 namelen = MAX_PATH;
256 memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen);
258 namedsem_handle.val = initial;
259 namedsem_handle.max = max;
261 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM,
262 &namedsem_handle);
263 } else {
264 /* A new reference to an existing named semaphore, so
265 * just create the private part
267 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM,
268 offset, TRUE);
271 if (handle == _WAPI_HANDLE_INVALID) {
272 g_warning ("%s: error creating named sem handle", __func__);
273 SetLastError (ERROR_GEN_FAILURE);
274 goto cleanup;
276 ret = handle;
278 if (offset == 0) {
279 /* Set the initial state, as this is a completely new
280 * handle
282 thr_ret = _wapi_handle_lock_shared_handles ();
283 g_assert (thr_ret == 0);
285 if (initial != 0) {
286 _wapi_shared_handle_set_signal_state (handle, TRUE);
289 _wapi_handle_unlock_shared_handles ();
292 DEBUG ("%s: returning named sem handle %p", __func__, handle);
294 cleanup:
295 g_free (utf8_name);
297 _wapi_namespace_unlock (NULL);
299 return (ret);
304 * CreateSemaphore:
305 * @security: Ignored for now.
306 * @initial: The initial count for the semaphore. The value must be
307 * greater than or equal to zero, and less than or equal to @max.
308 * @max: The maximum count for this semaphore. The value must be
309 * greater than zero.
310 * @name: Pointer to a string specifying the name of this semaphore,
311 * or %NULL. Currently ignored.
313 * Creates a new semaphore handle. A semaphore is signalled when its
314 * count is greater than zero, and unsignalled otherwise. The count
315 * is decreased by one whenever a wait function releases a thread that
316 * was waiting for the semaphore. The count is increased by calling
317 * ReleaseSemaphore().
319 * Return value: a new handle, or NULL
321 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
323 mono_once (&sem_ops_once, sem_ops_init);
325 if (max <= 0) {
326 DEBUG ("%s: max <= 0", __func__);
328 SetLastError (ERROR_INVALID_PARAMETER);
329 return(NULL);
332 if (initial > max || initial < 0) {
333 DEBUG ("%s: initial>max or < 0", __func__);
335 SetLastError (ERROR_INVALID_PARAMETER);
336 return(NULL);
339 if (name == NULL) {
340 return (sem_create (security, initial, max));
341 } else {
342 return (namedsem_create (security, initial, max, name));
346 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount)
348 struct _WapiHandle_sem *sem_handle;
349 gboolean ok;
350 gboolean ret=FALSE;
351 int thr_ret;
353 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
354 (gpointer *)&sem_handle);
355 if (ok == FALSE) {
356 g_warning ("%s: error looking up sem handle %p", __func__,
357 handle);
358 return(FALSE);
361 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
362 handle);
363 thr_ret = _wapi_handle_lock_handle (handle);
364 g_assert (thr_ret == 0);
366 DEBUG ("%s: sem %p val %d count %d", __func__, handle,
367 sem_handle->val, count);
369 /* Do this before checking for count overflow, because overflowing max
370 * is a listed technique for finding the current value
372 if (prevcount != NULL) {
373 *prevcount = sem_handle->val;
376 /* No idea why max is signed, but thats the spec :-( */
377 if (sem_handle->val + count > (guint32)sem_handle->max) {
378 DEBUG ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
380 goto end;
383 sem_handle->val += count;
384 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
386 ret = TRUE;
388 DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
390 end:
391 thr_ret = _wapi_handle_unlock_handle (handle);
392 g_assert (thr_ret == 0);
393 pthread_cleanup_pop (0);
395 return(ret);
398 static gboolean namedsem_release (gpointer handle, gint32 count,
399 gint32 *prevcount)
401 struct _WapiHandle_namedsem *sem_handle;
402 gboolean ok;
403 gboolean ret=FALSE;
404 int thr_ret;
406 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
407 (gpointer *)&sem_handle);
408 if (ok == FALSE) {
409 g_warning ("%s: error looking up sem handle %p", __func__,
410 handle);
411 return(FALSE);
414 thr_ret = _wapi_handle_lock_shared_handles ();
415 g_assert (thr_ret == 0);
417 DEBUG("%s: named sem %p val %d count %d", __func__, handle,
418 sem_handle->val, count);
420 /* Do this before checking for count overflow, because overflowing max
421 * is a listed technique for finding the current value
423 if (prevcount != NULL) {
424 *prevcount = sem_handle->val;
427 /* No idea why max is signed, but thats the spec :-( */
428 if (sem_handle->val + count > (guint32)sem_handle->max) {
429 DEBUG ("%s: named sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
431 goto end;
434 sem_handle->val += count;
435 _wapi_shared_handle_set_signal_state (handle, TRUE);
437 ret = TRUE;
439 DEBUG("%s: named sem %p val now %d", __func__, handle,
440 sem_handle->val);
442 end:
443 _wapi_handle_unlock_shared_handles ();
445 return(ret);
449 * ReleaseSemaphore:
450 * @handle: The semaphore handle to release.
451 * @count: The amount by which the semaphore's count should be
452 * increased.
453 * @prevcount: Pointer to a location to store the previous count of
454 * the semaphore, or %NULL.
456 * Increases the count of semaphore @handle by @count.
458 * Return value: %TRUE on success, %FALSE otherwise.
460 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
462 WapiHandleType type;
464 if (handle == NULL) {
465 SetLastError (ERROR_INVALID_HANDLE);
466 return (FALSE);
469 type = _wapi_handle_type (handle);
471 if (sem_ops[type].release == NULL) {
472 SetLastError (ERROR_INVALID_HANDLE);
473 return (FALSE);
476 return (sem_ops[type].release (handle, count, prevcount));
479 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
480 const gunichar2 *name)
482 gpointer handle;
483 gchar *utf8_name;
484 int thr_ret;
485 gpointer ret = NULL;
486 gint32 offset;
488 mono_once (&sem_ops_once, sem_ops_init);
490 /* w32 seems to guarantee that opening named objects can't
491 * race each other
493 thr_ret = _wapi_namespace_lock ();
494 g_assert (thr_ret == 0);
496 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
498 DEBUG ("%s: Opening named sem [%s]", __func__, utf8_name);
500 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
501 utf8_name);
502 if (offset == -1) {
503 /* The name has already been used for a different
504 * object.
506 SetLastError (ERROR_INVALID_HANDLE);
507 goto cleanup;
508 } else if (offset == 0) {
509 /* This name doesn't exist */
510 SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */
511 goto cleanup;
514 /* A new reference to an existing named semaphore, so just
515 * create the private part
517 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset,
518 TRUE);
520 if (handle == _WAPI_HANDLE_INVALID) {
521 g_warning ("%s: error opening named sem handle", __func__);
522 SetLastError (ERROR_GEN_FAILURE);
523 goto cleanup;
525 ret = handle;
527 DEBUG ("%s: returning named sem handle %p", __func__, handle);
529 cleanup:
530 g_free (utf8_name);
532 _wapi_namespace_unlock (NULL);
534 return (ret);