Cygwin: timerfd: avoid a deadlock
Add a function timerfd_tracker::enter_critical_section_cancelable, which is like enter_critical_section but honors a cancel event. Call this when a timer expires while the timerfd thread is in its inner loop. This avoids a deadlock if timerfd_tracker::dtor has entered its critical section and is trying to cancel the thread. See https://cygwin.com/ml/cygwin/2019-06/msg00096.html.
This commit is contained in:
parent
b7c9ab1af6
commit
73e3ff9328
|
@ -34,3 +34,6 @@ Bug Fixes
|
|||
- Define missing MSG_EOR. It's unsupported by the underlying Winsock
|
||||
layer so using it in send(2), sendto(2), or sendmsg(2) will return -1
|
||||
with errno set to EOPNOTSUPP and recvmsg(2) will never return it.
|
||||
|
||||
- Fix a race condition in the timerfd code.
|
||||
Addresses: https://cygwin.com/ml/cygwin/2019-06/msg00096.html
|
||||
|
|
|
@ -89,6 +89,25 @@ timerfd_tracker::handle_timechange_window ()
|
|||
}
|
||||
}
|
||||
|
||||
/* Like enter_critical_section, but returns -1 on a cancel event. */
|
||||
int
|
||||
timerfd_tracker::enter_critical_section_cancelable ()
|
||||
{
|
||||
HANDLE w[2] = { cancel_evt, _access_mtx };
|
||||
DWORD waitret = WaitForMultipleObjects (2, w, FALSE, INFINITE);
|
||||
|
||||
switch (waitret)
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
return -1;
|
||||
case WAIT_OBJECT_0 + 1:
|
||||
case WAIT_ABANDONED_0 + 1:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD
|
||||
timerfd_tracker::thread_func ()
|
||||
{
|
||||
|
@ -137,7 +156,10 @@ timerfd_tracker::thread_func ()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!enter_critical_section ())
|
||||
int ec = enter_critical_section_cancelable ();
|
||||
if (ec < 0)
|
||||
goto canceled;
|
||||
else if (!ec)
|
||||
continue;
|
||||
/* Make sure we haven't been abandoned and/or disarmed
|
||||
in the meantime */
|
||||
|
|
|
@ -86,6 +86,8 @@ class timerfd_tracker /* cygheap! */
|
|||
return (WaitForSingleObject (_access_mtx, INFINITE) & ~WAIT_ABANDONED_0)
|
||||
== WAIT_OBJECT_0;
|
||||
}
|
||||
/* A version that honors a cancel event, for use in thread_func. */
|
||||
int enter_critical_section_cancelable ();
|
||||
void leave_critical_section ()
|
||||
{
|
||||
ReleaseMutex (_access_mtx);
|
||||
|
|
Loading…
Reference in New Issue