+
+static void
+threadpool_clear_queue (ThreadPool *tp, MonoDomain *domain)
+{
+ int i, count = 0;
+ EnterCriticalSection (&tp->lock);
+ /*remove*/
+ for (i = tp->first_elem; i < tp->next_elem; ++i) {
+ MonoObject *obj = mono_array_get (tp->array, MonoObject*, i);
+ if (obj->vtable->domain == domain) {
+ mono_array_set (tp->array, MonoObject*, i, NULL);
+ InterlockedDecrement (&domain->threadpool_jobs);
+ ++count;
+ }
+ }
+ /*compact*/
+ if (count) {
+ int idx = 0;
+ for (i = tp->first_elem; i < tp->next_elem; ++i) {
+ MonoObject *obj = mono_array_get (tp->array, MonoObject*, i);
+ if (obj)
+ mono_array_set (tp->array, MonoObject*, idx++, obj);
+ }
+ tp->first_elem = 0;
+ tp->next_elem = count;
+ }
+ LeaveCriticalSection (&tp->lock);
+}
+
+/*
+ * Clean up the threadpool of all domain jobs.
+ * Can only be called as part of the domain unloading process as
+ * it will wait for all jobs to be visible to the interruption code.
+ */
+gboolean
+mono_thread_pool_remove_domain_jobs (MonoDomain *domain, int timeout)
+{
+ HANDLE sem_handle;
+ int result = TRUE;
+ guint32 start_time = 0;
+
+ g_assert (domain->state == MONO_APPDOMAIN_UNLOADING);
+
+ threadpool_clear_queue (&async_tp, domain);
+ threadpool_clear_queue (&async_io_tp, domain);
+
+ /*
+ * There might be some threads out that could be about to execute stuff from the given domain.
+ * We avoid that by setting up a semaphore to be pulsed by the thread that reaches zero.
+ */
+ sem_handle = CreateSemaphore (NULL, 0, 1, NULL);
+
+ domain->cleanup_semaphore = sem_handle;
+ /*
+ * The memory barrier here is required to have global ordering between assigning to cleanup_semaphone
+ * and reading threadpool_jobs.
+ * Otherwise this thread could read a stale version of threadpool_jobs and wait forever.
+ */
+ mono_memory_write_barrier ();
+
+ if (domain->threadpool_jobs && timeout != -1)
+ start_time = mono_msec_ticks ();
+ while (domain->threadpool_jobs) {
+ WaitForSingleObject (sem_handle, timeout);
+ if (timeout != -1 && (mono_msec_ticks () - start_time) > timeout) {
+ result = FALSE;
+ break;
+ }
+ }
+
+ domain->cleanup_semaphore = NULL;
+ CloseHandle (sem_handle);
+ return result;