Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/thread.cc')
-rw-r--r--winsup/cygwin/thread.cc170
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--;