3 * Runtime support for managed Semaphore on Unix
6 * Ludovic Henry (luhenry@microsoft.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #include "w32semaphore.h"
13 #include "w32handle-namespace.h"
14 #include "mono/utils/mono-logger-internals.h"
15 #include "mono/metadata/w32handle.h"
16 #include "object-internals.h"
17 #include "icall-decl.h"
24 } MonoW32HandleSemaphore
;
26 struct MonoW32HandleNamedSemaphore
{
27 MonoW32HandleSemaphore s
;
28 MonoW32HandleNamespace sharedns
;
31 static void sem_handle_signal (MonoW32Handle
*handle_data
)
33 MonoW32HandleSemaphore
*sem_handle
;
35 sem_handle
= (MonoW32HandleSemaphore
*) handle_data
->specific
;
37 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: signalling %s handle %p",
38 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
);
40 /* No idea why max is signed, but thats the spec :-( */
41 if (sem_handle
->val
+ 1 > (guint32
)sem_handle
->max
) {
42 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d, max value would be exceeded",
43 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, sem_handle
->val
, 1, sem_handle
->max
);
45 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d",
46 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, sem_handle
->val
, 1, sem_handle
->max
);
49 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
53 static gboolean
sem_handle_own (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
55 MonoW32HandleSemaphore
*sem_handle
;
59 sem_handle
= (MonoW32HandleSemaphore
*) handle_data
->specific
;
61 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: owning %s handle %p",
62 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
);
66 if (sem_handle
->val
== 0)
67 mono_w32handle_set_signal_state (handle_data
, FALSE
, FALSE
);
72 static void sema_details (MonoW32Handle
*handle_data
)
74 MonoW32HandleSemaphore
*sem
= (MonoW32HandleSemaphore
*)handle_data
->specific
;
75 g_print ("val: %5u, max: %5d", sem
->val
, sem
->max
);
78 static void namedsema_details (MonoW32Handle
*handle_data
)
80 MonoW32HandleNamedSemaphore
*namedsem
= (MonoW32HandleNamedSemaphore
*)handle_data
->specific
;
81 g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem
->s
.val
, namedsem
->s
.max
, namedsem
->sharedns
.name
);
84 static const gchar
* sema_typename (void)
89 static gsize
sema_typesize (void)
91 return sizeof (MonoW32HandleSemaphore
);
94 static const gchar
* namedsema_typename (void)
99 static gsize
namedsema_typesize (void)
101 return sizeof (MonoW32HandleNamedSemaphore
);
105 mono_w32semaphore_init (void)
107 static const MonoW32HandleOps sem_ops
= {
109 sem_handle_signal
, /* signal */
110 sem_handle_own
, /* own */
112 NULL
, /* special_wait */
114 sema_details
, /* details */
115 sema_typename
, /* typename */
116 sema_typesize
, /* typesize */
119 static const MonoW32HandleOps namedsem_ops
= {
121 sem_handle_signal
, /* signal */
122 sem_handle_own
, /* own */
124 NULL
, /* special_wait */
126 namedsema_details
, /* details */
127 namedsema_typename
, /* typename */
128 namedsema_typesize
, /* typesize */
131 mono_w32handle_register_ops (MONO_W32TYPE_SEM
, &sem_ops
);
132 mono_w32handle_register_ops (MONO_W32TYPE_NAMEDSEM
, &namedsem_ops
);
134 mono_w32handle_register_capabilities (MONO_W32TYPE_SEM
,
135 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
136 mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDSEM
,
137 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
141 sem_handle_create (MonoW32HandleSemaphore
*sem_handle
, MonoW32Type type
, gint32 initial
, gint32 max
)
143 MonoW32Handle
*handle_data
;
146 sem_handle
->val
= initial
;
147 sem_handle
->max
= max
;
149 handle
= mono_w32handle_new (type
, sem_handle
);
150 if (handle
== INVALID_HANDLE_VALUE
) {
151 g_warning ("%s: error creating %s handle",
152 __func__
, mono_w32handle_get_typename (type
));
153 mono_w32error_set_last (ERROR_GEN_FAILURE
);
157 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
158 g_error ("%s: unkown handle %p", __func__
, handle
);
160 if (handle_data
->type
!= type
)
161 g_error ("%s: unknown semaphore handle %p", __func__
, handle
);
163 mono_w32handle_lock (handle_data
);
166 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
168 mono_w32handle_unlock (handle_data
);
170 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: created %s handle %p",
171 __func__
, mono_w32handle_get_typename (type
), handle
);
173 mono_w32handle_unref (handle_data
);
179 sem_create (gint32 initial
, gint32 max
)
181 MonoW32HandleSemaphore sem_handle
;
182 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: creating %s handle, initial %d max %d",
183 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_SEM
), initial
, max
);
184 return sem_handle_create (&sem_handle
, MONO_W32TYPE_SEM
, initial
, max
);
188 namedsem_create (gint32 initial
, gint32 max
, const gunichar2
*name
, gint32 name_length
, MonoError
*error
)
192 gpointer handle
= NULL
;
194 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: creating %s handle, initial %d max %d name \"%s\"",
195 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDSEM
), initial
, max
, (const char*)name
);
198 char *utf8_name
= mono_utf16_to_utf8len (name
, name_length
, &utf8_len
, error
);
199 goto_if_nok (error
, exit
);
201 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: Creating named sem name [%s] initial %d max %d", __func__
, utf8_name
, initial
, max
);
203 // Opening named objects does not race.
204 mono_w32handle_namespace_lock ();
206 handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM
, utf8_name
);
207 if (handle
== INVALID_HANDLE_VALUE
) {
208 /* The name has already been used for a different object. */
210 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
212 /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */
213 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
215 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
217 /* A new named semaphore */
218 MonoW32HandleNamedSemaphore namedsem_handle
;
220 // FIXME Silent truncation.
221 size_t len
= utf8_len
< MAX_PATH
? utf8_len
: MAX_PATH
;
222 memcpy (&namedsem_handle
.sharedns
.name
[0], utf8_name
, len
);
223 namedsem_handle
.sharedns
.name
[len
] = '\0';
225 handle
= sem_handle_create ((MonoW32HandleSemaphore
*) &namedsem_handle
, MONO_W32TYPE_NAMEDSEM
, initial
, max
);
228 mono_w32handle_namespace_unlock ();
235 ves_icall_System_Threading_Semaphore_CreateSemaphore_icall (gint32 initialCount
, gint32 maximumCount
,
236 const gunichar2
*name
, gint32 name_length
, gint32
*win32error
, MonoError
*error
)
238 if (maximumCount
<= 0) {
239 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: maximumCount <= 0", __func__
);
241 *win32error
= ERROR_INVALID_PARAMETER
;
245 if (initialCount
> maximumCount
|| initialCount
< 0) {
246 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: initialCount > maximumCount or < 0", __func__
);
247 goto invalid_parameter
;
250 /* Need to blow away any old errors here, because code tests
251 * for ERROR_ALREADY_EXISTS on success (!) to see if a
252 * semaphore was freshly created
254 mono_w32error_set_last (ERROR_SUCCESS
);
256 gpointer sem
= name
? namedsem_create (initialCount
, maximumCount
, name
, name_length
, error
)
257 : sem_create (initialCount
, maximumCount
);
258 *win32error
= mono_w32error_get_last ();
263 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle
, gint32 releaseCount
, gint32
*prevcount
, MonoError
*error
)
265 MonoW32Handle
*handle_data
= NULL
;
266 MonoW32HandleSemaphore
*sem_handle
;
267 MonoBoolean ret
= FALSE
;
269 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
270 g_warning ("%s: unkown handle %p", __func__
, handle
);
272 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
276 if (handle_data
->type
!= MONO_W32TYPE_SEM
&& handle_data
->type
!= MONO_W32TYPE_NAMEDSEM
) {
277 g_warning ("%s: unknown sem handle %p", __func__
, handle
);
281 sem_handle
= (MonoW32HandleSemaphore
*) handle_data
->specific
;
283 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: releasing %s handle %p",
284 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
);
286 mono_w32handle_lock (handle_data
);
288 /* Do this before checking for count overflow, because overflowing
289 * max is a listed technique for finding the current value */
291 *prevcount
= sem_handle
->val
;
293 /* No idea why max is signed, but thats the spec :-( */
294 if (sem_handle
->val
+ releaseCount
> (guint32
)sem_handle
->max
) {
295 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d, max value would be exceeded",
296 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
300 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d",
301 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
303 sem_handle
->val
+= releaseCount
;
304 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
308 mono_w32handle_unlock (handle_data
);
311 mono_w32handle_unref (handle_data
);
316 ves_icall_System_Threading_Semaphore_OpenSemaphore_icall (const gunichar2
*name
, gint32 name_length
,
317 gint32 rights
, gint32
*win32error
, MonoError
*error
)
320 gpointer handle
= NULL
;
322 *win32error
= ERROR_SUCCESS
;
324 char *utf8_name
= mono_utf16_to_utf8 (name
, name_length
, error
);
325 goto_if_nok (error
, exit
);
327 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: Opening named sem [%s]", __func__
, utf8_name
);
329 // Opening named objects does not race.
330 mono_w32handle_namespace_lock ();
332 handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM
, utf8_name
);
334 mono_w32handle_namespace_unlock ();
336 if (handle
== INVALID_HANDLE_VALUE
) {
337 /* The name has already been used for a different object. */
338 *win32error
= ERROR_INVALID_HANDLE
;
340 } else if (!handle
) {
341 /* This name doesn't exist */
342 *win32error
= ERROR_FILE_NOT_FOUND
;
346 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: returning named sem handle %p", __func__
, handle
);
353 MonoW32HandleNamespace
*
354 mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore
*semaphore
)
356 return &semaphore
->sharedns
;