2010-02-13 Jb Evain <jbevain@novell.com>
[mono-project.git] / mono / io-layer / semaphores.c
blob73fda73552db2218fbe39388cf08279f08fe85d1
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/mono-mutex.h>
25 #include <mono/io-layer/semaphore-private.h>
27 #undef DEBUG
29 static void sema_signal(gpointer handle);
30 static gboolean sema_own (gpointer handle);
32 static void namedsema_signal (gpointer handle);
33 static gboolean namedsema_own (gpointer handle);
35 struct _WapiHandleOps _wapi_sem_ops = {
36 NULL, /* close */
37 sema_signal, /* signal */
38 sema_own, /* own */
39 NULL, /* is_owned */
40 NULL, /* special_wait */
41 NULL /* prewait */
44 void _wapi_sem_details (gpointer handle_info)
46 struct _WapiHandle_sem *sem = (struct _WapiHandle_sem *)handle_info;
48 g_print ("val: %5u, max: %5d", sem->val, sem->max);
51 struct _WapiHandleOps _wapi_namedsem_ops = {
52 NULL, /* close */
53 namedsema_signal, /* signal */
54 namedsema_own, /* own */
55 NULL, /* is_owned */
56 NULL, /* special_wait */
57 NULL /* prewait */
60 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prev);
61 static gboolean namedsem_release (gpointer handle, gint32 count, gint32 *prev);
63 static struct
65 gboolean (*release)(gpointer handle, gint32 count, gint32 *prev);
66 } sem_ops[WAPI_HANDLE_COUNT] = {
67 {NULL},
68 {NULL},
69 {NULL},
70 {NULL},
71 {sem_release},
72 {NULL},
73 {NULL},
74 {NULL},
75 {NULL},
76 {NULL},
77 {NULL},
78 {NULL},
79 {namedsem_release},
82 static mono_once_t sem_ops_once=MONO_ONCE_INIT;
84 static void sem_ops_init (void)
86 _wapi_handle_register_capabilities (WAPI_HANDLE_SEM,
87 WAPI_HANDLE_CAP_WAIT |
88 WAPI_HANDLE_CAP_SIGNAL);
89 _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDSEM,
90 WAPI_HANDLE_CAP_WAIT |
91 WAPI_HANDLE_CAP_SIGNAL);
94 static void sema_signal(gpointer handle)
96 ReleaseSemaphore(handle, 1, NULL);
99 static gboolean sema_own (gpointer handle)
101 struct _WapiHandle_sem *sem_handle;
102 gboolean ok;
104 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
105 (gpointer *)&sem_handle);
106 if(ok==FALSE) {
107 g_warning ("%s: error looking up sem handle %p", __func__,
108 handle);
109 return(FALSE);
112 #ifdef DEBUG
113 g_message("%s: owning sem handle %p", __func__, handle);
114 #endif
116 sem_handle->val--;
118 #ifdef DEBUG
119 g_message ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
120 #endif
122 if(sem_handle->val==0) {
123 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
126 return(TRUE);
129 static void namedsema_signal (gpointer handle)
131 ReleaseSemaphore (handle, 1, NULL);
134 /* NB, always called with the shared handle lock held */
135 static gboolean namedsema_own (gpointer handle)
137 struct _WapiHandle_namedsem *namedsem_handle;
138 gboolean ok;
140 #ifdef DEBUG
141 g_message ("%s: owning named sem handle %p", __func__, handle);
142 #endif
144 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
145 (gpointer *)&namedsem_handle);
146 if (ok == FALSE) {
147 g_warning ("%s: error looking up named sem handle %p",
148 __func__, handle);
149 return (FALSE);
152 namedsem_handle->val--;
154 #ifdef DEBUG
155 g_message ("%s: named sem %p val now %d", __func__, handle,
156 namedsem_handle->val);
157 #endif
159 if (namedsem_handle->val == 0) {
160 _wapi_shared_handle_set_signal_state (handle, FALSE);
163 return (TRUE);
165 static gpointer sem_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
166 gint32 initial, gint32 max)
168 struct _WapiHandle_sem sem_handle = {0};
169 gpointer handle;
170 int thr_ret;
172 /* Need to blow away any old errors here, because code tests
173 * for ERROR_ALREADY_EXISTS on success (!) to see if a
174 * semaphore was freshly created
176 SetLastError (ERROR_SUCCESS);
178 sem_handle.val = initial;
179 sem_handle.max = max;
181 handle = _wapi_handle_new (WAPI_HANDLE_SEM, &sem_handle);
182 if (handle == _WAPI_HANDLE_INVALID) {
183 g_warning ("%s: error creating semaphore handle", __func__);
184 SetLastError (ERROR_GEN_FAILURE);
185 return(NULL);
188 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
189 handle);
190 thr_ret = _wapi_handle_lock_handle (handle);
191 g_assert (thr_ret == 0);
193 if (initial != 0) {
194 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
197 #ifdef DEBUG
198 g_message ("%s: Created semaphore handle %p initial %d max %d",
199 __func__, handle, initial, max);
200 #endif
202 thr_ret = _wapi_handle_unlock_handle (handle);
203 g_assert (thr_ret == 0);
204 pthread_cleanup_pop (0);
206 return(handle);
209 static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
211 struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0};
212 gpointer handle;
213 gchar *utf8_name;
214 int thr_ret;
215 gpointer ret = NULL;
216 guint32 namelen;
217 gint32 offset;
219 /* w32 seems to guarantee that opening named objects can't
220 * race each other
222 thr_ret = _wapi_namespace_lock ();
223 g_assert (thr_ret == 0);
225 /* Need to blow away any old errors here, because code tests
226 * for ERROR_ALREADY_EXISTS on success (!) to see if a
227 * semaphore was freshly created
229 SetLastError (ERROR_SUCCESS);
231 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
233 #ifdef DEBUG
234 g_message ("%s: Creating named sem [%s]", __func__, utf8_name);
235 #endif
237 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
238 utf8_name);
239 if (offset == -1) {
240 /* The name has already been used for a different
241 * object.
243 SetLastError (ERROR_INVALID_HANDLE);
244 goto cleanup;
245 } else if (offset != 0) {
246 /* Not an error, but this is how the caller is
247 * informed that the semaphore wasn't freshly created
249 SetLastError (ERROR_ALREADY_EXISTS);
251 /* Fall through to create the semaphore handle */
253 if (offset == 0) {
254 /* A new named semaphore, so create both the private
255 * and shared parts
257 if (strlen (utf8_name) < MAX_PATH) {
258 namelen = strlen (utf8_name);
259 } else {
260 namelen = MAX_PATH;
263 memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen);
265 namedsem_handle.val = initial;
266 namedsem_handle.max = max;
268 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM,
269 &namedsem_handle);
270 } else {
271 /* A new reference to an existing named semaphore, so
272 * just create the private part
274 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM,
275 offset, TRUE);
278 if (handle == _WAPI_HANDLE_INVALID) {
279 g_warning ("%s: error creating named sem handle", __func__);
280 SetLastError (ERROR_GEN_FAILURE);
281 goto cleanup;
283 ret = handle;
285 if (offset == 0) {
286 /* Set the initial state, as this is a completely new
287 * handle
289 thr_ret = _wapi_handle_lock_shared_handles ();
290 g_assert (thr_ret == 0);
292 if (initial != 0) {
293 _wapi_shared_handle_set_signal_state (handle, TRUE);
296 _wapi_handle_unlock_shared_handles ();
299 #ifdef DEBUG
300 g_message ("%s: returning named sem handle %p", __func__, handle);
301 #endif
303 cleanup:
304 g_free (utf8_name);
306 _wapi_namespace_unlock (NULL);
308 return (ret);
313 * CreateSemaphore:
314 * @security: Ignored for now.
315 * @initial: The initial count for the semaphore. The value must be
316 * greater than or equal to zero, and less than or equal to @max.
317 * @max: The maximum count for this semaphore. The value must be
318 * greater than zero.
319 * @name: Pointer to a string specifying the name of this semaphore,
320 * or %NULL. Currently ignored.
322 * Creates a new semaphore handle. A semaphore is signalled when its
323 * count is greater than zero, and unsignalled otherwise. The count
324 * is decreased by one whenever a wait function releases a thread that
325 * was waiting for the semaphore. The count is increased by calling
326 * ReleaseSemaphore().
328 * Return value: a new handle, or NULL
330 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
332 mono_once (&sem_ops_once, sem_ops_init);
334 if (max <= 0) {
335 #ifdef DEBUG
336 g_message ("%s: max <= 0", __func__);
337 #endif
339 SetLastError (ERROR_INVALID_PARAMETER);
340 return(NULL);
343 if (initial > max || initial < 0) {
344 #ifdef DEBUG
345 g_message ("%s: initial>max or < 0", __func__);
346 #endif
348 SetLastError (ERROR_INVALID_PARAMETER);
349 return(NULL);
352 if (name == NULL) {
353 return (sem_create (security, initial, max));
354 } else {
355 return (namedsem_create (security, initial, max, name));
359 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount)
361 struct _WapiHandle_sem *sem_handle;
362 gboolean ok;
363 gboolean ret=FALSE;
364 int thr_ret;
366 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
367 (gpointer *)&sem_handle);
368 if (ok == FALSE) {
369 g_warning ("%s: error looking up sem handle %p", __func__,
370 handle);
371 return(FALSE);
374 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
375 handle);
376 thr_ret = _wapi_handle_lock_handle (handle);
377 g_assert (thr_ret == 0);
379 #ifdef DEBUG
380 g_message ("%s: sem %p val %d count %d", __func__, handle,
381 sem_handle->val, count);
382 #endif
384 /* Do this before checking for count overflow, because overflowing max
385 * is a listed technique for finding the current value
387 if (prevcount != NULL) {
388 *prevcount = sem_handle->val;
391 /* No idea why max is signed, but thats the spec :-( */
392 if (sem_handle->val + count > (guint32)sem_handle->max) {
393 #ifdef DEBUG
394 g_message ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
395 #endif
397 goto end;
400 sem_handle->val += count;
401 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
403 ret = TRUE;
405 #ifdef DEBUG
406 g_message ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
407 #endif
409 end:
410 thr_ret = _wapi_handle_unlock_handle (handle);
411 g_assert (thr_ret == 0);
412 pthread_cleanup_pop (0);
414 return(ret);
417 static gboolean namedsem_release (gpointer handle, gint32 count,
418 gint32 *prevcount)
420 struct _WapiHandle_namedsem *sem_handle;
421 gboolean ok;
422 gboolean ret=FALSE;
423 int thr_ret;
425 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
426 (gpointer *)&sem_handle);
427 if (ok == FALSE) {
428 g_warning ("%s: error looking up sem handle %p", __func__,
429 handle);
430 return(FALSE);
433 thr_ret = _wapi_handle_lock_shared_handles ();
434 g_assert (thr_ret == 0);
436 #ifdef DEBUG
437 g_message("%s: named sem %p val %d count %d", __func__, handle,
438 sem_handle->val, count);
439 #endif
441 /* Do this before checking for count overflow, because overflowing max
442 * is a listed technique for finding the current value
444 if (prevcount != NULL) {
445 *prevcount = sem_handle->val;
448 /* No idea why max is signed, but thats the spec :-( */
449 if (sem_handle->val + count > (guint32)sem_handle->max) {
450 #ifdef DEBUG
451 g_message ("%s: named sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
452 #endif
454 goto end;
457 sem_handle->val += count;
458 _wapi_shared_handle_set_signal_state (handle, TRUE);
460 ret = TRUE;
462 #ifdef DEBUG
463 g_message("%s: named sem %p val now %d", __func__, handle,
464 sem_handle->val);
465 #endif
467 end:
468 _wapi_handle_unlock_shared_handles ();
470 return(ret);
474 * ReleaseSemaphore:
475 * @handle: The semaphore handle to release.
476 * @count: The amount by which the semaphore's count should be
477 * increased.
478 * @prevcount: Pointer to a location to store the previous count of
479 * the semaphore, or %NULL.
481 * Increases the count of semaphore @handle by @count.
483 * Return value: %TRUE on success, %FALSE otherwise.
485 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
487 WapiHandleType type;
489 if (handle == NULL) {
490 SetLastError (ERROR_INVALID_HANDLE);
491 return (FALSE);
494 type = _wapi_handle_type (handle);
496 if (sem_ops[type].release == NULL) {
497 SetLastError (ERROR_INVALID_HANDLE);
498 return (FALSE);
501 return (sem_ops[type].release (handle, count, prevcount));
504 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
505 const gunichar2 *name)
507 gpointer handle;
508 gchar *utf8_name;
509 int thr_ret;
510 gpointer ret = NULL;
511 gint32 offset;
513 mono_once (&sem_ops_once, sem_ops_init);
515 /* w32 seems to guarantee that opening named objects can't
516 * race each other
518 thr_ret = _wapi_namespace_lock ();
519 g_assert (thr_ret == 0);
521 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
523 #ifdef DEBUG
524 g_message ("%s: Opening named sem [%s]", __func__, utf8_name);
525 #endif
527 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
528 utf8_name);
529 if (offset == -1) {
530 /* The name has already been used for a different
531 * object.
533 SetLastError (ERROR_INVALID_HANDLE);
534 goto cleanup;
535 } else if (offset == 0) {
536 /* This name doesn't exist */
537 SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */
538 goto cleanup;
541 /* A new reference to an existing named semaphore, so just
542 * create the private part
544 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset,
545 TRUE);
547 if (handle == _WAPI_HANDLE_INVALID) {
548 g_warning ("%s: error opening named sem handle", __func__);
549 SetLastError (ERROR_GEN_FAILURE);
550 goto cleanup;
552 ret = handle;
554 #ifdef DEBUG
555 g_message ("%s: returning named sem handle %p", __func__, handle);
556 #endif
558 cleanup:
559 g_free (utf8_name);
561 _wapi_namespace_unlock (NULL);
563 return (ret);