diff --git a/README.md b/README.md index 9372f53..3fca018 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Driver | Linux | Windows | OS X | FreeBSD ------------------|-------|---------|------|-------- SelectEventDriver | yes | yes | yes¹ | yes¹ EpollEventDriver | yes | no | no | no -WinAPIEventDriver | no | yes¹ | no | no +WinAPIEventDriver | no | yes | no | no KqueueEventDriver | no | no | yes¹ | yes¹ ¹ planned, but not currenly implemented diff --git a/source/eventcore/drivers/winapi.d b/source/eventcore/drivers/winapi.d index 9fa8d46..593ceaa 100644 --- a/source/eventcore/drivers/winapi.d +++ b/source/eventcore/drivers/winapi.d @@ -10,41 +10,49 @@ module eventcore.drivers.winapi; version (Windows): import eventcore.driver; +import eventcore.drivers.timer; import std.socket : Address; import core.time : Duration; +import core.sys.windows.windows; +import core.sys.windows.winsock2; final class WinAPIEventDriver : EventDriver { -@safe: /*@nogc:*/ nothrow: - private { WinAPIEventDriverCore m_core; WinAPIEventDriverFiles m_files; WinAPIEventDriverSockets m_sockets; WinAPIEventDriverDNS m_dns; - WinAPIEventDriverTimers m_timers; + LoopTimeoutTimerDriver m_timers; WinAPIEventDriverEvents m_events; WinAPIEventDriverSignals m_signals; WinAPIEventDriverWatchers m_watchers; } this() - { - m_core = new WinAPIEventDriverCore(); + @safe { + import std.exception : enforce; + + WSADATA wd; + enforce(() @trusted { return WSAStartup(0x0202, &wd); } () == 0, "Failed to initialize WinSock"); + + m_events = new WinAPIEventDriverEvents(); + m_signals = new WinAPIEventDriverSignals(); + m_timers = new LoopTimeoutTimerDriver(); + m_core = new WinAPIEventDriverCore(m_timers); m_files = new WinAPIEventDriverFiles(); m_sockets = new WinAPIEventDriverSockets(); m_dns = new WinAPIEventDriverDNS(); - m_timers = new WinAPIEventDriverTimers(); - m_events = new WinAPIEventDriverEvents(); - m_signals = new WinAPIEventDriverSignals(); m_watchers = new WinAPIEventDriverWatchers(); } +@safe: /*@nogc:*/ nothrow: + override @property WinAPIEventDriverCore core() { return m_core; } override @property WinAPIEventDriverFiles files() { return m_files; } override @property WinAPIEventDriverSockets sockets() { return m_sockets; } override @property WinAPIEventDriverDNS dns() { return m_dns; } - override @property WinAPIEventDriverTimers timers() { return m_timers; } + override @property LoopTimeoutTimerDriver timers() { return m_timers; } override @property WinAPIEventDriverEvents events() { return m_events; } override @property shared(WinAPIEventDriverEvents) events() shared { return m_events; } override @property WinAPIEventDriverSignals signals() { return m_signals; } @@ -52,30 +60,78 @@ final class WinAPIEventDriver : EventDriver { override void dispose() { - assert(false, "TODO!"); } } final class WinAPIEventDriverCore : EventDriverCore { @safe: /*@nogc:*/ nothrow: - override size_t waiterCount() - { - assert(false, "TODO!"); + private { + bool m_exit; + size_t m_waiterCount; + DWORD m_tid; + LoopTimeoutTimerDriver m_timers; + HANDLE[] m_registeredEvents; + HANDLE m_fileCompletionEvent; } + this(LoopTimeoutTimerDriver timers) + { + m_timers = timers; + m_tid = () @trusted { return GetCurrentThreadId(); } (); + m_fileCompletionEvent = () @trusted { return CreateEventW(null, false, false, null); } (); + m_registeredEvents ~= m_fileCompletionEvent; + } + + override size_t waiterCount() { return m_waiterCount; } + override ExitReason processEvents(Duration timeout = Duration.max) { - assert(false, "TODO!"); + import std.algorithm : min; + import core.time : hnsecs, seconds; + + if (m_exit) { + m_exit = false; + return ExitReason.exited; + } + + bool got_event; + + if (timeout <= 0.seconds) { + got_event = doProcessEvents(0.seconds); + got_event |= m_timers.process(currStdTime); + return got_event ? ExitReason.idle : ExitReason.timeout; + } else { + long now = currStdTime; + do { + auto nextto = min(m_timers.getNextTimeout(now), timeout); + got_event |= doProcessEvents(nextto); + long prev_step = now; + now = currStdTime; + got_event |= m_timers.process(now); + + if (m_exit) { + m_exit = false; + return ExitReason.exited; + } + if (timeout != Duration.max) + timeout -= (now - prev_step).hnsecs; + } while (timeout > 0.seconds); + } + + if (!waiterCount) return ExitReason.outOfWaiters; + if (got_event) return ExitReason.idle; + return ExitReason.timeout; } override void exit() - { - assert(false, "TODO!"); + @trusted { + m_exit = true; + PostThreadMessageW(m_tid, WM_QUIT, 0, 0); } override void clearExitFlag() { - assert(false, "TODO!"); + m_exit = false; } protected override void* rawUserData(StreamSocketFD descriptor, size_t size, DataInitializer initialize, DataInitializer destroy) @system @@ -87,6 +143,50 @@ final class WinAPIEventDriverCore : EventDriverCore { { assert(false, "TODO!"); } + + private bool doProcessEvents(Duration max_wait) + { + import core.time : seconds; + import std.algorithm.comparison : min; + + bool got_event; + + if (max_wait > 0.seconds) { + DWORD timeout_msecs = max_wait == Duration.max ? INFINITE : cast(DWORD)min(max_wait.total!"msecs", DWORD.max); + auto ret = () @trusted { return MsgWaitForMultipleObjectsEx(cast(DWORD)m_registeredEvents.length, m_registeredEvents.ptr, + timeout_msecs, QS_ALLEVENTS, MWMO_ALERTABLE|MWMO_INPUTAVAILABLE); } (); + + /*if (ret == WAIT_OBJECT_0) { + got_event = true; + Win32TCPConnection[] to_remove; + foreach( fw; m_fileWriters.byKey ) + if( fw.testFileWritten() ) + to_remove ~= fw; + foreach( fw; to_remove ) + m_fileWriters.remove(fw); + }*/ + } + + MSG msg; + //uint cnt = 0; + while (() @trusted { return PeekMessageW(&msg, null, 0, 0, PM_REMOVE); } ()) { + if( msg.message == WM_QUIT ) { + m_exit = true; + return false; + } + () @trusted { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } (); + + got_event = true; + + // process timers every now and then so that they don't get stuck + //if (++cnt % 10 == 0) processTimers(); + } + + return got_event; + } } final class WinAPIEventDriverSockets : EventDriverSockets { @@ -386,3 +486,10 @@ final class WinAPIEventDriverWatchers : EventDriverWatchers { assert(false, "TODO!"); } } + +private long currStdTime() +@safe nothrow { + import std.datetime : Clock; + scope (failure) assert(false); + return Clock.currStdTime; +}