ensure timer guts never called more than once per event loop round to ensure it doesnt lock up the nromal event handling message pump

This commit is contained in:
Adam D. Ruppe 2020-12-09 14:39:08 -05:00
parent d61f339e41
commit dd2f7a60a0
1 changed files with 20 additions and 3 deletions

View File

@ -3375,6 +3375,7 @@ struct EventLoopImpl {
int ret = -1; int ret = -1;
MSG message; MSG message;
while(ret != 0 && (whileCondition is null || whileCondition() == true) && notExited) { while(ret != 0 && (whileCondition is null || whileCondition() == true) && notExited) {
eventLoopRound++;
auto wto = SimpleWindow.eventAllQueueTimeoutMSecs(); auto wto = SimpleWindow.eventAllQueueTimeoutMSecs();
auto waitResult = MsgWaitForMultipleObjectsEx( auto waitResult = MsgWaitForMultipleObjectsEx(
cast(int) handles.length, handles.ptr, cast(int) handles.length, handles.ptr,
@ -4318,6 +4319,7 @@ class Timer {
this(int intervalInMilliseconds, void delegate() onPulse) { this(int intervalInMilliseconds, void delegate() onPulse) {
assert(onPulse !is null); assert(onPulse !is null);
this.intervalInMilliseconds = intervalInMilliseconds;
this.onPulse = onPulse; this.onPulse = onPulse;
version(Windows) { version(Windows) {
@ -4329,7 +4331,7 @@ class Timer {
// thanks to Archival 998 for the WaitableTimer blocks // thanks to Archival 998 for the WaitableTimer blocks
handle = CreateWaitableTimer(null, false, null); handle = CreateWaitableTimer(null, false, null);
long initialTime = 0; long initialTime = -intervalInMilliseconds;
if(handle is null || !SetWaitableTimer(handle, cast(LARGE_INTEGER*)&initialTime, intervalInMilliseconds, &timerCallback, handle, false)) if(handle is null || !SetWaitableTimer(handle, cast(LARGE_INTEGER*)&initialTime, intervalInMilliseconds, &timerCallback, handle, false))
throw new Exception("SetWaitableTimer Failed"); throw new Exception("SetWaitableTimer Failed");
@ -4370,6 +4372,8 @@ class Timer {
} else featureNotImplemented(); } else featureNotImplemented();
} }
private int intervalInMilliseconds;
/// Stop and destroy the timer object. /// Stop and destroy the timer object.
void destroy() { void destroy() {
version(Windows) { version(Windows) {
@ -4409,12 +4413,13 @@ class Timer {
void changeTime(int intervalInMilliseconds) void changeTime(int intervalInMilliseconds)
{ {
this.intervalInMilliseconds = intervalInMilliseconds;
version(Windows) version(Windows)
{ {
if(handle) if(handle)
{ {
//handle = SetTimer(null, handle, intervalInMilliseconds, &timerCallback); //handle = SetTimer(null, handle, intervalInMilliseconds, &timerCallback);
long initialTime = 0; long initialTime = -intervalInMilliseconds;
if(handle is null || !SetWaitableTimer(handle, cast(LARGE_INTEGER*)&initialTime, intervalInMilliseconds, &timerCallback, handle, false)) if(handle is null || !SetWaitableTimer(handle, cast(LARGE_INTEGER*)&initialTime, intervalInMilliseconds, &timerCallback, handle, false))
throw new Exception("couldn't change pulse timer"); throw new Exception("couldn't change pulse timer");
} }
@ -4426,18 +4431,27 @@ class Timer {
void delegate() onPulse; void delegate() onPulse;
int lastEventLoopRoundTriggered;
void trigger() { void trigger() {
version(linux) { version(linux) {
import unix = core.sys.posix.unistd; import unix = core.sys.posix.unistd;
long val; long val;
unix.read(fd, &val, val.sizeof); // gotta clear the pipe unix.read(fd, &val, val.sizeof); // gotta clear the pipe
} else version(Windows) { } else version(Windows) {
if(this.lastEventLoopRoundTriggered == eventLoopRound)
return; // never try to actually run faster than the event loop
lastEventLoopRoundTriggered = eventLoopRound;
} else featureNotImplemented(); } else featureNotImplemented();
onPulse(); onPulse();
} }
version(Windows)
void rearm() {
}
version(Windows) version(Windows)
extern(Windows) extern(Windows)
//static void timerCallback(HWND, UINT, UINT_PTR timer, DWORD dwTime) nothrow { //static void timerCallback(HWND, UINT, UINT_PTR timer, DWORD dwTime) nothrow {
@ -4461,6 +4475,9 @@ class Timer {
} }
} }
version(Windows)
private int eventLoopRound;
version(Windows) version(Windows)
/// Lets you add HANDLEs to the event loop. Not meant to be used for async I/O per se, but for other handles (it can only handle a few handles at a time.) Only works on certain types of handles! see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex /// Lets you add HANDLEs to the event loop. Not meant to be used for async I/O per se, but for other handles (it can only handle a few handles at a time.) Only works on certain types of handles! see: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex
class WindowsHandleReader { class WindowsHandleReader {