2 * semaphores.c: Semaphore handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
13 #ifdef HAVE_SEMAPHORE_H
14 #include <semaphore.h>
20 #include <mono/io-layer/wapi.h>
21 #include <mono/io-layer/wapi-private.h>
22 #include <mono/io-layer/semaphore-private.h>
23 #include <mono/io-layer/io-trace.h>
24 #include <mono/utils/mono-once.h>
25 #include <mono/utils/mono-logger-internals.h>
26 #include <mono/utils/w32handle.h>
28 static void sema_signal(gpointer handle);
29 static gboolean sema_own (gpointer handle);
30 static void sema_details (gpointer data);
31 static const gchar* sema_typename (void);
32 static gsize sema_typesize (void);
34 static void namedsema_signal (gpointer handle);
35 static gboolean namedsema_own (gpointer handle);
36 static void namedsema_details (gpointer data);
37 static const gchar* namedsema_typename (void);
38 static gsize namedsema_typesize (void);
40 static MonoW32HandleOps _wapi_sem_ops = {
42 sema_signal, /* signal */
45 NULL, /* special_wait */
47 sema_details, /* details */
48 sema_typename, /* typename */
49 sema_typesize, /* typesize */
52 static MonoW32HandleOps _wapi_namedsem_ops = {
54 namedsema_signal, /* signal */
55 namedsema_own, /* own */
57 NULL, /* special_wait */
59 namedsema_details, /* details */
60 namedsema_typename, /* typename */
61 namedsema_typesize, /* typesize */
65 _wapi_semaphore_init (void)
67 mono_w32handle_register_ops (MONO_W32HANDLE_SEM, &_wapi_sem_ops);
68 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDSEM, &_wapi_namedsem_ops);
70 mono_w32handle_register_capabilities (MONO_W32HANDLE_SEM,
71 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
72 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDSEM,
73 (MonoW32HandleCapability)(MONO_W32HANDLE_CAP_WAIT | MONO_W32HANDLE_CAP_SIGNAL));
76 static const char* sem_handle_type_to_string (MonoW32HandleType type)
79 case MONO_W32HANDLE_SEM: return "sem";
80 case MONO_W32HANDLE_NAMEDSEM: return "named sem";
82 g_assert_not_reached ();
86 static gboolean sem_handle_own (gpointer handle, MonoW32HandleType type)
88 struct _WapiHandle_sem *sem_handle;
90 if (!mono_w32handle_lookup (handle, type, (gpointer *)&sem_handle)) {
91 g_warning ("%s: error looking up %s handle %p",
92 __func__, sem_handle_type_to_string (type), handle);
96 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: owning %s handle %p",
97 __func__, sem_handle_type_to_string (type), handle);
101 if (sem_handle->val == 0)
102 mono_w32handle_set_signal_state (handle, FALSE, FALSE);
107 static void sema_signal(gpointer handle)
109 ReleaseSemaphore(handle, 1, NULL);
112 static gboolean sema_own (gpointer handle)
114 return sem_handle_own (handle, MONO_W32HANDLE_SEM);
117 static void namedsema_signal (gpointer handle)
119 ReleaseSemaphore (handle, 1, NULL);
122 /* NB, always called with the shared handle lock held */
123 static gboolean namedsema_own (gpointer handle)
125 return sem_handle_own (handle, MONO_W32HANDLE_NAMEDSEM);
128 static void sema_details (gpointer data)
130 struct _WapiHandle_sem *sem = (struct _WapiHandle_sem *)data;
131 g_print ("val: %5u, max: %5d", sem->val, sem->max);
134 static void namedsema_details (gpointer data)
136 struct _WapiHandle_namedsem *namedsem = (struct _WapiHandle_namedsem *)data;
137 g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem->s.val, namedsem->s.max, namedsem->sharedns.name);
140 static const gchar* sema_typename (void)
145 static gsize sema_typesize (void)
147 return sizeof (struct _WapiHandle_sem);
150 static const gchar* namedsema_typename (void)
152 return "N.Semaphore";
155 static gsize namedsema_typesize (void)
157 return sizeof (struct _WapiHandle_namedsem);
160 static gpointer sem_handle_create (struct _WapiHandle_sem *sem_handle, MonoW32HandleType type, gint32 initial, gint32 max)
165 sem_handle->val = initial;
166 sem_handle->max = max;
168 handle = mono_w32handle_new (type, sem_handle);
169 if (handle == INVALID_HANDLE_VALUE) {
170 g_warning ("%s: error creating %s handle",
171 __func__, sem_handle_type_to_string (type));
172 SetLastError (ERROR_GEN_FAILURE);
176 thr_ret = mono_w32handle_lock_handle (handle);
177 g_assert (thr_ret == 0);
180 mono_w32handle_set_signal_state (handle, TRUE, FALSE);
182 thr_ret = mono_w32handle_unlock_handle (handle);
183 g_assert (thr_ret == 0);
185 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: created %s handle %p",
186 __func__, sem_handle_type_to_string (type), handle);
191 static gpointer sem_create (gint32 initial, gint32 max)
193 struct _WapiHandle_sem sem_handle;
194 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle, initial %d max %d",
195 __func__, sem_handle_type_to_string (MONO_W32HANDLE_SEM), initial, max);
196 return sem_handle_create (&sem_handle, MONO_W32HANDLE_SEM, initial, max);
199 static gpointer namedsem_create (gint32 initial, gint32 max, const gunichar2 *name)
205 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: creating %s handle, initial %d max %d name \"%s\"",
206 __func__, sem_handle_type_to_string (MONO_W32HANDLE_NAMEDSEM), initial, max, name);
208 /* w32 seems to guarantee that opening named objects can't race each other */
209 thr_ret = _wapi_namespace_lock ();
210 g_assert (thr_ret == 0);
212 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
214 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Creating named sem name [%s] initial %d max %d", __func__, utf8_name, initial, max);
216 handle = _wapi_search_handle_namespace (MONO_W32HANDLE_NAMEDSEM, utf8_name);
217 if (handle == INVALID_HANDLE_VALUE) {
218 /* The name has already been used for a different object. */
220 SetLastError (ERROR_INVALID_HANDLE);
222 /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */
223 SetLastError (ERROR_ALREADY_EXISTS);
225 /* this is used as creating a new handle */
226 mono_w32handle_ref (handle);
228 /* A new named semaphore */
229 struct _WapiHandle_namedsem namedsem_handle;
231 strncpy (&namedsem_handle.sharedns.name [0], utf8_name, MAX_PATH);
232 namedsem_handle.sharedns.name [MAX_PATH] = '\0';
234 handle = sem_handle_create ((struct _WapiHandle_sem*) &namedsem_handle, MONO_W32HANDLE_NAMEDSEM, initial, max);
239 thr_ret = _wapi_namespace_unlock (NULL);
240 g_assert (thr_ret == 0);
248 * @security: Ignored for now.
249 * @initial: The initial count for the semaphore. The value must be
250 * greater than or equal to zero, and less than or equal to @max.
251 * @max: The maximum count for this semaphore. The value must be
253 * @name: Pointer to a string specifying the name of this semaphore,
254 * or %NULL. Currently ignored.
256 * Creates a new semaphore handle. A semaphore is signalled when its
257 * count is greater than zero, and unsignalled otherwise. The count
258 * is decreased by one whenever a wait function releases a thread that
259 * was waiting for the semaphore. The count is increased by calling
260 * ReleaseSemaphore().
262 * Return value: a new handle, or NULL
264 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name)
267 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: max <= 0", __func__);
269 SetLastError (ERROR_INVALID_PARAMETER);
273 if (initial > max || initial < 0) {
274 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: initial>max or < 0", __func__);
276 SetLastError (ERROR_INVALID_PARAMETER);
280 /* Need to blow away any old errors here, because code tests
281 * for ERROR_ALREADY_EXISTS on success (!) to see if a
282 * semaphore was freshly created
284 SetLastError (ERROR_SUCCESS);
286 return name ? namedsem_create (initial, max, name) : sem_create (initial, max);
291 * @handle: The semaphore handle to release.
292 * @count: The amount by which the semaphore's count should be
294 * @prevcount: Pointer to a location to store the previous count of
295 * the semaphore, or %NULL.
297 * Increases the count of semaphore @handle by @count.
299 * Return value: %TRUE on success, %FALSE otherwise.
301 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
303 MonoW32HandleType type;
304 struct _WapiHandle_sem *sem_handle;
309 SetLastError (ERROR_INVALID_HANDLE);
313 switch (type = mono_w32handle_get_type (handle)) {
314 case MONO_W32HANDLE_SEM:
315 case MONO_W32HANDLE_NAMEDSEM:
318 SetLastError (ERROR_INVALID_HANDLE);
322 if (!mono_w32handle_lookup (handle, type, (gpointer *)&sem_handle)) {
323 g_warning ("%s: error looking up sem handle %p", __func__, handle);
327 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: releasing %s handle %p",
328 __func__, sem_handle_type_to_string (type), handle);
330 thr_ret = mono_w32handle_lock_handle (handle);
331 g_assert (thr_ret == 0);
333 /* Do this before checking for count overflow, because overflowing
334 * max is a listed technique for finding the current value */
336 *prevcount = sem_handle->val;
338 /* No idea why max is signed, but thats the spec :-( */
339 if (sem_handle->val + count > (guint32)sem_handle->max) {
340 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d, max value would be exceeded",
341 __func__, sem_handle_type_to_string (type), handle, sem_handle->val, count, sem_handle->max, count);
345 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: %s handle %p val %d count %d max %d",
346 __func__, sem_handle_type_to_string (type), handle, sem_handle->val, count, sem_handle->max, count);
348 sem_handle->val += count;
349 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
354 thr_ret = mono_w32handle_unlock_handle (handle);
355 g_assert (thr_ret == 0);
360 gpointer OpenSemaphore (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED,
361 const gunichar2 *name)
367 /* w32 seems to guarantee that opening named objects can't
370 thr_ret = _wapi_namespace_lock ();
371 g_assert (thr_ret == 0);
373 utf8_name = g_utf16_to_utf8 (name, -1, NULL, NULL, NULL);
375 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Opening named sem [%s]", __func__, utf8_name);
377 handle = _wapi_search_handle_namespace (MONO_W32HANDLE_NAMEDSEM,
379 if (handle == INVALID_HANDLE_VALUE) {
380 /* The name has already been used for a different
383 SetLastError (ERROR_INVALID_HANDLE);
385 } else if (!handle) {
386 /* This name doesn't exist */
387 SetLastError (ERROR_FILE_NOT_FOUND); /* yes, really */
391 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: returning named sem handle %p", __func__, handle);
396 _wapi_namespace_unlock (NULL);