+ gpointer obj = o;
+
+ // FIXME: need some fix in sgen code.
+ // There are roots at: async*tp.unused (MonoMList) and wsqs [n]->queue (MonoArray)
+ return obj == async_tp.first || obj == async_io_tp.first;
+}
+
+static void
+add_wsq (MonoWSQ *wsq)
+{
+ int i;
+
+ if (wsq == NULL)
+ return;
+
+ EnterCriticalSection (&wsqs_lock);
+ if (wsqs == NULL) {
+ LeaveCriticalSection (&wsqs_lock);
+ return;
+ }
+ for (i = 0; i < wsqs->len; i++) {
+ if (g_ptr_array_index (wsqs, i) == NULL) {
+ wsqs->pdata [i] = wsq;
+ LeaveCriticalSection (&wsqs_lock);
+ return;
+ }
+ }
+ g_ptr_array_add (wsqs, wsq);
+ LeaveCriticalSection (&wsqs_lock);
+}
+
+static void
+remove_wsq (MonoWSQ *wsq)
+{
+ if (wsq == NULL)
+ return;
+
+ EnterCriticalSection (&wsqs_lock);
+ if (wsqs == NULL) {
+ LeaveCriticalSection (&wsqs_lock);
+ return;
+ }
+ g_ptr_array_remove_fast (wsqs, wsq);
+ LeaveCriticalSection (&wsqs_lock);
+}
+
+static void
+try_steal (gpointer *data, gboolean retry)
+{
+ int i;
+ int ms;
+
+ if (wsqs == NULL || data == NULL || *data != NULL)
+ return;
+
+ ms = 0;
+ do {
+ if (mono_runtime_is_shutting_down ())
+ return;
+ for (i = 0; wsqs != NULL && i < wsqs->len; i++) {
+ if (mono_runtime_is_shutting_down ()) {
+ return;
+ }
+ mono_wsq_try_steal (wsqs->pdata [i], data, ms);
+ if (*data != NULL) {
+ return;
+ }
+ }
+ ms += 10;
+ } while (retry && ms < 11);
+}
+
+static gboolean
+dequeue_or_steal (ThreadPool *tp, gpointer *data)
+{
+ if (mono_runtime_is_shutting_down ())
+ return FALSE;
+ TP_DEBUG ("Dequeue");
+ MONO_SEM_WAIT (&tp->lock);
+ *data = dequeue_job_nolock (tp);
+ MONO_SEM_POST (&tp->lock);
+ if (!tp->is_io && !*data)
+ try_steal (data, FALSE);
+ return (*data != NULL);
+}
+
+static void
+process_idle_times (ThreadPool *tp, gint64 t)
+{
+ gint64 ticks;
+ gint64 avg;
+ gboolean compute_avg;
+ gint new_threads;
+ gint64 per1;
+
+ if (tp->ignore_times || t <= 0)
+ return;
+
+ compute_avg = FALSE;
+ ticks = mono_100ns_ticks ();
+ t = ticks - t;
+ SPIN_LOCK (tp->sp_lock);
+ if (tp->ignore_times) {
+ SPIN_UNLOCK (tp->sp_lock);
+ return;
+ }
+ tp->time_sum += t;
+ tp->n_sum++;
+ if (tp->last_check == 0)
+ tp->last_check = ticks;
+ else if (tp->last_check > 0 && (ticks - tp->last_check) > 5000000) {
+ tp->ignore_times = 1;
+ compute_avg = TRUE;
+ }
+ SPIN_UNLOCK (tp->sp_lock);
+
+ if (!compute_avg)
+ return;
+
+ //printf ("Items: %d Time elapsed: %.3fs\n", tp->n_sum, (ticks - tp->last_check) / 10000.0);
+ tp->last_check = ticks;
+ new_threads = 0;
+ avg = tp->time_sum / tp->n_sum;
+ if (tp->averages [1] == 0) {
+ tp->averages [1] = avg;
+ } else {
+ per1 = ((100 * (ABS (avg - tp->averages [1]))) / tp->averages [1]);
+ if (per1 > 5) {
+ if (avg > tp->averages [1]) {
+ if (tp->averages [1] < tp->averages [0]) {
+ new_threads = -1;
+ } else {
+ new_threads = 1;
+ }
+ } else if (avg < tp->averages [1] && tp->averages [1] < tp->averages [0]) {
+ new_threads = 1;
+ }
+ } else {
+ int min, n;
+ min = tp->min_threads;
+ n = tp->nthreads;
+ if ((n - min) < min && tp->busy_threads == n)
+ new_threads = 1;
+ }
+ /*
+ if (new_threads != 0) {
+ printf ("n: %d per1: %lld avg=%lld avg1=%lld avg0=%lld\n", new_threads, per1, avg, tp->averages [1], tp->averages [0]);
+ }
+ */
+ }
+
+ tp->time_sum = 0;
+ tp->n_sum = 0;
+
+ tp->averages [0] = tp->averages [1];
+ tp->averages [1] = avg;
+ tp->ignore_times = 0;
+
+ if (tp->waiting == 0 && new_threads == 1) {
+ threadpool_start_thread (tp);
+ } else if (new_threads == -1) {
+ if (tp->destroy_thread == 0 && InterlockedCompareExchange (&tp->destroy_thread, 1, 0) == 0)
+ pulse_on_new_job (tp);
+ }
+}
+
+static gboolean
+should_i_die (ThreadPool *tp)
+{
+ gboolean result = FALSE;
+ if (tp->destroy_thread == 1 && InterlockedCompareExchange (&tp->destroy_thread, 0, 1) == 1)
+ result = (tp->nthreads > tp->min_threads);
+ return result;