X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fthreadpool.c;h=a7f7c04f54e6d4a87223171e98f49bbf6ab60c23;hb=dc1155ddcf876d543da9a162a69e14d4903d2ac8;hp=b7eb634595e438575135dcf815b1a1802f3da6c9;hpb=9a8eeddc379f07c8befb73f144ad314e1dd83a0c;p=mono.git diff --git a/mono/metadata/threadpool.c b/mono/metadata/threadpool.c index b7eb634595e..a7f7c04f54e 100644 --- a/mono/metadata/threadpool.c +++ b/mono/metadata/threadpool.c @@ -15,15 +15,15 @@ #ifdef PLATFORM_WIN32 #define WINVER 0x0500 #define _WIN32_WINNT 0x0500 -#define THREADS_PER_CPU 25 -#else -#define THREADS_PER_CPU 50 #endif +#define THREADS_PER_CPU 5 /* 20 + THREADS_PER_CPU * number of CPUs */ + #include #include #include #include +#include #include #include #include @@ -47,11 +47,15 @@ #include "threadpool.h" +#define THREAD_WANTS_A_BREAK(t) ((t->state & (ThreadState_StopRequested | \ + ThreadState_SuspendRequested)) != 0) + #undef EPOLL_DEBUG + /* maximum number of worker threads */ -static int mono_max_worker_threads = THREADS_PER_CPU; -static int mono_min_worker_threads = 0; -static int mono_io_max_worker_threads = THREADS_PER_CPU * 2; +static int mono_max_worker_threads; +static int mono_min_worker_threads; +static int mono_io_max_worker_threads; /* current number of worker threads */ static int mono_worker_threads = 0; @@ -91,25 +95,29 @@ static SocketIOData socket_io_data; static HANDLE job_added; static HANDLE io_job_added; +/* Keep in sync with the System.MonoAsyncCall class which provides GC tracking */ typedef struct { + MonoObject object; MonoMethodMessage *msg; - HANDLE wait_event; MonoMethod *cb_method; MonoDelegate *cb_target; MonoObject *state; MonoObject *res; MonoArray *out_args; + /* This is a HANDLE, we use guint64 so the managed object layout remains constant */ + guint64 wait_event; } ASyncCall; static void async_invoke_thread (gpointer data); -static void append_job (CRITICAL_SECTION *cs, GList **plist, MonoAsyncResult *ar); +static void append_job (CRITICAL_SECTION *cs, GList **plist, gpointer ar); static void start_thread_or_queue (MonoAsyncResult *ares); static void mono_async_invoke (MonoAsyncResult *ares); -static MonoAsyncResult *dequeue_job (CRITICAL_SECTION *cs, GList **plist); +static gpointer dequeue_job (CRITICAL_SECTION *cs, GList **plist); static GList *async_call_queue = NULL; static GList *async_io_queue = NULL; +static MonoClass *async_call_klass; static MonoClass *socket_async_call_klass; #define INIT_POLLFD(a, b, c) {(a)->fd = b; (a)->events = c; (a)->revents = 0;} @@ -121,6 +129,8 @@ enum { AIO_OP_RECEIVEFROM, AIO_OP_SEND, AIO_OP_SENDTO, + AIO_OP_RECV_JUST_CALLBACK, + AIO_OP_SEND_JUST_CALLBACK, AIO_OP_LAST }; @@ -168,9 +178,11 @@ get_event_from_state (MonoSocketAsyncResult *state) switch (state->operation) { case AIO_OP_ACCEPT: case AIO_OP_RECEIVE: + case AIO_OP_RECV_JUST_CALLBACK: case AIO_OP_RECEIVEFROM: return MONO_POLLIN; case AIO_OP_SEND: + case AIO_OP_SEND_JUST_CALLBACK: case AIO_OP_SENDTO: case AIO_OP_CONNECT: return MONO_POLLOUT; @@ -195,6 +207,14 @@ get_events_from_list (GSList *list) return events; } +#define ICALL_RECV(x) ves_icall_System_Net_Sockets_Socket_Receive_internal (\ + (SOCKET) x->handle, x->buffer, x->offset, x->size,\ + x->socket_flags, &x->error); + +#define ICALL_SEND(x) ves_icall_System_Net_Sockets_Socket_Send_internal (\ + (SOCKET) x->handle, x->buffer, x->offset, x->size,\ + x->socket_flags, &x->error); + static void async_invoke_io_thread (gpointer data) { @@ -202,22 +222,41 @@ async_invoke_io_thread (gpointer data) MonoThread *thread; thread = mono_thread_current (); thread->threadpool_thread = TRUE; - thread->state |= ThreadState_Background; + ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); for (;;) { + MonoSocketAsyncResult *state; MonoAsyncResult *ar; - ar = (MonoAsyncResult *) data; - if (ar) { + state = (MonoSocketAsyncResult *) data; + if (state) { InterlockedDecrement (&pending_io_items); + ar = state->ares; /* worker threads invokes methods in different domains, * so we need to set the right domain here */ + switch (state->operation) { + case AIO_OP_RECEIVE: + state->total = ICALL_RECV (state); + break; + case AIO_OP_SEND: + state->total = ICALL_SEND (state); + break; + } + domain = ((MonoObject *)ar)->vtable->domain; + mono_thread_push_appdomain_ref (domain); if (mono_domain_set (domain, FALSE)) { - mono_thread_push_appdomain_ref (domain); + ASyncCall *ac; + mono_async_invoke (ar); - mono_thread_pop_appdomain_ref (); + ac = (ASyncCall *) ar->object_data; + /* + if (ac->msg->exc != NULL) + mono_unhandled_exception (ac->msg->exc); + */ + mono_domain_set (mono_get_root_domain (), TRUE); } + mono_thread_pop_appdomain_ref (); InterlockedDecrement (&busy_io_worker_threads); } @@ -230,7 +269,7 @@ async_invoke_io_thread (gpointer data) do { wr = WaitForSingleObjectEx (io_job_added, (guint32)timeout, TRUE); - if ((thread->state & ThreadState_StopRequested)!=0) + if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); timeout -= GetTickCount () - start_time; @@ -259,7 +298,7 @@ async_invoke_io_thread (gpointer data) } static void -start_io_thread_or_queue (MonoAsyncResult *ares) +start_io_thread_or_queue (MonoSocketAsyncResult *ares) { int busy, worker; MonoDomain *domain; @@ -271,7 +310,7 @@ start_io_thread_or_queue (MonoAsyncResult *ares) InterlockedIncrement (&busy_io_worker_threads); InterlockedIncrement (&io_worker_threads); domain = ((ares) ? ((MonoObject *) ares)->vtable->domain : mono_domain_get ()); - mono_thread_create (domain, async_invoke_io_thread, ares); + mono_thread_create (mono_get_root_domain (), async_invoke_io_thread, ares); } else { append_job (&io_queue_lock, &async_io_queue, ares); ReleaseSemaphore (io_job_added, 1, NULL); @@ -301,7 +340,7 @@ process_io_event (GSList *list, int event) g_print ("Dispatching event %d on socket %d\n", event, state->handle); #endif InterlockedIncrement (&pending_io_items); - start_io_thread_or_queue (state->ares); + start_io_thread_or_queue (state); } return oldlist; @@ -319,7 +358,7 @@ mark_bad_fds (mono_pollfd *pfds, int nfds) if (pfd->fd == -1) continue; - ret = mono_poll (pfds, 1, 0); + ret = mono_poll (pfd, 1, 0); if (ret == -1 && errno == EBADF) { pfd->revents |= MONO_POLLNVAL; count++; @@ -345,7 +384,7 @@ socket_io_poll_main (gpointer p) thread = mono_thread_current (); thread->threadpool_thread = TRUE; - thread->state |= ThreadState_Background; + ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); allocated = INITIAL_POLLFD_SIZE; pfds = g_new0 (mono_pollfd, allocated); @@ -361,7 +400,7 @@ socket_io_poll_main (gpointer p) do { if (nsock == -1) { - if ((thread->state & ThreadState_StopRequested) != 0) + if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } @@ -486,18 +525,14 @@ socket_io_epoll_main (gpointer p) epollfd = data->epollfd; thread = mono_thread_current (); thread->threadpool_thread = TRUE; - thread->state |= ThreadState_Background; + ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); events = g_new0 (struct epoll_event, nevents); while (1) { do { if (ready == -1) { - if ((thread->state & ThreadState_StopRequested) != 0) { - g_free (events); - close (epollfd); + if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); - g_assert_not_reached (); - } } #ifdef EPOLL_DEBUG g_print ("epoll_wait init\n"); @@ -506,7 +541,7 @@ socket_io_epoll_main (gpointer p) #ifdef EPOLL_DEBUG { int err = errno; - g_print ("epoll_wait end with %d ready sockets.\n", ready); + g_print ("epoll_wait end with %d ready sockets (%d %s).\n", ready, err, (err) ? g_strerror (err) : ""); errno = err; } #endif @@ -515,11 +550,10 @@ socket_io_epoll_main (gpointer p) if (ready == -1) { int err = errno; g_free (events); - if (err != EBADF) { + if (err != EBADF) g_warning ("epoll_wait: %d %s\n", err, g_strerror (err)); - } else { - close (epollfd); - } + + close (epollfd); return; } @@ -558,26 +592,20 @@ socket_io_epoll_main (gpointer p) g_print ("MOD %d to %d\n", fd, evt->events); #endif if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, evt)) { - int err = errno; - if (err == EBADF) { - g_free (events); - return; /* epollfd closed */ + if (epoll_ctl (epollfd, EPOLL_CTL_ADD, fd, evt) == -1) { +#ifdef EPOLL_DEBUG + int err = errno; + g_message ("epoll_ctl(MOD): %d %s fd: %d events: %d", err, g_strerror (err), fd, evt->events); + errno = err; +#endif } - g_message ("epoll_ctl(MOD): %d %s fd: %d events: %d", err, g_strerror (err), fd, evt->events); } } else { g_hash_table_remove (data->sock_to_state, GINT_TO_POINTER (fd)); #ifdef EPOLL_DEBUG g_print ("DEL %d\n", fd); #endif - if (epoll_ctl (epollfd, EPOLL_CTL_DEL, fd, evt)) { - int err = errno; - if (err == EBADF) { - g_free (events); - return; /* epollfd closed */ - } - g_message ("epoll_ctl(DEL): %d %s", err, g_strerror (err)); - } + epoll_ctl (epollfd, EPOLL_CTL_DEL, fd, evt); } } LeaveCriticalSection (&data->io_lock); @@ -585,6 +613,44 @@ socket_io_epoll_main (gpointer p) } #endif +/* + * select/poll wake up when a socket is closed, but epoll just removes + * the socket from its internal list without notification. + */ +void +mono_thread_pool_remove_socket (int sock) +{ +#ifdef HAVE_EPOLL + GSList *list, *next; + MonoSocketAsyncResult *state; + + if (socket_io_data.epoll_disabled == TRUE || socket_io_data.inited == FALSE) + return; + + EnterCriticalSection (&socket_io_data.io_lock); + list = g_hash_table_lookup (socket_io_data.sock_to_state, GINT_TO_POINTER (sock)); + if (list) { + g_hash_table_remove (socket_io_data.sock_to_state, GINT_TO_POINTER (sock)); + } + LeaveCriticalSection (&socket_io_data.io_lock); + + while (list) { + state = (MonoSocketAsyncResult *) list->data; + if (state->operation == AIO_OP_RECEIVE) + state->operation = AIO_OP_RECV_JUST_CALLBACK; + else if (state->operation == AIO_OP_SEND) + state->operation = AIO_OP_SEND_JUST_CALLBACK; + + next = g_slist_remove_link (list, list); + list = process_io_event (list, MONO_POLLIN); + if (list) + process_io_event (list, MONO_POLLOUT); + + list = next; + } +#endif +} + #ifdef PLATFORM_WIN32 static void connect_hack (gpointer x) @@ -639,10 +705,12 @@ socket_io_init (SocketIOData *data) #endif #ifndef PLATFORM_WIN32 - if (data->epoll_disabled && pipe (data->pipe) != 0) { - int err = errno; - perror ("mono"); - g_assert (err); + if (data->epoll_disabled) { + if (pipe (data->pipe) != 0) { + int err = errno; + perror ("mono"); + g_assert (err); + } } else { data->pipe [0] = -1; data->pipe [1] = -1; @@ -670,6 +738,9 @@ socket_io_init (SocketIOData *data) g_assert (data->pipe [0] != INVALID_SOCKET); closesocket (srv); #endif + mono_io_max_worker_threads = mono_max_worker_threads / 2; + if (mono_io_max_worker_threads < 10) + mono_io_max_worker_threads = 10; data->sock_to_state = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -697,6 +768,14 @@ socket_io_add_poll (MonoSocketAsyncResult *state) GSList *list; SocketIOData *data = &socket_io_data; +#if defined(PLATFORM_MACOSX) || defined(PLATFORM_BSD6) + /* select() for connect() does not work well on the Mac. Bug #75436. */ + /* Bug #77637 for the BSD 6 case */ + if (state->operation == AIO_OP_CONNECT && state->blocking == TRUE) { + start_io_thread_or_queue (state); + return; + } +#endif WaitForSingleObject (data->new_sem, INFINITE); if (data->newpfd == NULL) data->newpfd = g_new0 (mono_pollfd, 1); @@ -758,10 +837,11 @@ socket_io_add_epoll (MonoSocketAsyncResult *state) #endif if (epoll_ctl (data->epollfd, epoll_op, fd, &event) == -1) { int err = errno; - g_message ("epoll_ctl(ADD): %d %s\n", err, g_strerror (err)); - epoll_op = EPOLL_CTL_MOD; - if (epoll_ctl (data->epollfd, epoll_op, fd, &event) == -1) { - g_message ("epoll_ctl(MOD): %d %s\n", err, g_strerror (err)); + if (epoll_op == EPOLL_CTL_ADD && err == EEXIST) { + epoll_op = EPOLL_CTL_MOD; + if (epoll_ctl (data->epollfd, epoll_op, fd, &event) == -1) { + g_message ("epoll_ctl(MOD): %d %s\n", err, g_strerror (err)); + } } } @@ -774,7 +854,7 @@ static void socket_io_add (MonoAsyncResult *ares, MonoSocketAsyncResult *state) { socket_io_init (&socket_io_data); - state->ares = ares; + MONO_OBJECT_SETREF (state, ares, ares); #ifdef HAVE_EPOLL if (socket_io_data.epoll_disabled == FALSE) { if (socket_io_add_epoll (state)) @@ -794,24 +874,21 @@ socket_io_filter (MonoObject *target, MonoObject *state) if (target == NULL || state == NULL) return FALSE; - klass = InterlockedCompareExchangePointer ((gpointer *) &socket_async_call_klass, NULL, NULL); - if (klass == NULL) { - MonoImage *system_assembly = mono_image_loaded ("System"); - - if (system_assembly == NULL) - return FALSE; - - klass = mono_class_from_name (system_assembly, "System.Net.Sockets", "Socket/SocketAsyncCall"); - if (klass == NULL) { - /* Should never happen... */ - g_print ("socket_io_filter: SocketAsyncCall class not found.\n"); - return FALSE; - } - - InterlockedCompareExchangePointer ((gpointer *) &socket_async_call_klass, klass, NULL); + if (socket_async_call_klass == NULL) { + klass = target->vtable->klass; + /* Check if it's SocketAsyncCall in System + * FIXME: check the assembly is signed correctly for extra care + */ + if (klass->name [0] == 'S' && strcmp (klass->name, "SocketAsyncCall") == 0 + && strcmp (mono_image_get_name (klass->image), "System") == 0 + && klass->nested_in && strcmp (klass->nested_in->name, "Socket") == 0) + socket_async_call_klass = klass; } - if (target->vtable->klass != klass) + /* return both when socket_async_call_klass has not been seen yet and when + * the object is not an instance of the class. + */ + if (target->vtable->klass != socket_async_call_klass) return FALSE; op = sock_res->operation; @@ -824,7 +901,17 @@ socket_io_filter (MonoObject *target, MonoObject *state) static void mono_async_invoke (MonoAsyncResult *ares) { - ASyncCall *ac = (ASyncCall *)ares->data; + ASyncCall *ac = (ASyncCall *)ares->object_data; + MonoThread *thread = NULL; + + if (ares->execution_context) { + /* use captured ExecutionContext (if available) */ + thread = mono_thread_current (); + MONO_OBJECT_SETREF (ares, original_context, thread->execution_context); + MONO_OBJECT_SETREF (thread, execution_context, ares->execution_context); + } else { + ares->original_context = NULL; + } ac->msg->exc = NULL; ac->res = mono_message_invoke (ares->async_delegate, ac->msg, @@ -837,8 +924,16 @@ mono_async_invoke (MonoAsyncResult *ares) MonoObject *exc = NULL; void *pa = &ares; mono_runtime_invoke (ac->cb_method, ac->cb_target, pa, &exc); - if (!ac->msg->exc) - ac->msg->exc = exc; + /* 'exc' will be the previous ac->msg->exc if not NULL and not + * catched. If catched, this will be set to NULL and the + * exception will not be printed. */ + MONO_OBJECT_SETREF (ac->msg, exc, exc); + } + + /* restore original thread execution context if flow isn't suppressed, i.e. non null */ + if (ares->original_context) { + MONO_OBJECT_SETREF (thread, execution_context, ares->original_context); + ares->original_context = NULL; } /* notify listeners */ @@ -866,16 +961,19 @@ mono_thread_pool_init () MONO_GC_REGISTER_ROOT (ares_htable); InitializeCriticalSection (&socket_io_data.io_lock); InitializeCriticalSection (&ares_lock); - ares_htable = mono_g_hash_table_new (NULL, NULL); + ares_htable = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_KEY_VALUE_GC); job_added = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); GetSystemInfo (&info); - if (getenv ("MONO_THREADS_PER_CPU") != NULL) { - threads_per_cpu = atoi (getenv ("MONO_THREADS_PER_CPU")); + if (g_getenv ("MONO_THREADS_PER_CPU") != NULL) { + threads_per_cpu = atoi (g_getenv ("MONO_THREADS_PER_CPU")); if (threads_per_cpu <= 0) threads_per_cpu = THREADS_PER_CPU; } - mono_max_worker_threads = threads_per_cpu * info.dwNumberOfProcessors; + mono_max_worker_threads = 20 + threads_per_cpu * info.dwNumberOfProcessors; + + async_call_klass = mono_class_from_name (mono_defaults.corlib, "System", "MonoAsyncCall"); + g_assert (async_call_klass); } MonoAsyncResult * @@ -888,21 +986,23 @@ mono_thread_pool_add (MonoObject *target, MonoMethodMessage *msg, MonoDelegate * #ifdef HAVE_BOEHM_GC ac = GC_MALLOC (sizeof (ASyncCall)); +#elif defined(HAVE_SGEN_GC) + ac = mono_object_new (mono_domain_get (), async_call_klass); #else /* We'll leak the event if creaated... */ ac = g_new0 (ASyncCall, 1); #endif ac->wait_event = NULL; - ac->msg = msg; - ac->state = state; + MONO_OBJECT_SETREF (ac, msg, msg); + MONO_OBJECT_SETREF (ac, state, state); if (async_callback) { ac->cb_method = mono_get_delegate_invoke (((MonoObject *)async_callback)->vtable->klass); ac->cb_target = async_callback; } - ares = mono_async_result_new (domain, NULL, ac->state, ac); - ares->async_delegate = target; + ares = mono_async_result_new (domain, NULL, ac->state, NULL, ac); + MONO_OBJECT_SETREF (ares, async_delegate, target); EnterCriticalSection (&ares_lock); mono_g_hash_table_insert (ares_htable, ares, ares); @@ -930,7 +1030,7 @@ start_thread_or_queue (MonoAsyncResult *ares) InterlockedIncrement (&mono_worker_threads); InterlockedIncrement (&busy_worker_threads); domain = ((MonoObject *) ares)->vtable->domain; - mono_thread_create (domain, async_invoke_thread, ares); + mono_thread_create (mono_get_root_domain (), async_invoke_thread, ares); } else { append_job (&mono_delegate_section, &async_call_queue, ares); ReleaseSemaphore (job_added, 1, NULL); @@ -956,7 +1056,7 @@ mono_thread_pool_finish (MonoAsyncResult *ares, MonoArray **out_args, MonoObject } ares->endinvoke_called = 1; - ac = (ASyncCall *)ares->data; + ac = (ASyncCall *)ares->object_data; g_assert (ac != NULL); @@ -964,7 +1064,7 @@ mono_thread_pool_finish (MonoAsyncResult *ares, MonoArray **out_args, MonoObject if (!ares->completed) { if (ares->handle == NULL) { ac->wait_event = CreateEvent (NULL, TRUE, FALSE, NULL); - ares->handle = (MonoObject *) mono_wait_handle_new (mono_object_domain (ares), ac->wait_event); + MONO_OBJECT_SETREF (ares, handle, (MonoObject *) mono_wait_handle_new (mono_object_domain (ares), ac->wait_event)); } mono_monitor_exit ((MonoObject *) ares); WaitForSingleObjectEx (ac->wait_event, INFINITE, TRUE); @@ -972,7 +1072,7 @@ mono_thread_pool_finish (MonoAsyncResult *ares, MonoArray **out_args, MonoObject mono_monitor_exit ((MonoObject *) ares); } - *exc = ac->msg->exc; + *exc = ac->msg->exc; /* FIXME: GC add write barrier */ *out_args = ac->out_args; return ac->res; @@ -995,7 +1095,7 @@ mono_thread_pool_cleanup (void) } static void -append_job (CRITICAL_SECTION *cs, GList **plist, MonoAsyncResult *ar) +append_job (CRITICAL_SECTION *cs, GList **plist, gpointer ar) { GList *tmp, *list; @@ -1015,17 +1115,17 @@ append_job (CRITICAL_SECTION *cs, GList **plist, MonoAsyncResult *ar) LeaveCriticalSection (cs); } -static MonoAsyncResult * +static gpointer dequeue_job (CRITICAL_SECTION *cs, GList **plist) { - MonoAsyncResult *ar = NULL; + gpointer ar = NULL; GList *tmp, *tmp2, *list; EnterCriticalSection (cs); list = *plist; tmp = list; if (tmp) { - ar = (MonoAsyncResult *) tmp->data; + ar = tmp->data; tmp->data = NULL; tmp2 = tmp; for (tmp2 = tmp; tmp2->next != NULL; tmp2 = tmp2->next); @@ -1051,7 +1151,7 @@ async_invoke_thread (gpointer data) thread = mono_thread_current (); thread->threadpool_thread = TRUE; - thread->state |= ThreadState_Background; + ves_icall_System_Threading_Thread_SetState (thread, ThreadState_Background); for (;;) { MonoAsyncResult *ar; @@ -1061,16 +1161,24 @@ async_invoke_thread (gpointer data) /* worker threads invokes methods in different domains, * so we need to set the right domain here */ domain = ((MonoObject *)ar)->vtable->domain; + mono_thread_push_appdomain_ref (domain); if (mono_domain_set (domain, FALSE)) { - mono_thread_push_appdomain_ref (domain); + ASyncCall *ac; + mono_async_invoke (ar); - mono_thread_pop_appdomain_ref (); + ac = (ASyncCall *) ar->object_data; + /* + if (ac->msg->exc != NULL) + mono_unhandled_exception (ac->msg->exc); + */ + mono_domain_set (mono_get_root_domain (), TRUE); } + mono_thread_pop_appdomain_ref (); InterlockedDecrement (&busy_worker_threads); } data = dequeue_job (&mono_delegate_section, &async_call_queue); - + if (!data) { guint32 wr; int timeout = 10000; @@ -1078,7 +1186,7 @@ async_invoke_thread (gpointer data) do { wr = WaitForSingleObjectEx (job_added, (guint32)timeout, TRUE); - if ((thread->state & ThreadState_StopRequested)!=0) + if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); timeout -= GetTickCount () - start_time; @@ -1095,7 +1203,7 @@ async_invoke_thread (gpointer data) while (!data && workers <= min) { WaitForSingleObjectEx (job_added, INFINITE, TRUE); - if ((thread->state & ThreadState_StopRequested)!=0) + if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); data = dequeue_job (&mono_delegate_section, &async_call_queue);