Cygwin: timerfd: implement TFD_TIMER_CANCEL_ON_SET

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2019-01-20 22:47:52 +01:00
parent 95bc4240ed
commit 2993057a94
3 changed files with 109 additions and 11 deletions

View File

@ -663,6 +663,7 @@ LoadDLLfunc (CreateDesktopW, 24, user32)
LoadDLLfunc (CreateWindowExW, 48, user32)
LoadDLLfunc (CreateWindowStationW, 16, user32)
LoadDLLfunc (DefWindowProcW, 16, user32)
LoadDLLfunc (DestroyWindow, 4, user32)
LoadDLLfunc (DispatchMessageW, 4, user32)
LoadDLLfunc (EmptyClipboard, 0, user32)
LoadDLLfunc (EnumWindows, 8, user32)
@ -690,6 +691,7 @@ LoadDLLfunc (SetClipboardData, 8, user32)
LoadDLLfunc (SetParent, 8, user32)
LoadDLLfunc (SetProcessWindowStation, 4, user32)
LoadDLLfunc (SetThreadDesktop, 4, user32)
LoadDLLfunc (UnregisterClassW, 8, user32)
LoadDLLfuncEx (CreateEnvironmentBlock, 12, userenv, 1)
LoadDLLfuncEx2 (CreateProfile, 16, userenv, 1, 1)

View File

@ -16,6 +16,70 @@ details. */
#include <sys/timerfd.h>
#include "timerfd.h"
#define TFD_CANCEL_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
/* Unfortunately MsgWaitForMultipleObjectsEx does not receive WM_TIMECHANGED
messages without a window defined in this process. Create a hidden window
for that purpose. */
void
timerfd_tracker::create_timechange_window ()
{
WNDCLASSW wclass = { 0 };
WCHAR cname[NAME_MAX];
__small_swprintf (cname, L"Cygwin.timerfd.%u", winpid);
wclass.lpfnWndProc = DefWindowProcW;
wclass.hInstance = GetModuleHandle (NULL);
wclass.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wclass.lpszClassName = cname;
atom = RegisterClassW (&wclass);
if (!atom)
debug_printf ("RegisterClass %E");
else
{
window = CreateWindowExW (0, cname, cname, WS_POPUP, 0, 0, 0, 0,
NULL, NULL, NULL, NULL);
if (!window)
debug_printf ("RegisterClass %E");
}
}
void
timerfd_tracker::delete_timechange_window ()
{
if (window)
DestroyWindow (window);
if (atom)
UnregisterClassW ((LPWSTR) (uintptr_t) atom, GetModuleHandle (NULL));
}
void
timerfd_tracker::handle_timechange_window ()
{
MSG msg;
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) && msg.message != WM_QUIT)
{
DispatchMessageW(&msg);
if (msg.message == WM_TIMECHANGE
&& get_clockid () == CLOCK_REALTIME
&& (flags () & TFD_CANCEL_FLAGS) == TFD_CANCEL_FLAGS
&& enter_critical_section ())
{
/* make sure to handle each WM_TIMECHANGE only once! */
if (msg.time != tc_time ())
{
set_overrun_count (-1LL);
disarm_timer ();
timer_expired ();
set_tc_time (msg.time);
}
leave_critical_section ();
}
}
}
DWORD
timerfd_tracker::thread_func ()
{
@ -23,14 +87,19 @@ timerfd_tracker::thread_func ()
HANDLE armed[2] = { tfd_shared->arm_evt (),
cancel_evt };
create_timechange_window ();
while (1)
{
switch (WaitForMultipleObjects (2, armed, FALSE, INFINITE))
switch (MsgWaitForMultipleObjectsEx (2, armed, INFINITE, QS_POSTMESSAGE,
MWMO_INPUTAVAILABLE))
{
case WAIT_OBJECT_0:
break;
case WAIT_OBJECT_0 + 1:
goto canceled;
case WAIT_OBJECT_0 + 2:
handle_timechange_window ();
continue;
default:
continue;
}
@ -40,9 +109,12 @@ timerfd_tracker::thread_func ()
HANDLE expired[3] = { tfd_shared->timer (),
tfd_shared->disarm_evt (),
cancel_evt };
while (1)
{
switch (WaitForMultipleObjects (3, expired, FALSE, INFINITE))
switch (MsgWaitForMultipleObjectsEx (3, expired, INFINITE,
QS_POSTMESSAGE,
MWMO_INPUTAVAILABLE))
{
case WAIT_OBJECT_0:
break;
@ -50,12 +122,23 @@ timerfd_tracker::thread_func ()
goto disarmed;
case WAIT_OBJECT_0 + 2:
goto canceled;
case WAIT_OBJECT_0 + 3:
handle_timechange_window ();
continue;
default:
continue;
}
if (!enter_critical_section ())
continue;
/* Make sure we haven't been abandoned and/or disarmed
in the meantime */
if (overrun_count () == -1LL
|| IsEventSignalled (tfd_shared->disarm_evt ()))
{
leave_critical_section ();
goto disarmed;
}
/* One-shot timer? */
if (!get_interval ())
{
@ -93,7 +176,7 @@ timerfd_tracker::thread_func ()
NtSetTimer (tfd_shared->timer (), &DueTime, NULL, NULL,
Resume, 0, NULL);
}
}
}
/* Arm the expiry object */
timer_expired ();
leave_critical_section ();
@ -103,6 +186,7 @@ disarmed:
}
canceled:
delete_timechange_window ();
_my_tls._ctinfo->auto_release (); /* automatically return the cygthread to the cygthread pool */
return 0;
}

