diff options
Diffstat (limited to 'winsup/cygwin/thread.cc')
-rw-r--r-- | winsup/cygwin/thread.cc | 145 |
1 files changed, 100 insertions, 45 deletions
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index aefab24cd..af0d6afc7 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -32,7 +32,6 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "ntdll.h" -#include "cygwait.h" extern "C" void __fp_lock_all (); extern "C" void __fp_unlock_all (); @@ -432,18 +431,8 @@ pthread::precreate (pthread_attr *newattr) magic = 0; return; } - /* This mutex MUST be recursive. Consider the following scenario: - - The thread installs a cleanup handler. - - The cleanup handler calls a function which itself installs a - cleanup handler. - - pthread_cancel is called for this thread. - - The thread's cleanup handler is called under mutex lock condition. - - The cleanup handler calls the subsequent function with cleanup handler. - - The function runs to completion, so it calls pthread_cleanup_pop. - - pthread_cleanup_pop calls pthread::pop_cleanup_handler which will again - try to lock the mutex. - - Deadlock. */ - mutex.set_type (PTHREAD_MUTEX_RECURSIVE); + /* Change the mutex type to NORMAL to speed up mutex operations */ + mutex.set_type (PTHREAD_MUTEX_NORMAL); if (!create_cancel_event ()) magic = 0; } @@ -573,27 +562,10 @@ pthread::cancel () CONTEXT context; context.ContextFlags = CONTEXT_CONTROL; GetThreadContext (win32_obj_id, &context); - /* The OS is not foolproof in terms of asynchronous thread cancellation - and tends to hang infinitely if we change the instruction pointer. - So just don't cancel asynchronously if the thread is currently - executing Windows code. Rely on deferred cancellation in this case. */ - if (!cygtls->inside_kernel (&context)) - { - context.Eip = (DWORD) pthread::static_cancel_self; - SetThreadContext (win32_obj_id, &context); - } + context.Eip = (DWORD) pthread::static_cancel_self; + SetThreadContext (win32_obj_id, &context); } mutex.unlock (); - /* See above. For instance, a thread which waits for a semaphore in sem_wait - will call cancelable_wait which in turn calls WFMO. While this WFMO call - is cancelable by setting the thread's cancel_event object, the OS - apparently refuses to set the thread's context and continues to wait for - the WFMO conditions. This is *not* reflected in the return value of - SetThreadContext or ResumeThread, btw. - So, what we do here is to set the cancel_event as well to allow at least - a deferred cancel. */ - canceled = true; - SetEvent (cancel_event); ResumeThread (win32_obj_id); return 0; @@ -938,6 +910,92 @@ pthread::static_cancel_self () pthread::self ()->cancel_self (); } +DWORD +cancelable_wait (HANDLE object, PLARGE_INTEGER timeout, + const cw_cancel_action cancel_action, + const enum cw_sig_wait sig_wait) +{ + DWORD res; + DWORD num = 0; + HANDLE wait_objects[4]; + pthread_t thread = pthread::self (); + + /* Do not change the wait order. + The object must have higher priority than the cancel event, + because WaitForMultipleObjects will return the smallest index + if both objects are signaled. */ + wait_objects[num++] = object; + DWORD cancel_n; + if (cancel_action == cw_no_cancel || !pthread::is_good_object (&thread) || + thread->cancelstate == PTHREAD_CANCEL_DISABLE) + cancel_n = WAIT_TIMEOUT + 1; + else + { + cancel_n = WAIT_OBJECT_0 + num++; + wait_objects[cancel_n] = thread->cancel_event; + } + + DWORD sig_n; + if (sig_wait == cw_sig_nosig) + sig_n = WAIT_TIMEOUT + 1; + else + { + sig_n = WAIT_OBJECT_0 + num++; + wait_objects[sig_n] = signal_arrived; + } + + DWORD timeout_n; + if (!timeout) + timeout_n = WAIT_TIMEOUT + 1; + else + { + timeout_n = WAIT_OBJECT_0 + num++; + if (!_my_tls.locals.cw_timer) + NtCreateTimer (&_my_tls.locals.cw_timer, TIMER_ALL_ACCESS, NULL, + NotificationTimer); + NtSetTimer (_my_tls.locals.cw_timer, timeout, NULL, NULL, FALSE, 0, NULL); + wait_objects[timeout_n] = _my_tls.locals.cw_timer; + } + + while (1) + { + res = WaitForMultipleObjects (num, wait_objects, FALSE, INFINITE); + if (res == cancel_n) + { + if (cancel_action == cw_cancel_self) + pthread::static_cancel_self (); + res = WAIT_CANCELED; + } + else if (res == timeout_n) + res = WAIT_TIMEOUT; + else if (res != sig_n) + /* all set */; + else if (sig_wait == cw_sig_eintr) + res = WAIT_SIGNALED; + else + { + _my_tls.call_signal_handler (); + continue; + } + break; + } + + if (timeout) + { + TIMER_BASIC_INFORMATION tbi; + + NtQueryTimer (_my_tls.locals.cw_timer, TimerBasicInformation, &tbi, + sizeof tbi, NULL); + /* if timer expired, TimeRemaining is negative and represents the + system uptime when signalled */ + if (timeout->QuadPart < 0LL) + timeout->QuadPart = tbi.SignalState ? 0LL : tbi.TimeRemaining.QuadPart; + NtCancelTimer (_my_tls.locals.cw_timer, NULL); + } + + return res; +} + int pthread::setcancelstate (int state, int *oldstate) { @@ -1014,9 +1072,6 @@ pthread::pop_cleanup_handler (int const execute) void pthread::pop_all_cleanup_handlers () { - /* We will no honor cancels since the thread is exiting. */ - cancelstate = PTHREAD_CANCEL_DISABLE; - while (cleanup_stack != NULL) pop_cleanup_handler (1); } @@ -1228,7 +1283,7 @@ pthread_cond::wait (pthread_mutex_t mutex, PLARGE_INTEGER timeout) ++mutex->condwaits; mutex->unlock (); - rv = cancelable_wait (sem_wait, timeout, cw_cancel | cw_sig_eintr); + rv = cancelable_wait (sem_wait, timeout, cw_no_cancel_self, cw_sig_eintr); mtx_out.lock (); @@ -1743,8 +1798,7 @@ pthread_mutex::lock () else if (type == PTHREAD_MUTEX_NORMAL /* potentially causes deadlock */ || !pthread::equal (owner, self)) { - /* FIXME: no cancel? */ - cancelable_wait (win32_obj_id, cw_infinite, cw_sig); + cancelable_wait (win32_obj_id, NULL, cw_no_cancel, cw_sig_resume); set_owner (self); } else @@ -1884,8 +1938,7 @@ pthread_spinlock::lock () /* Minimal timeout to minimize CPU usage while still spinning. */ LARGE_INTEGER timeout; timeout.QuadPart = -10000LL; - /* FIXME: no cancel? */ - cancelable_wait (win32_obj_id, &timeout, cw_sig); + cancelable_wait (win32_obj_id, &timeout, cw_no_cancel, cw_sig_resume); } } while (result == -1); @@ -1930,7 +1983,6 @@ pthread::thread_init_wrapper (void *arg) _my_tls.sigmask = thread->parent_sigmask; thread->mutex.unlock (); - debug_printf ("tid %p", &_my_tls); thread_printf ("started thread %p %p %p %p %p %p", arg, &_my_tls.local_clib, _impure_ptr, thread, thread->function, thread->arg); @@ -2364,7 +2416,7 @@ pthread::join (pthread_t *thread, void **return_val) (*thread)->attr.joinable = PTHREAD_CREATE_DETACHED; (*thread)->mutex.unlock (); - switch (cancelable_wait ((*thread)->win32_obj_id, cw_infinite, cw_sig | cw_cancel)) + switch (cancelable_wait ((*thread)->win32_obj_id, NULL, cw_no_cancel_self, cw_sig_resume)) { case WAIT_OBJECT_0: if (return_val) @@ -3015,7 +3067,10 @@ pthread_kill (pthread_t thread, int sig) if (!thread->valid) rval = ESRCH; else if (sig) - rval = sig_send (NULL, si, thread->cygtls); + { + thread->cygtls->set_threadkill (); + rval = sig_send (NULL, si, thread->cygtls); + } else switch (WaitForSingleObject (thread->win32_obj_id, 0)) { @@ -3476,7 +3531,7 @@ semaphore::_timedwait (const struct timespec *abstime) timeout.QuadPart = abstime->tv_sec * NSPERSEC + (abstime->tv_nsec + 99) / 100 + FACTOR; - switch (cancelable_wait (win32_obj_id, &timeout, cw_cancel | cw_cancel_self | cw_sig_eintr)) + switch (cancelable_wait (win32_obj_id, &timeout, cw_cancel_self, cw_sig_eintr)) { case WAIT_OBJECT_0: currentvalue--; @@ -3498,7 +3553,7 @@ semaphore::_timedwait (const struct timespec *abstime) int semaphore::_wait () { - switch (cancelable_wait (win32_obj_id, cw_infinite, cw_cancel | cw_cancel_self | cw_sig_eintr)) + switch (cancelable_wait (win32_obj_id, NULL, cw_cancel_self, cw_sig_eintr)) { case WAIT_OBJECT_0: currentvalue--; |