diff options
Diffstat (limited to 'winsup/cygwin/thread.cc')
-rw-r--r-- | winsup/cygwin/thread.cc | 170 |
1 files changed, 112 insertions, 58 deletions
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index aefab24cd..901155a0e 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -27,12 +27,15 @@ details. */ #include "miscfuncs.h" #include "path.h" #include <stdlib.h> +#include "pinfo.h" #include "sigproc.h" +#include "perprocess.h" +#include "cygtls.h" #include "fhandler.h" #include "dtable.h" #include "cygheap.h" #include "ntdll.h" -#include "cygwait.h" +#include "miscfuncs.h" extern "C" void __fp_lock_all (); extern "C" void __fp_unlock_all (); @@ -432,18 +435,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 +566,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 +914,93 @@ 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) + { + const size_t sizeof_tbi = sizeof (TIMER_BASIC_INFORMATION); + PTIMER_BASIC_INFORMATION tbi = (PTIMER_BASIC_INFORMATION) malloc (sizeof_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 +1077,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); } @@ -1067,8 +1127,8 @@ pthread::resume () pthread_attr::pthread_attr ():verifyable_object (PTHREAD_ATTR_MAGIC), joinable (PTHREAD_CREATE_JOINABLE), contentionscope (PTHREAD_SCOPE_PROCESS), -inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), -stacksize (PTHREAD_DEFAULT_STACKSIZE), guardsize (PTHREAD_DEFAULT_GUARDSIZE) +inheritsched (PTHREAD_INHERIT_SCHED), stackaddr (NULL), stacksize (0), +guardsize ((size_t) -1) { schedparam.sched_priority = 0; } @@ -1228,7 +1288,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 +1803,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 +1943,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 +1988,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 +2421,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) @@ -2689,7 +2746,8 @@ pthread_cond_signal (pthread_cond_t *cond) } static int -__pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) +__pthread_cond_dowait (pthread_cond_t *cond, pthread_mutex_t *mutex, + PLARGE_INTEGER waitlength) { if (!pthread_mutex::is_good_object (mutex)) return EINVAL; @@ -2701,7 +2759,7 @@ __pthread_cond_wait_init (pthread_cond_t *cond, pthread_mutex_t *mutex) if (!pthread_cond::is_good_object (cond)) return EINVAL; - return 0; + return (*cond)->wait (*mutex, waitlength); } extern "C" int @@ -2717,10 +2775,6 @@ pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, pthread_testcancel (); - int err = __pthread_cond_wait_init (cond, mutex); - if (err) - return err; - /* According to SUSv3, the abstime value must be checked for validity. */ if (abstime->tv_sec < 0 || abstime->tv_nsec < 0 @@ -2749,7 +2803,7 @@ pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, timeout.QuadPart *= -1LL; break; } - return (*cond)->wait (*mutex, &timeout); + return __pthread_cond_dowait (cond, mutex, &timeout); } extern "C" int @@ -2757,10 +2811,7 @@ pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex) { pthread_testcancel (); - int err = __pthread_cond_wait_init (cond, mutex); - if (err) - return err; - return (*cond)->wait (*mutex, NULL); + return __pthread_cond_dowait (cond, mutex, NULL); } extern "C" int @@ -3015,7 +3066,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 +3530,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 +3552,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--; |