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 gint32
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
);
44 return MONO_W32HANDLE_WAIT_RET_TOO_MANY_POSTS
;
46 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d",
47 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
, sem_handle
->val
, 1, sem_handle
->max
);
50 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
52 return MONO_W32HANDLE_WAIT_RET_SUCCESS_0
;
55 static gboolean
sem_handle_own (MonoW32Handle
*handle_data
, gboolean
*abandoned
)
57 MonoW32HandleSemaphore
*sem_handle
;
61 sem_handle
= (MonoW32HandleSemaphore
*) handle_data
->specific
;
63 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: owning %s handle %p",
64 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle_data
);
68 if (sem_handle
->val
== 0)
69 mono_w32handle_set_signal_state (handle_data
, FALSE
, FALSE
);
74 static void sema_details (MonoW32Handle
*handle_data
)
76 MonoW32HandleSemaphore
*sem
= (MonoW32HandleSemaphore
*)handle_data
->specific
;
77 g_print ("val: %5u, max: %5d", sem
->val
, sem
->max
);
80 static void namedsema_details (MonoW32Handle
*handle_data
)
82 MonoW32HandleNamedSemaphore
*namedsem
= (MonoW32HandleNamedSemaphore
*)handle_data
->specific
;
83 g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem
->s
.val
, namedsem
->s
.max
, namedsem
->sharedns
.name
);
86 static const gchar
* sema_typename (void)
91 static gsize
sema_typesize (void)
93 return sizeof (MonoW32HandleSemaphore
);
96 static const gchar
* namedsema_typename (void)
101 static gsize
namedsema_typesize (void)
103 return sizeof (MonoW32HandleNamedSemaphore
);
107 mono_w32semaphore_init (void)
109 static const MonoW32HandleOps sem_ops
= {
111 sem_handle_signal
, /* signal */
112 sem_handle_own
, /* own */
114 NULL
, /* special_wait */
116 sema_details
, /* details */
117 sema_typename
, /* typename */
118 sema_typesize
, /* typesize */
121 static const MonoW32HandleOps namedsem_ops
= {
123 sem_handle_signal
, /* signal */
124 sem_handle_own
, /* own */
126 NULL
, /* special_wait */
128 namedsema_details
, /* details */
129 namedsema_typename
, /* typename */
130 namedsema_typesize
, /* typesize */
133 mono_w32handle_register_ops (MONO_W32TYPE_SEM
, &sem_ops
);
134 mono_w32handle_register_ops (MONO_W32TYPE_NAMEDSEM
, &namedsem_ops
);
136 mono_w32handle_register_capabilities (MONO_W32TYPE_SEM
,
137 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
138 mono_w32handle_register_capabilities (MONO_W32TYPE_NAMEDSEM
,
139 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
143 sem_handle_create (MonoW32HandleSemaphore
*sem_handle
, MonoW32Type type
, gint32 initial
, gint32 max
)
145 MonoW32Handle
*handle_data
;
148 sem_handle
->val
= initial
;
149 sem_handle
->max
= max
;
151 handle
= mono_w32handle_new (type
, sem_handle
);
152 if (handle
== INVALID_HANDLE_VALUE
) {
153 g_warning ("%s: error creating %s handle",
154 __func__
, mono_w32handle_get_typename (type
));
155 mono_w32error_set_last (ERROR_GEN_FAILURE
);
159 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
))
160 g_error ("%s: unkown handle %p", __func__
, handle
);
162 if (handle_data
->type
!= type
)
163 g_error ("%s: unknown semaphore handle %p", __func__
, handle
);
165 mono_w32handle_lock (handle_data
);
168 mono_w32handle_set_signal_state (handle_data
, TRUE
, FALSE
);
170 mono_w32handle_unlock (handle_data
);
172 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: created %s handle %p",
173 __func__
, mono_w32handle_get_typename (type
), handle
);
175 mono_w32handle_unref (handle_data
);
181 sem_create (gint32 initial
, gint32 max
)
183 MonoW32HandleSemaphore sem_handle
;
184 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: creating %s handle, initial %d max %d",
185 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_SEM
), initial
, max
);
186 return sem_handle_create (&sem_handle
, MONO_W32TYPE_SEM
, initial
, max
);
190 namedsem_create (gint32 initial
, gint32 max
, const gunichar2
*name
, gint32 name_length
, MonoError
*error
)
194 gpointer handle
= NULL
;
196 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: creating %s handle, initial %d max %d name \"%s\"",
197 __func__
, mono_w32handle_get_typename (MONO_W32TYPE_NAMEDSEM
), initial
, max
, (const char*)name
);
200 char *utf8_name
= mono_utf16_to_utf8len (name
, name_length
, &utf8_len
, error
);
201 goto_if_nok (error
, exit
);
203 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
);
205 // Opening named objects does not race.
206 mono_w32handle_namespace_lock ();
208 handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM
, utf8_name
);
209 if (handle
== INVALID_HANDLE_VALUE
) {
210 /* The name has already been used for a different object. */
212 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
214 /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */
215 mono_w32error_set_last (ERROR_ALREADY_EXISTS
);
217 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
219 /* A new named semaphore */
220 MonoW32HandleNamedSemaphore namedsem_handle
;
222 // FIXME Silent truncation.
223 size_t len
= utf8_len
< MAX_PATH
? utf8_len
: MAX_PATH
;
224 memcpy (&namedsem_handle
.sharedns
.name
[0], utf8_name
, len
);
225 namedsem_handle
.sharedns
.name
[len
] = '\0';
227 handle
= sem_handle_create ((MonoW32HandleSemaphore
*) &namedsem_handle
, MONO_W32TYPE_NAMEDSEM
, initial
, max
);
230 mono_w32handle_namespace_unlock ();
236 // These functions appear to be using coop-aware locking functions, and so this file does not include explicit
237 // GC-safe transitions like its corresponding Windows version
240 ves_icall_System_Threading_Semaphore_CreateSemaphore_icall (gint32 initialCount
, gint32 maximumCount
,
241 const gunichar2
*name
, gint32 name_length
, gint32
*win32error
, MonoError
*error
)
243 if (maximumCount
<= 0) {
244 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: maximumCount <= 0", __func__
);
246 *win32error
= ERROR_INVALID_PARAMETER
;
250 if (initialCount
> maximumCount
|| initialCount
< 0) {
251 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: initialCount > maximumCount or < 0", __func__
);
252 goto invalid_parameter
;
255 /* Need to blow away any old errors here, because code tests
256 * for ERROR_ALREADY_EXISTS on success (!) to see if a
257 * semaphore was freshly created
259 mono_w32error_set_last (ERROR_SUCCESS
);
261 gpointer sem
= name
? namedsem_create (initialCount
, maximumCount
, name
, name_length
, error
)
262 : sem_create (initialCount
, maximumCount
);
263 *win32error
= mono_w32error_get_last ();
268 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle
, gint32 releaseCount
, gint32
*prevcount
, MonoError
*error
)
270 MonoW32Handle
*handle_data
= NULL
;
271 MonoW32HandleSemaphore
*sem_handle
;
272 MonoBoolean ret
= FALSE
;
274 if (!mono_w32handle_lookup_and_ref (handle
, &handle_data
)) {
275 g_warning ("%s: unkown handle %p", __func__
, handle
);
277 mono_w32error_set_last (ERROR_INVALID_HANDLE
);
281 if (handle_data
->type
!= MONO_W32TYPE_SEM
&& handle_data
->type
!= MONO_W32TYPE_NAMEDSEM
) {
282 g_warning ("%s: unknown sem handle %p", __func__
, handle
);
286 sem_handle
= (MonoW32HandleSemaphore
*) handle_data
->specific
;
288 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: releasing %s handle %p",
289 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
);
291 mono_w32handle_lock (handle_data
);
293 /* Do this before checking for count overflow, because overflowing
294 * max is a listed technique for finding the current value */
296 *prevcount
= sem_handle
->val
;
298 /* No idea why max is signed, but thats the spec :-( */
299 if (sem_handle
->val
+ releaseCount
> (guint32
)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, max value would be exceeded",
301 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
305 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: %s handle %p val %d count %d max %d",
306 __func__
, mono_w32handle_get_typename (handle_data
->type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
308 sem_handle
->val
+= releaseCount
;
309 mono_w32handle_set_signal_state (handle_data
, TRUE
, TRUE
);
313 mono_w32handle_unlock (handle_data
);
316 mono_w32handle_unref (handle_data
);
321 ves_icall_System_Threading_Semaphore_OpenSemaphore_icall (const gunichar2
*name
, gint32 name_length
,
322 gint32 rights
, gint32
*win32error
, MonoError
*error
)
325 gpointer handle
= NULL
;
327 *win32error
= ERROR_SUCCESS
;
329 char *utf8_name
= mono_utf16_to_utf8 (name
, name_length
, error
);
330 goto_if_nok (error
, exit
);
332 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: Opening named sem [%s]", __func__
, utf8_name
);
334 // Opening named objects does not race.
335 mono_w32handle_namespace_lock ();
337 handle
= mono_w32handle_namespace_search_handle (MONO_W32TYPE_NAMEDSEM
, utf8_name
);
339 mono_w32handle_namespace_unlock ();
341 if (handle
== INVALID_HANDLE_VALUE
) {
342 /* The name has already been used for a different object. */
343 *win32error
= ERROR_INVALID_HANDLE
;
345 } else if (!handle
) {
346 /* This name doesn't exist */
347 *win32error
= ERROR_FILE_NOT_FOUND
;
351 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER_SEMAPHORE
, "%s: returning named sem handle %p", __func__
, handle
);
358 MonoW32HandleNamespace
*
359 mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore
*semaphore
)
361 return &semaphore
->sharedns
;