View File

@ -31,6 +31,7 @@ class timerfd_shared
LONG64 _interval; /* timer interval in 100ns */
LONG64 _overrun_count; /* expiry counter */
int _flags; /* settime flags */
DWORD _tc_time; /* timestamp of the last WM_TIMECHANGE msg */
int create (clockid_t);
bool dtor ();
@ -43,7 +44,8 @@ class timerfd_shared
LONG64 get_clock_now () const { return get_clock (_clockid)->n100secs (); }
struct itimerspec &time_spec () { return _time_spec; }
int flags () const { return _flags; }
LONG64 overrun_count () const { return _overrun_count; }
/* write access methods */
void increment_overrun_count (LONG64 add)
{ InterlockedAdd64 (&_overrun_count, add); }
void set_overrun_count (LONG64 newval)
@ -55,8 +57,6 @@ class timerfd_shared
ResetEvent (_expired_evt);
return ret;
}
/* write access methods */
bool enter_cs ()
{
return (WaitForSingleObject (_access_mtx, INFINITE) & ~WAIT_ABANDONED_0)
@ -73,7 +73,7 @@ class timerfd_shared
memset (&_time_spec, 0, sizeof _time_spec);
_exp_ts = 0;
_interval = 0;
_flags = 0;
/* _flags = 0; DON'T DO THAT. Required for TFD_TIMER_CANCEL_ON_SET */
NtCancelTimer (timer (), NULL);
SetEvent (_disarm_evt);
return 0;
@ -86,8 +86,6 @@ class timerfd_shared
class timerfd_tracker /* cygheap! */
{
DWORD winpid; /* This is used @ fork/exec time to know if
this tracker already has been fixed up. */
HANDLE tfd_shared_hdl; /* handle auf shared mem */
timerfd_shared *tfd_shared; /* pointer auf shared mem, needs
NtMapViewOfSection in each new process. */
@ -96,6 +94,14 @@ class timerfd_tracker /* cygheap! */
HANDLE sync_thr; /* cygthread sync object. */
LONG local_instance_count; /* each open fd increments this.
If 0 -> cancel thread. */
DWORD winpid; /* This is used @ fork/exec time to know if
this tracker already has been fixed up. */
HWND window; /* window handle */
ATOM atom; /* window class */
void create_timechange_window ();
void delete_timechange_window ();
void handle_timechange_window ();
bool dtor ();
@ -107,9 +113,10 @@ class timerfd_tracker /* cygheap! */
int disarm_timer () const { return tfd_shared->disarm_timer (); }
void timer_expired () const { tfd_shared->timer_expired (); }
LONG64 overrun_count () const { return tfd_shared->_overrun_count; }
void increment_overrun_count (LONG64 add) const
{ tfd_shared->increment_overrun_count (add); }
void set_overrun_count (uint64_t ov_cnt) const
void set_overrun_count (LONG64 ov_cnt) const
{ tfd_shared->set_overrun_count ((LONG64) ov_cnt); }
LONG64 read_and_reset_overrun_count () const
{ return tfd_shared->read_and_reset_overrun_count (); }
@ -119,9 +126,13 @@ class timerfd_tracker /* cygheap! */
struct timespec it_interval () const
{ return tfd_shared->time_spec ().it_interval; }
clock_t get_clockid () const { return tfd_shared->_clockid; }
LONG64 get_clock_now () const { return tfd_shared->get_clock_now (); }
LONG64 get_exp_ts () const { return tfd_shared->_exp_ts; }
LONG64 get_interval () const { return tfd_shared->_interval; }
int flags () const { return tfd_shared->flags (); }
DWORD tc_time () const { return tfd_shared->_tc_time; }
void set_tc_time (DWORD new_time) { tfd_shared->_tc_time = new_time; }
void set_exp_ts (LONG64 ts) const { tfd_shared->set_exp_ts (ts); }
@ -129,7 +140,8 @@ class timerfd_tracker /* cygheap! */
void *operator new (size_t, void *p) __attribute__ ((nothrow)) {return p;}
timerfd_tracker ()
: tfd_shared_hdl (NULL), tfd_shared (NULL), cancel_evt (NULL),
sync_thr (NULL), local_instance_count (1) {}
sync_thr (NULL), local_instance_count (1), winpid (0), window (NULL),
atom (0) {}
int create (clockid_t);
int gettime (struct itimerspec *);
int settime (int, const struct itimerspec *, struct itimerspec *);