1 /**
2 * \file
3 * Generic and internal operations on handles
4 *
5 * Author:
6 * Dick Porter (dick@ximian.com)
7 * Ludovic Henry (luhenry@microsoft.com)
8 *
9 * (C) 2002-2011 Novell, Inc.
10 * Copyright 2011 Xamarin Inc
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 */
13
14 #include <config.h>
15 #include <glib.h>
16
17 #include "w32handle.h"
18
19 #include "utils/atomic.h"
20 #include "utils/mono-logger-internals.h"
21 #include "utils/mono-proclib.h"
22 #include "utils/mono-threads.h"
23 #include "utils/mono-time.h"
24
25 #undef DEBUG_REFS
26
27 #define SLOT_MAX (1024 * 32)
28
29 /* must be a power of 2 */
30 #define HANDLE_PER_SLOT (256)
31
32 static MonoW32HandleCapability handle_caps [MONO_W32TYPE_COUNT];
33 static MonoW32HandleOps *handle_ops [MONO_W32TYPE_COUNT];
34
35 /*
36 * We can hold SLOT_MAX * HANDLE_PER_SLOT handles.
37 * If 4M handles are not enough... Oh, well... we will crash.
38 */
39 #define SLOT_INDEX(x) (x / HANDLE_PER_SLOT)
40 #define SLOT_OFFSET(x) (x % HANDLE_PER_SLOT)
41
42 static MonoW32Handle **private_handles;
43 static guint32 private_handles_size = 0;
44
45 /*
46 * This is an internal handle which is used for handling waiting for multiple handles.
47 * Threads which wait for multiple handles wait on this one handle, and when a handle
48 * is signalled, this handle is signalled too.
49 */
50 static MonoCoopMutex global_signal_mutex;
51 static MonoCoopCond global_signal_cond;
52
53 static MonoCoopMutex scan_mutex;
54
55 static gboolean shutting_down = FALSE;
56
57 static const gchar*
58 mono_w32handle_ops_typename (MonoW32Type type);
59
60 const gchar*
mono_w32handle_get_typename(MonoW32Type type)61 mono_w32handle_get_typename (MonoW32Type type)
62 {
63 return mono_w32handle_ops_typename (type);
64 }
65
66 void
mono_w32handle_set_signal_state(MonoW32Handle * handle_data,gboolean state,gboolean broadcast)67 mono_w32handle_set_signal_state (MonoW32Handle *handle_data, gboolean state, gboolean broadcast)
68 {
69 #ifdef DEBUG
70 g_message ("%s: setting state of %p to %s (broadcast %s)", __func__,
71 handle, state?"TRUE":"FALSE", broadcast?"TRUE":"FALSE");
72 #endif
73
74 if (state) {
75 /* Tell everyone blocking on a single handle */
76
77 /* The condition the global signal cond is waiting on is the signalling of
78 * _any_ handle. So lock it before setting the signalled state.
79 */
80 mono_coop_mutex_lock (&global_signal_mutex);
81
82 /* This function _must_ be called with
83 * handle->signal_mutex locked
84 */
85 handle_data->signalled = TRUE;
86
87 if (broadcast)
88 mono_coop_cond_broadcast (&handle_data->signal_cond);
89 else
90 mono_coop_cond_signal (&handle_data->signal_cond);
91
92 /* Tell everyone blocking on multiple handles that something
93 * was signalled
94 */
95 mono_coop_cond_broadcast (&global_signal_cond);
96
97 mono_coop_mutex_unlock (&global_signal_mutex);
98 } else {
99 handle_data->signalled = FALSE;
100 }
101 }
102
103 gboolean
mono_w32handle_issignalled(MonoW32Handle * handle_data)104 mono_w32handle_issignalled (MonoW32Handle *handle_data)
105 {
106 return handle_data->signalled;
107 }
108
109 static void
mono_w32handle_set_in_use(MonoW32Handle * handle_data,gboolean in_use)110 mono_w32handle_set_in_use (MonoW32Handle *handle_data, gboolean in_use)
111 {
112 handle_data->in_use = in_use;
113 }
114
115 static void
mono_w32handle_lock_signal_mutex(void)116 mono_w32handle_lock_signal_mutex (void)
117 {
118 #ifdef DEBUG
119 g_message ("%s: lock global signal mutex", __func__);
120 #endif
121
122 mono_coop_mutex_lock (&global_signal_mutex);
123 }
124
125 static void
mono_w32handle_unlock_signal_mutex(void)126 mono_w32handle_unlock_signal_mutex (void)
127 {
128 #ifdef DEBUG
129 g_message ("%s: unlock global signal mutex", __func__);
130 #endif
131
132 mono_coop_mutex_unlock (&global_signal_mutex);
133 }
134
135 void
mono_w32handle_lock(MonoW32Handle * handle_data)136 mono_w32handle_lock (MonoW32Handle *handle_data)
137 {
138 mono_coop_mutex_lock (&handle_data->signal_mutex);
139 }
140
141 gboolean
mono_w32handle_trylock(MonoW32Handle * handle_data)142 mono_w32handle_trylock (MonoW32Handle *handle_data)
143 {
144 return mono_coop_mutex_trylock (&handle_data->signal_mutex) == 0;
145 }
146
147 void
mono_w32handle_unlock(MonoW32Handle * handle_data)148 mono_w32handle_unlock (MonoW32Handle *handle_data)
149 {
150 mono_coop_mutex_unlock (&handle_data->signal_mutex);
151 }
152
153 void
mono_w32handle_init(void)154 mono_w32handle_init (void)
155 {
156 static gboolean initialized = FALSE;
157
158 if (initialized)
159 return;
160
161 mono_coop_mutex_init (&scan_mutex);
162
163 mono_coop_cond_init (&global_signal_cond);
164 mono_coop_mutex_init (&global_signal_mutex);
165
166 private_handles = g_new0 (MonoW32Handle*, SLOT_MAX);
167
168 initialized = TRUE;
169 }
170
171 void
mono_w32handle_cleanup(void)172 mono_w32handle_cleanup (void)
173 {
174 int i;
175
176 g_assert (!shutting_down);
177 shutting_down = TRUE;
178
179 for (i = 0; i < SLOT_MAX; ++i)
180 g_free (private_handles [i]);
181
182 g_free (private_handles);
183 }
184
185 static gsize
186 mono_w32handle_ops_typesize (MonoW32Type type);
187
188 /*
189 * mono_w32handle_new_internal:
190 * @type: Init handle to this type
191 *
192 * Search for a free handle and initialize it. Return the handle on
193 * success and 0 on failure. This is only called from
194 * mono_w32handle_new, and scan_mutex must be held.
195 */
196 static MonoW32Handle*
mono_w32handle_new_internal(MonoW32Type type,gpointer handle_specific)197 mono_w32handle_new_internal (MonoW32Type type, gpointer handle_specific)
198 {
199 guint32 i, k, count;
200 static guint32 last = 0;
201 gboolean retry = FALSE;
202
203 /* A linear scan should be fast enough. Start from the last
204 * allocation, assuming that handles are allocated more often
205 * than they're freed. Leave the space reserved for file
206 * descriptors
207 */
208
209 if (last == 0) {
210 /* We need to go from 1 since a handle of value 0 can be considered invalid in managed code */
211 last = 1;
212 } else {
213 retry = TRUE;
214 }
215
216 again:
217 count = last;
218 for(i = SLOT_INDEX (count); i < private_handles_size; i++) {
219 if (private_handles [i]) {
220 for (k = SLOT_OFFSET (count); k < HANDLE_PER_SLOT; k++) {
221 MonoW32Handle *handle_data = &private_handles [i][k];
222
223 if (handle_data->type == MONO_W32TYPE_UNUSED) {
224 last = count + 1;
225
226 g_assert (handle_data->ref == 0);
227
228 handle_data->type = type;
229 handle_data->signalled = FALSE;
230 handle_data->ref = 1;
231
232 mono_coop_cond_init (&handle_data->signal_cond);
233 mono_coop_mutex_init (&handle_data->signal_mutex);
234
235 if (handle_specific)
236 handle_data->specific = g_memdup (handle_specific, mono_w32handle_ops_typesize (type));
237
238 return handle_data;
239 }
240 count++;
241 }
242 }
243 }
244
245 if (retry) {
246 /* Try again from the beginning */
247 last = 1;
248 retry = FALSE;
249 goto again;
250 }
251
252 /* Will need to expand the array. The caller will sort it out */
253
254 return GINT_TO_POINTER (-1);
255 }
256
257 gpointer
mono_w32handle_new(MonoW32Type type,gpointer handle_specific)258 mono_w32handle_new (MonoW32Type type, gpointer handle_specific)
259 {
260 MonoW32Handle *handle_data;
261
262 g_assert (!shutting_down);
263
264 mono_coop_mutex_lock (&scan_mutex);
265
266 while ((handle_data = mono_w32handle_new_internal (type, handle_specific)) == GINT_TO_POINTER (-1)) {
267 /* Try and expand the array, and have another go */
268 if (private_handles_size >= SLOT_MAX) {
269 mono_coop_mutex_unlock (&scan_mutex);
270
271 /* We ran out of slots */
272 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: failed to create %s handle", __func__, mono_w32handle_ops_typename (type));
273 return INVALID_HANDLE_VALUE;
274 }
275
276 private_handles [private_handles_size ++] = g_new0 (MonoW32Handle, HANDLE_PER_SLOT);
277 }
278
279 mono_coop_mutex_unlock (&scan_mutex);
280
281 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: create %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data);
282
283 return (gpointer) handle_data;
284 }
285
286 static gboolean
287 mono_w32handle_ref_core (MonoW32Handle *handle_data);
288
289 static gboolean
290 mono_w32handle_unref_core (MonoW32Handle *handle_data);
291
292 static void
293 w32handle_destroy (MonoW32Handle *handle_data);
294
295 gpointer
mono_w32handle_duplicate(MonoW32Handle * handle_data)296 mono_w32handle_duplicate (MonoW32Handle *handle_data)
297 {
298 if (!mono_w32handle_ref_core (handle_data))
299 g_error ("%s: unknown handle %p", __func__, handle_data);
300
301 return (gpointer) handle_data;
302 }
303
304 gboolean
mono_w32handle_close(gpointer handle)305 mono_w32handle_close (gpointer handle)
306 {
307 MonoW32Handle *handle_data;
308 gboolean destroy;
309
310 if (handle == INVALID_HANDLE_VALUE)
311 return FALSE;
312
313 handle_data = (MonoW32Handle*) handle;
314
315 if (handle_data->type == MONO_W32TYPE_UNUSED)
316 return FALSE;
317
318 destroy = mono_w32handle_unref_core (handle_data);
319 if (destroy)
320 w32handle_destroy (handle_data);
321
322 return TRUE;
323 }
324
325 gboolean
mono_w32handle_lookup_and_ref(gpointer handle,MonoW32Handle ** handle_data)326 mono_w32handle_lookup_and_ref (gpointer handle, MonoW32Handle **handle_data)
327 {
328 g_assert (handle_data);
329
330 if (handle == INVALID_HANDLE_VALUE)
331 return FALSE;
332
333 *handle_data = (MonoW32Handle*) handle;
334
335 if (!mono_w32handle_ref_core (*handle_data))
336 return FALSE;
337
338 if ((*handle_data)->type == MONO_W32TYPE_UNUSED) {
339 mono_w32handle_unref_core (*handle_data);
340 return FALSE;
341 }
342
343 return TRUE;
344 }
345
346 void
mono_w32handle_foreach(gboolean (* on_each)(MonoW32Handle * handle_data,gpointer user_data),gpointer user_data)347 mono_w32handle_foreach (gboolean (*on_each)(MonoW32Handle *handle_data, gpointer user_data), gpointer user_data)
348 {
349 GPtrArray *handles_to_destroy;
350 guint32 i, k;
351
352 handles_to_destroy = NULL;
353
354 mono_coop_mutex_lock (&scan_mutex);
355
356 for (i = SLOT_INDEX (0); i < private_handles_size; i++) {
357 if (!private_handles [i])
358 continue;
359 for (k = SLOT_OFFSET (0); k < HANDLE_PER_SLOT; k++) {
360 MonoW32Handle *handle_data;
361 gboolean destroy, finished;
362
363 handle_data = &private_handles [i][k];
364 if (handle_data->type == MONO_W32TYPE_UNUSED)
365 continue;
366
367 if (!mono_w32handle_ref_core (handle_data)) {
368 /* we are racing with mono_w32handle_unref:
369 * the handle ref has been decremented, but it
370 * hasn't yet been destroyed. */
371 continue;
372 }
373
374 finished = on_each (handle_data, user_data);
375
376 /* we might have to destroy the handle here, as
377 * it could have been unrefed in another thread */
378 destroy = mono_w32handle_unref_core (handle_data);
379 if (destroy) {
380 /* we do not destroy it while holding the scan_mutex
381 * lock, because w32handle_destroy also needs to take
382 * the lock, and it calls user code which might lead
383 * to a deadlock */
384 if (!handles_to_destroy)
385 handles_to_destroy = g_ptr_array_sized_new (4);
386 g_ptr_array_add (handles_to_destroy, (gpointer) handle_data);
387 }
388
389 if (finished)
390 goto done;
391 }
392 }
393
394 done:
395 mono_coop_mutex_unlock (&scan_mutex);
396
397 if (handles_to_destroy) {
398 for (i = 0; i < handles_to_destroy->len; ++i)
399 w32handle_destroy ((MonoW32Handle*) handles_to_destroy->pdata [i]);
400
401 g_ptr_array_free (handles_to_destroy, TRUE);
402 }
403 }
404
405 static gboolean
mono_w32handle_ref_core(MonoW32Handle * handle_data)406 mono_w32handle_ref_core (MonoW32Handle *handle_data)
407 {
408 guint old, new;
409
410 do {
411 old = handle_data->ref;
412 if (old == 0)
413 return FALSE;
414
415 new = old + 1;
416 } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old);
417
418 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: ref %s handle %p, ref: %d -> %d",
419 __func__, mono_w32handle_ops_typename (handle_data->type), handle_data, old, new);
420
421 return TRUE;
422 }
423
424 static gboolean
mono_w32handle_unref_core(MonoW32Handle * handle_data)425 mono_w32handle_unref_core (MonoW32Handle *handle_data)
426 {
427 MonoW32Type type;
428 guint old, new;
429
430 type = handle_data->type;
431
432 do {
433 old = handle_data->ref;
434 if (!(old >= 1))
435 g_error ("%s: handle %p has ref %d, it should be >= 1", __func__, handle_data, old);
436
437 new = old - 1;
438 } while (mono_atomic_cas_i32 ((gint32*) &handle_data->ref, (gint32)new, (gint32)old) != (gint32)old);
439
440 /* handle_data might contain invalid data from now on, if
441 * another thread is unref'ing this handle at the same time */
442
443 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: unref %s handle %p, ref: %d -> %d destroy: %s",
444 __func__, mono_w32handle_ops_typename (type), handle_data, old, new, new == 0 ? "true" : "false");
445
446 return new == 0;
447 }
448
449 static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer);
450
451 static void
w32handle_destroy(MonoW32Handle * handle_data)452 w32handle_destroy (MonoW32Handle *handle_data)
453 {
454 /* Need to copy the handle info, reset the slot in the
455 * array, and _only then_ call the close function to
456 * avoid race conditions (eg file descriptors being
457 * closed, and another file being opened getting the
458 * same fd racing the memset())
459 */
460 MonoW32Type type;
461 gpointer handle_specific;
462 void (*close_func)(gpointer, gpointer);
463
464 g_assert (!handle_data->in_use);
465
466 type = handle_data->type;
467 handle_specific = handle_data->specific;
468
469 mono_coop_mutex_lock (&scan_mutex);
470
471 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: destroy %s handle %p", __func__, mono_w32handle_ops_typename (type), handle_data);
472
473 mono_coop_mutex_destroy (&handle_data->signal_mutex);
474 mono_coop_cond_destroy (&handle_data->signal_cond);
475
476 memset (handle_data, 0, sizeof (MonoW32Handle));
477
478 mono_coop_mutex_unlock (&scan_mutex);
479
480 close_func = _wapi_handle_ops_get_close_func (type);
481 if (close_func != NULL) {
482 close_func (handle_data, handle_specific);
483 }
484
485 memset (handle_specific, 0, mono_w32handle_ops_typesize (type));
486
487 g_free (handle_specific);
488 }
489
490 /* The handle must not be locked on entry to this function */
491 void
mono_w32handle_unref(MonoW32Handle * handle_data)492 mono_w32handle_unref (MonoW32Handle *handle_data)
493 {
494 gboolean destroy;
495
496 destroy = mono_w32handle_unref_core (handle_data);
497 if (destroy)
498 w32handle_destroy (handle_data);
499 }
500
501 void
mono_w32handle_register_ops(MonoW32Type type,MonoW32HandleOps * ops)502 mono_w32handle_register_ops (MonoW32Type type, MonoW32HandleOps *ops)
503 {
504 handle_ops [type] = ops;
505 }
506
507 void
mono_w32handle_register_capabilities(MonoW32Type type,MonoW32HandleCapability caps)508 mono_w32handle_register_capabilities (MonoW32Type type, MonoW32HandleCapability caps)
509 {
510 handle_caps[type] = caps;
511 }
512
513 static gboolean
mono_w32handle_test_capabilities(MonoW32Handle * handle_data,MonoW32HandleCapability caps)514 mono_w32handle_test_capabilities (MonoW32Handle *handle_data, MonoW32HandleCapability caps)
515 {
516 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: testing 0x%x against 0x%x (%d)", __func__,
517 handle_caps[handle_data->type], caps, handle_caps[handle_data->type] & caps);
518
519 return (handle_caps [handle_data->type] & caps) != 0;
520 }
521
_wapi_handle_ops_get_close_func(MonoW32Type type)522 static void (*_wapi_handle_ops_get_close_func (MonoW32Type type))(gpointer, gpointer)
523 {
524 if (handle_ops[type] != NULL &&
525 handle_ops[type]->close != NULL) {
526 return (handle_ops[type]->close);
527 }
528
529 return (NULL);
530 }
531
532 static void
mono_w32handle_ops_details(MonoW32Handle * handle_data)533 mono_w32handle_ops_details (MonoW32Handle *handle_data)
534 {
535 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->details != NULL)
536 handle_ops [handle_data->type]->details (handle_data);
537 }
538
539 static const gchar*
mono_w32handle_ops_typename(MonoW32Type type)540 mono_w32handle_ops_typename (MonoW32Type type)
541 {
542 g_assert (handle_ops [type]);
543 g_assert (handle_ops [type]->typename);
544 return handle_ops [type]->typename ();
545 }
546
547 static gsize
mono_w32handle_ops_typesize(MonoW32Type type)548 mono_w32handle_ops_typesize (MonoW32Type type)
549 {
550 g_assert (handle_ops [type]);
551 g_assert (handle_ops [type]->typesize);
552 return handle_ops [type]->typesize ();
553 }
554
555 static void
mono_w32handle_ops_signal(MonoW32Handle * handle_data)556 mono_w32handle_ops_signal (MonoW32Handle *handle_data)
557 {
558 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->signal)
559 handle_ops [handle_data->type]->signal (handle_data);
560 }
561
562 static gboolean
mono_w32handle_ops_own(MonoW32Handle * handle_data,gboolean * abandoned)563 mono_w32handle_ops_own (MonoW32Handle *handle_data, gboolean *abandoned)
564 {
565 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->own_handle)
566 return handle_ops [handle_data->type]->own_handle (handle_data, abandoned);
567
568 return FALSE;
569 }
570
571 static gboolean
mono_w32handle_ops_isowned(MonoW32Handle * handle_data)572 mono_w32handle_ops_isowned (MonoW32Handle *handle_data)
573 {
574 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->is_owned)
575 return handle_ops [handle_data->type]->is_owned (handle_data);
576
577 return FALSE;
578 }
579
580 static MonoW32HandleWaitRet
mono_w32handle_ops_specialwait(MonoW32Handle * handle_data,guint32 timeout,gboolean * alerted)581 mono_w32handle_ops_specialwait (MonoW32Handle *handle_data, guint32 timeout, gboolean *alerted)
582 {
583 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->special_wait)
584 return handle_ops [handle_data->type]->special_wait (handle_data, timeout, alerted);
585
586 return MONO_W32HANDLE_WAIT_RET_FAILED;
587 }
588
589 static void
mono_w32handle_ops_prewait(MonoW32Handle * handle_data)590 mono_w32handle_ops_prewait (MonoW32Handle *handle_data)
591 {
592 if (handle_ops [handle_data->type] && handle_ops [handle_data->type]->prewait)
593 handle_ops [handle_data->type]->prewait (handle_data);
594 }
595
596 static void
mono_w32handle_lock_handles(MonoW32Handle ** handles_data,gsize nhandles)597 mono_w32handle_lock_handles (MonoW32Handle **handles_data, gsize nhandles)
598 {
599 gint i, j, iter = 0;
600 #ifndef HOST_WIN32
601 struct timespec sleepytime;
602 #endif
603
604 /* Lock all the handles, with backoff */
605 again:
606 for (i = 0; i < nhandles; i++) {
607 if (!mono_w32handle_trylock (handles_data [i])) {
608 /* Bummer */
609
610 for (j = i - 1; j >= 0; j--)
611 mono_w32handle_unlock (handles_data [j]);
612
613 iter += 10;
614 if (iter == 1000)
615 iter = 10;
616
617 #ifdef HOST_WIN32
618 SleepEx (iter, TRUE);
619 #else
620 /* If iter ever reaches 1000 the nanosleep will
621 * return EINVAL immediately, but we have a
622 * design flaw if that happens. */
623 g_assert (iter < 1000);
624
625 sleepytime.tv_sec = 0;
626 sleepytime.tv_nsec = iter * 1000000;
627 nanosleep (&sleepytime, NULL);
628 #endif /* HOST_WIN32 */
629
630 goto again;
631 }
632 }
633
634 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: Locked all handles", __func__);
635 }
636
637 static void
mono_w32handle_unlock_handles(MonoW32Handle ** handles_data,gsize nhandles)638 mono_w32handle_unlock_handles (MonoW32Handle **handles_data, gsize nhandles)
639 {
640 gint i;
641
642 for (i = nhandles - 1; i >= 0; i--)
643 mono_w32handle_unlock (handles_data [i]);
644 }
645
646 static int
mono_w32handle_timedwait_signal_naked(MonoCoopCond * cond,MonoCoopMutex * mutex,guint32 timeout,gboolean poll,gboolean * alerted)647 mono_w32handle_timedwait_signal_naked (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout, gboolean poll, gboolean *alerted)
648 {
649 int res;
650
651 if (!poll) {
652 res = mono_coop_cond_timedwait (cond, mutex, timeout);
653 } else {
654 /* This is needed when waiting for process handles */
655 if (!alerted) {
656 /*
657 * pthread_cond_(timed)wait() can return 0 even if the condition was not
658 * signalled. This happens at least on Darwin. We surface this, i.e., we
659 * get spurious wake-ups.
660 *
661 * http://pubs.opengroup.org/onlinepubs/007908775/xsh/pthread_cond_wait.html
662 */
663 res = mono_coop_cond_timedwait (cond, mutex, timeout);
664 } else {
665 if (timeout < 100) {
666 /* Real timeout is less than 100ms time */
667 res = mono_coop_cond_timedwait (cond, mutex, timeout);
668 } else {
669 res = mono_coop_cond_timedwait (cond, mutex, 100);
670
671 /* Mask the fake timeout, this will cause
672 * another poll if the cond was not really signaled
673 */
674 if (res == -1)
675 res = 0;
676 }
677 }
678 }
679
680 return res;
681 }
682
683 static void
signal_global(gpointer unused)684 signal_global (gpointer unused)
685 {
686 /* If we reach here, then interrupt token is set to the flag value, which
687 * means that the target thread is either
688 * - before the first CAS in timedwait, which means it won't enter the wait.
689 * - it is after the first CAS, so it is already waiting, or it will enter
690 * the wait, and it will be interrupted by the broadcast. */
691 mono_coop_mutex_lock (&global_signal_mutex);
692 mono_coop_cond_broadcast (&global_signal_cond);
693 mono_coop_mutex_unlock (&global_signal_mutex);
694 }
695
696 static int
mono_w32handle_timedwait_signal(guint32 timeout,gboolean poll,gboolean * alerted)697 mono_w32handle_timedwait_signal (guint32 timeout, gboolean poll, gboolean *alerted)
698 {
699 int res;
700
701 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for global", __func__);
702
703 if (alerted)
704 *alerted = FALSE;
705
706 if (alerted) {
707 mono_thread_info_install_interrupt (signal_global, NULL, alerted);
708 if (*alerted)
709 return 0;
710 }
711
712 res = mono_w32handle_timedwait_signal_naked (&global_signal_cond, &global_signal_mutex, timeout, poll, alerted);
713
714 if (alerted)
715 mono_thread_info_uninstall_interrupt (alerted);
716
717 return res;
718 }
719
720 static void
signal_handle_and_unref(gpointer handle_duplicate)721 signal_handle_and_unref (gpointer handle_duplicate)
722 {
723 MonoW32Handle *handle_data;
724 MonoCoopCond *cond;
725 MonoCoopMutex *mutex;
726
727 if (!mono_w32handle_lookup_and_ref (handle_duplicate, &handle_data))
728 g_error ("%s: unknown handle %p", __func__, handle_duplicate);
729
730 /* If we reach here, then interrupt token is set to the flag value, which
731 * means that the target thread is either
732 * - before the first CAS in timedwait, which means it won't enter the wait.
733 * - it is after the first CAS, so it is already waiting, or it will enter
734 * the wait, and it will be interrupted by the broadcast. */
735 cond = &handle_data->signal_cond;
736 mutex = &handle_data->signal_mutex;
737
738 mono_coop_mutex_lock (mutex);
739 mono_coop_cond_broadcast (cond);
740 mono_coop_mutex_unlock (mutex);
741
742 mono_w32handle_unref (handle_data);
743
744 mono_w32handle_close (handle_duplicate);
745 }
746
747 static int
mono_w32handle_timedwait_signal_handle(MonoW32Handle * handle_data,guint32 timeout,gboolean poll,gboolean * alerted)748 mono_w32handle_timedwait_signal_handle (MonoW32Handle *handle_data, guint32 timeout, gboolean poll, gboolean *alerted)
749 {
750 gpointer handle_duplicate;
751 int res;
752
753 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: waiting for %p (type %s)", __func__, handle_data,
754 mono_w32handle_ops_typename (handle_data->type));
755
756 if (alerted)
757 *alerted = FALSE;
758
759 if (alerted) {
760 mono_thread_info_install_interrupt (signal_handle_and_unref, handle_duplicate = mono_w32handle_duplicate (handle_data), alerted);
761 if (*alerted) {
762 mono_w32handle_close (handle_duplicate);
763 return 0;
764 }
765 }
766
767 res = mono_w32handle_timedwait_signal_naked (&handle_data->signal_cond, &handle_data->signal_mutex, timeout, poll, alerted);
768
769 if (alerted) {
770 mono_thread_info_uninstall_interrupt (alerted);
771 if (!*alerted) {
772 /* if it is alerted, then the handle_duplicate is closed in the interrupt callback */
773 mono_w32handle_close (handle_duplicate);
774 }
775 }
776
777 return res;
778 }
779
780 static gboolean
dump_callback(MonoW32Handle * handle_data,gpointer user_data)781 dump_callback (MonoW32Handle *handle_data, gpointer user_data)
782 {
783 g_print ("%p [%7s] signalled: %5s ref: %3d ",
784 handle_data, mono_w32handle_ops_typename (handle_data->type), handle_data->signalled ? "true" : "false", handle_data->ref - 1 /* foreach increase ref by 1 */);
785 mono_w32handle_ops_details (handle_data);
786 g_print ("\n");
787
788 return FALSE;
789 }
790
mono_w32handle_dump(void)791 void mono_w32handle_dump (void)
792 {
793 mono_w32handle_foreach (dump_callback, NULL);
794 }
795
796 static gboolean
own_if_signalled(MonoW32Handle * handle_data,gboolean * abandoned)797 own_if_signalled (MonoW32Handle *handle_data, gboolean *abandoned)
798 {
799 if (!mono_w32handle_issignalled (handle_data))
800 return FALSE;
801
802 *abandoned = FALSE;
803 mono_w32handle_ops_own (handle_data, abandoned);
804 return TRUE;
805 }
806
807 static gboolean
own_if_owned(MonoW32Handle * handle_data,gboolean * abandoned)808 own_if_owned (MonoW32Handle *handle_data, gboolean *abandoned)
809 {
810 if (!mono_w32handle_ops_isowned (handle_data))
811 return FALSE;
812
813 *abandoned = FALSE;
814 mono_w32handle_ops_own (handle_data, abandoned);
815 return TRUE;
816 }
817
818 MonoW32HandleWaitRet
mono_w32handle_wait_one(gpointer handle,guint32 timeout,gboolean alertable)819 mono_w32handle_wait_one (gpointer handle, guint32 timeout, gboolean alertable)
820 {
821 MonoW32Handle *handle_data;
822 MonoW32HandleWaitRet ret;
823 gboolean alerted;
824 gint64 start;
825 gboolean abandoned = FALSE;
826
827 alerted = FALSE;
828
829 if (!mono_w32handle_lookup_and_ref (handle, &handle_data))
830 return MONO_W32HANDLE_WAIT_RET_FAILED;
831
832 if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
833 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p has special wait", __func__, handle_data);
834
835 mono_w32handle_unref (handle_data);
836 return mono_w32handle_ops_specialwait (handle_data, timeout, alertable ? &alerted : NULL);
837 }
838
839 if (!mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_WAIT)) {
840 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handle_data);
841
842 mono_w32handle_unref (handle_data);
843 return MONO_W32HANDLE_WAIT_RET_FAILED;
844 }
845
846 mono_w32handle_lock (handle_data);
847
848 if (mono_w32handle_test_capabilities (handle_data, MONO_W32HANDLE_CAP_OWN)) {
849 if (own_if_owned (handle_data, &abandoned)) {
850 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, handle_data);
851
852 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
853 goto done;
854 }
855 }
856
857 if (timeout != MONO_INFINITE_WAIT)
858 start = mono_msec_ticks ();
859
860 mono_w32handle_set_in_use (handle_data, TRUE);
861
862 for (;;) {
863 gint waited;
864
865 if (own_if_signalled (handle_data, &abandoned)) {
866 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, handle_data);
867
868 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
869 goto done;
870 }
871
872 mono_w32handle_ops_prewait (handle_data);
873
874 if (timeout == MONO_INFINITE_WAIT) {
875 waited = mono_w32handle_timedwait_signal_handle (handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
876 } else {
877 gint64 elapsed;
878
879 elapsed = mono_msec_ticks () - start;
880 if (elapsed > timeout) {
881 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
882 goto done;
883 }
884
885 waited = mono_w32handle_timedwait_signal_handle (handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
886 }
887
888 if (alerted) {
889 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
890 goto done;
891 }
892
893 if (waited != 0) {
894 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
895 goto done;
896 }
897 }
898
899 done:
900 mono_w32handle_set_in_use (handle_data, FALSE);
901
902 mono_w32handle_unlock (handle_data);
903
904 mono_w32handle_unref (handle_data);
905
906 return ret;
907 }
908
909 MonoW32HandleWaitRet
mono_w32handle_wait_multiple(gpointer * handles,gsize nhandles,gboolean waitall,guint32 timeout,gboolean alertable)910 mono_w32handle_wait_multiple (gpointer *handles, gsize nhandles, gboolean waitall, guint32 timeout, gboolean alertable)
911 {
912 MonoW32HandleWaitRet ret;
913 gboolean alerted, poll;
914 gint i;
915 gint64 start;
916 MonoW32Handle *handles_data [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS], *handles_data_sorted [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS];
917 gboolean abandoned [MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS] = {0};
918
919 if (nhandles == 0)
920 return MONO_W32HANDLE_WAIT_RET_FAILED;
921
922 if (nhandles == 1)
923 return mono_w32handle_wait_one (handles [0], timeout, alertable);
924
925 alerted = FALSE;
926
927 if (nhandles > MONO_W32HANDLE_MAXIMUM_WAIT_OBJECTS) {
928 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: too many handles: %zd",
929 __func__, nhandles);
930
931 return MONO_W32HANDLE_WAIT_RET_FAILED;
932 }
933
934 for (i = 0; i < nhandles; ++i) {
935 if (!mono_w32handle_lookup_and_ref (handles [i], &handles_data [i])) {
936 for (; i >= 0; --i)
937 mono_w32handle_unref (handles_data [i]);
938 return MONO_W32HANDLE_WAIT_RET_FAILED;
939 }
940 }
941
942 for (i = 0; i < nhandles; ++i) {
943 if (!mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_WAIT)
944 && !mono_w32handle_test_capabilities (handles_data[i], MONO_W32HANDLE_CAP_SPECIAL_WAIT))
945 {
946 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p can't be waited for", __func__, handles_data [i]);
947
948 for (i = nhandles - 1; i >= 0; --i)
949 mono_w32handle_unref (handles_data [i]);
950
951 return MONO_W32HANDLE_WAIT_RET_FAILED;
952 }
953
954 handles_data_sorted [i] = handles_data [i];
955 }
956
957 qsort (handles_data_sorted, nhandles, sizeof (gpointer), g_direct_equal);
958 for (i = 1; i < nhandles; ++i) {
959 if (handles_data_sorted [i - 1] == handles_data_sorted [i]) {
960 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_data_sorted [i]);
961
962 for (i = nhandles - 1; i >= 0; --i)
963 mono_w32handle_unref (handles_data [i]);
964
965 return MONO_W32HANDLE_WAIT_RET_FAILED;
966 }
967 }
968
969 poll = FALSE;
970 for (i = 0; i < nhandles; ++i) {
971 if (handles_data [i]->type == MONO_W32TYPE_PROCESS) {
972 /* Can't wait for a process handle + another handle without polling */
973 poll = TRUE;
974 }
975 }
976
977 if (timeout != MONO_INFINITE_WAIT)
978 start = mono_msec_ticks ();
979
980 for (;;) {
981 gsize count, lowest;
982 gboolean signalled;
983 gint waited;
984
985 count = 0;
986 lowest = nhandles;
987
988 mono_w32handle_lock_handles (handles_data, nhandles);
989
990 for (i = 0; i < nhandles; i++) {
991 if ((mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_OWN) && mono_w32handle_ops_isowned (handles_data [i]))
992 || mono_w32handle_issignalled (handles_data [i]))
993 {
994 count ++;
995
996 if (i < lowest)
997 lowest = i;
998 }
999 }
1000
1001 signalled = (waitall && count == nhandles) || (!waitall && count > 0);
1002
1003 if (signalled) {
1004 for (i = 0; i < nhandles; i++) {
1005 if (own_if_signalled (handles_data [i], &abandoned [i]) && !waitall) {
1006 /* if we are calling WaitHandle.WaitAny, .NET only owns the first one; it matters for Mutex which
1007 * throw AbandonedMutexException in case we owned it but didn't release it */
1008 break;
1009 }
1010 }
1011 }
1012
1013 mono_w32handle_unlock_handles (handles_data, nhandles);
1014
1015 if (signalled) {
1016 ret = MONO_W32HANDLE_WAIT_RET_SUCCESS_0 + lowest;
1017 for (i = lowest; i < nhandles; i++) {
1018 if (abandoned [i]) {
1019 ret = MONO_W32HANDLE_WAIT_RET_ABANDONED_0 + lowest;
1020 break;
1021 }
1022 }
1023 goto done;
1024 }
1025
1026 for (i = 0; i < nhandles; i++) {
1027 mono_w32handle_ops_prewait (handles_data [i]);
1028
1029 if (mono_w32handle_test_capabilities (handles_data [i], MONO_W32HANDLE_CAP_SPECIAL_WAIT)
1030 && !mono_w32handle_issignalled (handles_data [i]))
1031 {
1032 mono_w32handle_ops_specialwait (handles_data [i], 0, alertable ? &alerted : NULL);
1033 }
1034 }
1035
1036 mono_w32handle_lock_signal_mutex ();
1037
1038 if (waitall) {
1039 signalled = TRUE;
1040 for (i = 0; i < nhandles; ++i) {
1041 if (!mono_w32handle_issignalled (handles_data [i])) {
1042 signalled = FALSE;
1043 break;
1044 }
1045 }
1046 } else {
1047 signalled = FALSE;
1048 for (i = 0; i < nhandles; ++i) {
1049 if (mono_w32handle_issignalled (handles_data [i])) {
1050 signalled = TRUE;
1051 break;
1052 }
1053 }
1054 }
1055
1056 waited = 0;
1057
1058 if (!signalled) {
1059 if (timeout == MONO_INFINITE_WAIT) {
1060 waited = mono_w32handle_timedwait_signal (MONO_INFINITE_WAIT, poll, alertable ? &alerted : NULL);
1061 } else {
1062 gint64 elapsed;
1063
1064 elapsed = mono_msec_ticks () - start;
1065 if (elapsed > timeout) {
1066 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1067
1068 mono_w32handle_unlock_signal_mutex ();
1069
1070 goto done;
1071 }
1072
1073 waited = mono_w32handle_timedwait_signal (timeout - elapsed, poll, alertable ? &alerted : NULL);
1074 }
1075 }
1076
1077 mono_w32handle_unlock_signal_mutex ();
1078
1079 if (alerted) {
1080 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1081 goto done;
1082 }
1083
1084 if (waited != 0) {
1085 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1086 goto done;
1087 }
1088 }
1089
1090 done:
1091 for (i = nhandles - 1; i >= 0; i--) {
1092 /* Unref everything we reffed above */
1093 mono_w32handle_unref (handles_data [i]);
1094 }
1095
1096 return ret;
1097 }
1098
1099 MonoW32HandleWaitRet
mono_w32handle_signal_and_wait(gpointer signal_handle,gpointer wait_handle,guint32 timeout,gboolean alertable)1100 mono_w32handle_signal_and_wait (gpointer signal_handle, gpointer wait_handle, guint32 timeout, gboolean alertable)
1101 {
1102 MonoW32Handle *signal_handle_data, *wait_handle_data, *handles_data [2];
1103 MonoW32HandleWaitRet ret;
1104 gint64 start;
1105 gboolean alerted;
1106 gboolean abandoned = FALSE;
1107
1108 alerted = FALSE;
1109
1110 if (!mono_w32handle_lookup_and_ref (signal_handle, &signal_handle_data)) {
1111 return MONO_W32HANDLE_WAIT_RET_FAILED;
1112 }
1113 if (!mono_w32handle_lookup_and_ref (wait_handle, &wait_handle_data)) {
1114 mono_w32handle_unref (signal_handle_data);
1115 return MONO_W32HANDLE_WAIT_RET_FAILED;
1116 }
1117
1118 if (!mono_w32handle_test_capabilities (signal_handle_data, MONO_W32HANDLE_CAP_SIGNAL)) {
1119 mono_w32handle_unref (wait_handle_data);
1120 mono_w32handle_unref (signal_handle_data);
1121 return MONO_W32HANDLE_WAIT_RET_FAILED;
1122 }
1123 if (!mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_WAIT)) {
1124 mono_w32handle_unref (wait_handle_data);
1125 mono_w32handle_unref (signal_handle_data);
1126 return MONO_W32HANDLE_WAIT_RET_FAILED;
1127 }
1128
1129 if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_SPECIAL_WAIT)) {
1130 g_warning ("%s: handle %p has special wait, implement me!!", __func__, wait_handle_data);
1131 mono_w32handle_unref (wait_handle_data);
1132 mono_w32handle_unref (signal_handle_data);
1133 return MONO_W32HANDLE_WAIT_RET_FAILED;
1134 }
1135
1136 handles_data [0] = wait_handle_data;
1137 handles_data [1] = signal_handle_data;
1138
1139 mono_w32handle_lock_handles (handles_data, 2);
1140
1141 mono_w32handle_ops_signal (signal_handle_data);
1142
1143 mono_w32handle_unlock (signal_handle_data);
1144
1145 if (mono_w32handle_test_capabilities (wait_handle_data, MONO_W32HANDLE_CAP_OWN)) {
1146 if (own_if_owned (wait_handle_data, &abandoned)) {
1147 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p already owned", __func__, wait_handle_data);
1148
1149 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1150 goto done;
1151 }
1152 }
1153
1154 if (timeout != MONO_INFINITE_WAIT)
1155 start = mono_msec_ticks ();
1156
1157 for (;;) {
1158 gint waited;
1159
1160 if (own_if_signalled (wait_handle_data, &abandoned)) {
1161 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p signalled", __func__, wait_handle_data);
1162
1163 ret = abandoned ? MONO_W32HANDLE_WAIT_RET_ABANDONED_0 : MONO_W32HANDLE_WAIT_RET_SUCCESS_0;
1164 goto done;
1165 }
1166
1167 mono_w32handle_ops_prewait (wait_handle_data);
1168
1169 if (timeout == MONO_INFINITE_WAIT) {
1170 waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, MONO_INFINITE_WAIT, FALSE, alertable ? &alerted : NULL);
1171 } else {
1172 gint64 elapsed;
1173
1174 elapsed = mono_msec_ticks () - start;
1175 if (elapsed > timeout) {
1176 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1177 goto done;
1178 }
1179
1180 waited = mono_w32handle_timedwait_signal_handle (wait_handle_data, timeout - elapsed, FALSE, alertable ? &alerted : NULL);
1181 }
1182
1183 if (alerted) {
1184 ret = MONO_W32HANDLE_WAIT_RET_ALERTED;
1185 goto done;
1186 }
1187
1188 if (waited != 0) {
1189 ret = MONO_W32HANDLE_WAIT_RET_TIMEOUT;
1190 goto done;
1191 }
1192 }
1193
1194 done:
1195 mono_w32handle_unlock (wait_handle_data);
1196
1197 mono_w32handle_unref (wait_handle_data);
1198 mono_w32handle_unref (signal_handle_data);
1199
1200 return ret;
1201 }
1202