2 * w32semaphore-unix.c: Runtime support for managed Semaphore on Unix
5 * Ludovic Henry (luhenry@microsoft.com)
7 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 #include "w32semaphore.h"
12 #include "w32handle-namespace.h"
13 #include "mono/io-layer/io-layer.h"
14 #include "mono/utils/mono-logger-internals.h"
15 #include "mono/utils/w32handle.h"
20 } MonoW32HandleSemaphore
;
22 struct MonoW32HandleNamedSemaphore
{
23 MonoW32HandleSemaphore s
;
24 MonoW32HandleNamespace sharedns
;
27 static gboolean
sem_handle_own (gpointer handle
, MonoW32HandleType type
, guint32
*statuscode
)
29 MonoW32HandleSemaphore
*sem_handle
;
31 *statuscode
= WAIT_OBJECT_0
;
33 if (!mono_w32handle_lookup (handle
, type
, (gpointer
*)&sem_handle
)) {
34 g_warning ("%s: error looking up %s handle %p",
35 __func__
, mono_w32handle_ops_typename (type
), handle
);
39 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: owning %s handle %p",
40 __func__
, mono_w32handle_ops_typename (type
), handle
);
44 if (sem_handle
->val
== 0)
45 mono_w32handle_set_signal_state (handle
, FALSE
, FALSE
);
50 static void sema_signal(gpointer handle
)
52 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal(handle
, 1, NULL
);
55 static gboolean
sema_own (gpointer handle
, guint32
*statuscode
)
57 return sem_handle_own (handle
, MONO_W32HANDLE_SEM
, statuscode
);
60 static void namedsema_signal (gpointer handle
)
62 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (handle
, 1, NULL
);
65 /* NB, always called with the shared handle lock held */
66 static gboolean
namedsema_own (gpointer handle
, guint32
*statuscode
)
68 return sem_handle_own (handle
, MONO_W32HANDLE_NAMEDSEM
, statuscode
);
71 static void sema_details (gpointer data
)
73 MonoW32HandleSemaphore
*sem
= (MonoW32HandleSemaphore
*)data
;
74 g_print ("val: %5u, max: %5d", sem
->val
, sem
->max
);
77 static void namedsema_details (gpointer data
)
79 MonoW32HandleNamedSemaphore
*namedsem
= (MonoW32HandleNamedSemaphore
*)data
;
80 g_print ("val: %5u, max: %5d, name: \"%s\"", namedsem
->s
.val
, namedsem
->s
.max
, namedsem
->sharedns
.name
);
83 static const gchar
* sema_typename (void)
88 static gsize
sema_typesize (void)
90 return sizeof (MonoW32HandleSemaphore
);
93 static const gchar
* namedsema_typename (void)
98 static gsize
namedsema_typesize (void)
100 return sizeof (MonoW32HandleNamedSemaphore
);
104 mono_w32semaphore_init (void)
106 static MonoW32HandleOps sem_ops
= {
108 sema_signal
, /* signal */
111 NULL
, /* special_wait */
113 sema_details
, /* details */
114 sema_typename
, /* typename */
115 sema_typesize
, /* typesize */
118 static MonoW32HandleOps namedsem_ops
= {
120 namedsema_signal
, /* signal */
121 namedsema_own
, /* own */
123 NULL
, /* special_wait */
125 namedsema_details
, /* details */
126 namedsema_typename
, /* typename */
127 namedsema_typesize
, /* typesize */
130 mono_w32handle_register_ops (MONO_W32HANDLE_SEM
, &sem_ops
);
131 mono_w32handle_register_ops (MONO_W32HANDLE_NAMEDSEM
, &namedsem_ops
);
133 mono_w32handle_register_capabilities (MONO_W32HANDLE_SEM
,
134 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
135 mono_w32handle_register_capabilities (MONO_W32HANDLE_NAMEDSEM
,
136 (MonoW32HandleCapability
)(MONO_W32HANDLE_CAP_WAIT
| MONO_W32HANDLE_CAP_SIGNAL
));
140 sem_handle_create (MonoW32HandleSemaphore
*sem_handle
, MonoW32HandleType type
, gint32 initial
, gint32 max
)
145 sem_handle
->val
= initial
;
146 sem_handle
->max
= max
;
148 handle
= mono_w32handle_new (type
, sem_handle
);
149 if (handle
== INVALID_HANDLE_VALUE
) {
150 g_warning ("%s: error creating %s handle",
151 __func__
, mono_w32handle_ops_typename (type
));
152 SetLastError (ERROR_GEN_FAILURE
);
156 thr_ret
= mono_w32handle_lock_handle (handle
);
157 g_assert (thr_ret
== 0);
160 mono_w32handle_set_signal_state (handle
, TRUE
, FALSE
);
162 thr_ret
= mono_w32handle_unlock_handle (handle
);
163 g_assert (thr_ret
== 0);
165 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: created %s handle %p",
166 __func__
, mono_w32handle_ops_typename (type
), handle
);
172 sem_create (gint32 initial
, gint32 max
)
174 MonoW32HandleSemaphore sem_handle
;
175 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: creating %s handle, initial %d max %d",
176 __func__
, mono_w32handle_ops_typename (MONO_W32HANDLE_SEM
), initial
, max
);
177 return sem_handle_create (&sem_handle
, MONO_W32HANDLE_SEM
, initial
, max
);
181 namedsem_create (gint32 initial
, gint32 max
, const gunichar2
*name
)
186 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: creating %s handle, initial %d max %d name \"%s\"",
187 __func__
, mono_w32handle_ops_typename (MONO_W32HANDLE_NAMEDSEM
), initial
, max
, (const char*)name
);
189 /* w32 seems to guarantee that opening named objects can't race each other */
190 mono_w32handle_namespace_lock ();
192 utf8_name
= g_utf16_to_utf8 (name
, -1, NULL
, NULL
, NULL
);
194 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
);
196 handle
= mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDSEM
, utf8_name
);
197 if (handle
== INVALID_HANDLE_VALUE
) {
198 /* The name has already been used for a different object. */
200 SetLastError (ERROR_INVALID_HANDLE
);
202 /* Not an error, but this is how the caller is informed that the semaphore wasn't freshly created */
203 SetLastError (ERROR_ALREADY_EXISTS
);
205 /* mono_w32handle_namespace_search_handle already adds a ref to the handle */
207 /* A new named semaphore */
208 MonoW32HandleNamedSemaphore namedsem_handle
;
210 strncpy (&namedsem_handle
.sharedns
.name
[0], utf8_name
, MAX_PATH
);
211 namedsem_handle
.sharedns
.name
[MAX_PATH
] = '\0';
213 handle
= sem_handle_create ((MonoW32HandleSemaphore
*) &namedsem_handle
, MONO_W32HANDLE_NAMEDSEM
, initial
, max
);
218 mono_w32handle_namespace_unlock ();
224 ves_icall_System_Threading_Semaphore_CreateSemaphore_internal (gint32 initialCount
, gint32 maximumCount
, MonoString
*name
, gint32
*error
)
228 if (maximumCount
<= 0) {
229 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: maximumCount <= 0", __func__
);
231 *error
= ERROR_INVALID_PARAMETER
;
235 if (initialCount
> maximumCount
|| initialCount
< 0) {
236 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: initialCount > maximumCount or < 0", __func__
);
238 *error
= ERROR_INVALID_PARAMETER
;
242 /* Need to blow away any old errors here, because code tests
243 * for ERROR_ALREADY_EXISTS on success (!) to see if a
244 * semaphore was freshly created
246 SetLastError (ERROR_SUCCESS
);
249 sem
= sem_create (initialCount
, maximumCount
);
251 sem
= namedsem_create (initialCount
, maximumCount
, mono_string_chars (name
));
253 *error
= GetLastError ();
259 ves_icall_System_Threading_Semaphore_ReleaseSemaphore_internal (gpointer handle
, gint32 releaseCount
, gint32
*prevcount
)
261 MonoW32HandleType type
;
262 MonoW32HandleSemaphore
*sem_handle
;
267 SetLastError (ERROR_INVALID_HANDLE
);
271 switch (type
= mono_w32handle_get_type (handle
)) {
272 case MONO_W32HANDLE_SEM
:
273 case MONO_W32HANDLE_NAMEDSEM
:
276 SetLastError (ERROR_INVALID_HANDLE
);
280 if (!mono_w32handle_lookup (handle
, type
, (gpointer
*)&sem_handle
)) {
281 g_warning ("%s: error looking up sem handle %p", __func__
, handle
);
285 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: releasing %s handle %p",
286 __func__
, mono_w32handle_ops_typename (type
), handle
);
288 thr_ret
= mono_w32handle_lock_handle (handle
);
289 g_assert (thr_ret
== 0);
291 /* Do this before checking for count overflow, because overflowing
292 * max is a listed technique for finding the current value */
294 *prevcount
= sem_handle
->val
;
296 /* No idea why max is signed, but thats the spec :-( */
297 if (sem_handle
->val
+ releaseCount
> (guint32
)sem_handle
->max
) {
298 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",
299 __func__
, mono_w32handle_ops_typename (type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
303 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: %s handle %p val %d count %d max %d",
304 __func__
, mono_w32handle_ops_typename (type
), handle
, sem_handle
->val
, releaseCount
, sem_handle
->max
);
306 sem_handle
->val
+= releaseCount
;
307 mono_w32handle_set_signal_state (handle
, TRUE
, TRUE
);
312 thr_ret
= mono_w32handle_unlock_handle (handle
);
313 g_assert (thr_ret
== 0);
319 ves_icall_System_Threading_Semaphore_OpenSemaphore_internal (MonoString
*name
, gint32 rights
, gint32
*error
)
324 *error
= ERROR_SUCCESS
;
326 /* w32 seems to guarantee that opening named objects can't race each other */
327 mono_w32handle_namespace_lock ();
329 utf8_name
= g_utf16_to_utf8 (mono_string_chars (name
), -1, NULL
, NULL
, NULL
);
331 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: Opening named sem [%s]", __func__
, utf8_name
);
333 handle
= mono_w32handle_namespace_search_handle (MONO_W32HANDLE_NAMEDSEM
, utf8_name
);
334 if (handle
== INVALID_HANDLE_VALUE
) {
335 /* The name has already been used for a different object. */
336 *error
= ERROR_INVALID_HANDLE
;
338 } else if (!handle
) {
339 /* This name doesn't exist */
340 *error
= ERROR_FILE_NOT_FOUND
;
344 mono_trace (G_LOG_LEVEL_DEBUG
, MONO_TRACE_IO_LAYER
, "%s: returning named sem handle %p", __func__
, handle
);
349 mono_w32handle_namespace_unlock ();
354 MonoW32HandleNamespace
*
355 mono_w32semaphore_get_namespace (MonoW32HandleNamedSemaphore
*semaphore
)
357 return &semaphore
->sharedns
;