update rx (mobile builds).
[mono-project.git] / mono / io-layer / semaphores.c
blob8b4bb0ebcca6599b727ef5feb5d3b1f6791dd09f
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 #if 0
28 #define DEBUG(...) g_message(__VA_ARGS__)
29 #else
30 #define DEBUG(...)
31 #endif
33 static void sema_signal(gpointer handle);
34 static gboolean sema_own (gpointer handle);
36 static void namedsema_signal (gpointer handle);
37 static gboolean namedsema_own (gpointer handle);
39 struct _WapiHandleOps _wapi_sem_ops = {
40 NULL, /* close */
41 sema_signal, /* signal */
42 sema_own, /* own */
43 NULL, /* is_owned */
44 NULL, /* special_wait */
45 NULL /* prewait */
48 void _wapi_sem_details (gpointer handle_info)
50 struct _WapiHandle_sem *sem = (struct _WapiHandle_sem *)handle_info;
52 g_print ("val: %5u, max: %5d", sem->val, sem->max);
55 struct _WapiHandleOps _wapi_namedsem_ops = {
56 NULL, /* close */
57 namedsema_signal, /* signal */
58 namedsema_own, /* own */
59 NULL, /* is_owned */
60 NULL, /* special_wait */
61 NULL /* prewait */
64 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prev);
65 static gboolean namedsem_release (gpointer handle, gint32 count, gint32 *prev);
67 static struct
69 gboolean (*release)(gpointer handle, gint32 count, gint32 *prev);
70 } sem_ops[WAPI_HANDLE_COUNT] = {
71 {NULL},
72 {NULL},
73 {NULL},
74 {NULL},
75 {sem_release},
76 {NULL},
77 {NULL},
78 {NULL},
79 {NULL},
80 {NULL},
81 {NULL},
82 {NULL},
83 {namedsem_release},
86 static mono_once_t sem_ops_once=MONO_ONCE_INIT;
88 static void sem_ops_init (void)
90 _wapi_handle_register_capabilities (WAPI_HANDLE_SEM,
91 WAPI_HANDLE_CAP_WAIT |
92 WAPI_HANDLE_CAP_SIGNAL);
93 _wapi_handle_register_capabilities (WAPI_HANDLE_NAMEDSEM,
94 WAPI_HANDLE_CAP_WAIT |
95 WAPI_HANDLE_CAP_SIGNAL);
98 static void sema_signal(gpointer handle)
100 ReleaseSemaphore(handle, 1, NULL);
103 static gboolean sema_own (gpointer handle)
105 struct _WapiHandle_sem *sem_handle;
106 gboolean ok;
108 ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
109 (gpointer *)&sem_handle);
110 if(ok==FALSE) {
111 g_warning ("%s: error looking up sem handle %p", __func__,
112 handle);
113 return(FALSE);
116 DEBUG("%s: owning sem handle %p", __func__, handle);
118 sem_handle->val--;
120 DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
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 DEBUG ("%s: owning named sem handle %p", __func__, handle);
142 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
143 (gpointer *)&namedsem_handle);
144 if (ok == FALSE) {
145 g_warning ("%s: error looking up named sem handle %p",
146 __func__, handle);
147 return (FALSE);
150 namedsem_handle->val--;
152 DEBUG ("%s: named sem %p val now %d", __func__, handle,
153 namedsem_handle->val);
155 if (namedsem_handle->val == 0) {
156 _wapi_shared_handle_set_signal_state (handle, FALSE);
159 return (TRUE);
161 static gpointer sem_create (WapiSecurityAttributes *security G_GNUC_UNUSED,
162 gint32 initial, gint32 max)
164 struct _WapiHandle_sem sem_handle = {0};
165 gpointer handle;
166 int thr_ret;
168 /* Need to blow away any old errors here, because code tests
169 * for ERROR_ALREADY_EXISTS on success (!) to see if a
170 * semaphore was freshly created
172 SetLastError (ERROR_SUCCESS);
174 sem_handle.val = initial;
175 sem_handle.max = max;
177 handle = _wapi_handle_new (WAPI_HANDLE_SEM, &sem_handle);
178 if (handle == _WAPI_HANDLE_INVALID) {
179 g_warning ("%s: error creating semaphore handle", __func__);
180 SetLastError (ERROR_GEN_FAILURE);
181 return(NULL);
184 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
185 handle);
186 thr_ret = _wapi_handle_lock_handle (handle);
187 g_assert (thr_ret == 0);
189 if (initial != 0) {
190 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
193 DEBUG ("%s: Created semaphore handle %p initial %d max %d",
194 __func__, handle, initial, max);
196 thr_ret = _wapi_handle_unlock_handle (handle);
197 g_assert (thr_ret == 0);
198 pthread_cleanup_pop (0);
200 return(handle);
203 static gpointer namedsem_create (WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
205 struct _WapiHandle_namedsem namedsem_handle = {{{0}}, 0};
206 gpointer handle;
207 gchar *utf8_name;
208 int thr_ret;
209 gpointer ret = NULL;
210 guint32 namelen;
211 gint32 offset;
213 /* w32 seems to guarantee that opening named objects can't
214 * race each other
216 thr_ret = _wapi_namespace_lock ();
217 g_assert (thr_ret == 0);
219 /* Need to blow away any old errors here, because code tests
220 * for ERROR_ALREADY_EXISTS on success (!) to see if a
221 * semaphore was freshly created
223 SetLastError (ERROR_SUCCESS);
225 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
227 DEBUG ("%s: Creating named sem [%s]", __func__, utf8_name);
229 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
230 utf8_name);
231 if (offset == -1) {
232 /* The name has already been used for a different
233 * object.
235 SetLastError (ERROR_INVALID_HANDLE);
236 goto cleanup;
237 } else if (offset != 0) {
238 /* Not an error, but this is how the caller is
239 * informed that the semaphore wasn't freshly created
241 SetLastError (ERROR_ALREADY_EXISTS);
243 /* Fall through to create the semaphore handle */
245 if (offset == 0) {
246 /* A new named semaphore, so create both the private
247 * and shared parts
249 if (strlen (utf8_name) < MAX_PATH) {
250 namelen = strlen (utf8_name);
251 } else {
252 namelen = MAX_PATH;
255 memcpy (&namedsem_handle.sharedns.name, utf8_name, namelen);
257 namedsem_handle.val = initial;
258 namedsem_handle.max = max;
260 handle = _wapi_handle_new (WAPI_HANDLE_NAMEDSEM,
261 &namedsem_handle);
262 } else {
263 /* A new reference to an existing named semaphore, so
264 * just create the private part
266 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM,
267 offset, TRUE);
270 if (handle == _WAPI_HANDLE_INVALID) {
271 g_warning ("%s: error creating named sem handle", __func__);
272 SetLastError (ERROR_GEN_FAILURE);
273 goto cleanup;
275 ret = handle;
277 if (offset == 0) {
278 /* Set the initial state, as this is a completely new
279 * handle
281 thr_ret = _wapi_handle_lock_shared_handles ();
282 g_assert (thr_ret == 0);
284 if (initial != 0) {
285 _wapi_shared_handle_set_signal_state (handle, TRUE);
288 _wapi_handle_unlock_shared_handles ();
291 DEBUG ("%s: returning named sem handle %p", __func__, handle);
293 cleanup:
294 g_free (utf8_name);
296 _wapi_namespace_unlock (NULL);
298 return (ret);
303 * CreateSemaphore:
304 * @security: Ignored for now.
305 * @initial: The initial count for the semaphore. The value must be
306 * greater than or equal to zero, and less than or equal to @max.
307 * @max: The maximum count for this semaphore. The value must be
308 * greater than zero.
309 * @name: Pointer to a string specifying the name of this semaphore,
310 * or %NULL. Currently ignored.
312 * Creates a new semaphore handle. A semaphore is signalled when its
313 * count is greater than zero, and unsignalled otherwise. The count
314 * is decreased by one whenever a wait function releases a thread that
315 * was waiting for the semaphore. The count is increased by calling
316 * ReleaseSemaphore().
318 * Return value: a new handle, or NULL
320 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
322 mono_once (&sem_ops_once, sem_ops_init);
324 if (max <= 0) {
325 DEBUG ("%s: max <= 0", __func__);
327 SetLastError (ERROR_INVALID_PARAMETER);
328 return(NULL);
331 if (initial > max || initial < 0) {
332 DEBUG ("%s: initial>max or < 0", __func__);
334 SetLastError (ERROR_INVALID_PARAMETER);
335 return(NULL);
338 if (name == NULL) {
339 return (sem_create (security, initial, max));
340 } else {
341 return (namedsem_create (security, initial, max, name));
345 static gboolean sem_release (gpointer handle, gint32 count, gint32 *prevcount)
347 struct _WapiHandle_sem *sem_handle;
348 gboolean ok;
349 gboolean ret=FALSE;
350 int thr_ret;
352 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
353 (gpointer *)&sem_handle);
354 if (ok == FALSE) {
355 g_warning ("%s: error looking up sem handle %p", __func__,
356 handle);
357 return(FALSE);
360 pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
361 handle);
362 thr_ret = _wapi_handle_lock_handle (handle);
363 g_assert (thr_ret == 0);
365 DEBUG ("%s: sem %p val %d count %d", __func__, handle,
366 sem_handle->val, count);
368 /* Do this before checking for count overflow, because overflowing max
369 * is a listed technique for finding the current value
371 if (prevcount != NULL) {
372 *prevcount = sem_handle->val;
375 /* No idea why max is signed, but thats the spec :-( */
376 if (sem_handle->val + count > (guint32)sem_handle->max) {
377 DEBUG ("%s: sem %p max value would be exceeded: max %d current %d count %d", __func__, handle, sem_handle->max, sem_handle->val, count);
379 goto end;
382 sem_handle->val += count;
383 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
385 ret = TRUE;
387 DEBUG ("%s: sem %p val now %d", __func__, handle, sem_handle->val);
389 end:
390 thr_ret = _wapi_handle_unlock_handle (handle);
391 g_assert (thr_ret == 0);
392 pthread_cleanup_pop (0);
394 return(ret);
397 static gboolean namedsem_release (gpointer handle, gint32 count,
398 gint32 *prevcount)
400 struct _WapiHandle_namedsem *sem_handle;
401 gboolean ok;
402 gboolean ret=FALSE;
403 int thr_ret;
405 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_NAMEDSEM,
406 (gpointer *)&sem_handle);
407 if (ok == FALSE) {
408 g_warning ("%s: error looking up sem handle %p", __func__,
409 handle);
410 return(FALSE);
413 thr_ret = _wapi_handle_lock_shared_handles ();
414 g_assert (thr_ret == 0);
416 DEBUG("%s: named sem %p val %d count %d", __func__, handle,
417 sem_handle->val, count);
419 /* Do this before checking for count overflow, because overflowing max
420 * is a listed technique for finding the current value
422 if (prevcount != NULL) {
423 *prevcount = sem_handle->val;
426 /* No idea why max is signed, but thats the spec :-( */
427 if (sem_handle->val + count > (guint32)sem_handle->max) {
428 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);
430 goto end;
433 sem_handle->val += count;
434 _wapi_shared_handle_set_signal_state (handle, TRUE);
436 ret = TRUE;
438 DEBUG("%s: named sem %p val now %d", __func__, handle,
439 sem_handle->val);
441 end:
442 _wapi_handle_unlock_shared_handles ();
444 return(ret);
448 * ReleaseSemaphore:
449 * @handle: The semaphore handle to release.
450 * @count: The amount by which the semaphore's count should be
451 * increased.
452 * @prevcount: Pointer to a location to store the previous count of
453 * the semaphore, or %NULL.
455 * Increases the count of semaphore @handle by @count.
457 * Return value: %TRUE on success, %FALSE otherwise.
459 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
461 WapiHandleType type;
463 if (handle == NULL) {
464 SetLastError (ERROR_INVALID_HANDLE);
465 return (FALSE);
468 type = _wapi_handle_type (handle);
470 if (sem_ops[type].release == NULL) {
471 SetLastError (ERROR_INVALID_HANDLE);
472 return (FALSE);
475 return (sem_ops[type].release (handle, count, prevcount));
478 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
479 const gunichar2 *name)
481 gpointer handle;
482 gchar *utf8_name;
483 int thr_ret;
484 gpointer ret = NULL;
485 gint32 offset;
487 mono_once (&sem_ops_once, sem_ops_init);
489 /* w32 seems to guarantee that opening named objects can't
490 * race each other
492 thr_ret = _wapi_namespace_lock ();
493 g_assert (thr_ret == 0);
495 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
497 DEBUG ("%s: Opening named sem [%s]", __func__, utf8_name);
499 offset = _wapi_search_handle_namespace (WAPI_HANDLE_NAMEDSEM,
500 utf8_name);
501 if (offset == -1) {
502 /* The name has already been used for a different
503 * object.
505 SetLastError (ERROR_INVALID_HANDLE);
506 goto cleanup;
507 } else if (offset == 0) {
508 /* This name doesn't exist */
509 SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */
510 goto cleanup;
513 /* A new reference to an existing named semaphore, so just
514 * create the private part
516 handle = _wapi_handle_new_from_offset (WAPI_HANDLE_NAMEDSEM, offset,
517 TRUE);
519 if (handle == _WAPI_HANDLE_INVALID) {
520 g_warning ("%s: error opening named sem handle", __func__);
521 SetLastError (ERROR_GEN_FAILURE);
522 goto cleanup;
524 ret = handle;
526 DEBUG ("%s: returning named sem handle %p", __func__, handle);
528 cleanup:
529 g_free (utf8_name);
531 _wapi_namespace_unlock (NULL);
533 return (ret);