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:
Ken Brown 2019-06-24 12:28:48 -04:00
parent a90aa583fb
commit 9604a251bd
3 changed files with 28 additions and 1 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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);