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