diff --git a/README.md b/README.md index 3c93423..0fe3934 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,13 @@ USDS | yes | yes | no | no DNS | yes | yes | no | no Timers | yes | yes | no | no Events | yes | yes | no | no -Signals | no | no | no | no +Signals | yes² | yes² | no | no Files | yes | yes | no | no UI Integration | no | no | no | no File watcher | no | no | no | no +² Currently only supported on Linux + Open questions -------------- diff --git a/source/eventcore/driver.d b/source/eventcore/driver.d index 53210f3..d4afb02 100644 --- a/source/eventcore/driver.d +++ b/source/eventcore/driver.d @@ -160,8 +160,18 @@ interface EventDriverEvents { interface EventDriverSignals { @safe: /*@nogc:*/ nothrow: - void wait(int sig, SignalCallback on_signal); - void cancelWait(int sig); + SignalListenID listen(int sig, SignalCallback on_signal); + + /** Increments the reference count of the given resource. + */ + void addRef(SignalListenID descriptor); + + /** Decrements the reference count of the given resource. + + Once the reference count reaches zero, all associated resources will be + freed and the resource descriptor gets invalidated. + */ + void releaseRef(SignalListenID descriptor); } interface EventDriverTimers { @@ -212,7 +222,7 @@ alias DatagramIOCallback = void delegate(DatagramSocketFD, IOStatus, size_t, sco alias DNSLookupCallback = void delegate(DNSLookupID, DNSStatus, scope Address[]); alias FileIOCallback = void delegate(FileFD, IOStatus, size_t); alias EventCallback = void delegate(EventID); -alias SignalCallback = void delegate(int); +alias SignalCallback = void delegate(SignalListenID, SignalStatus, int); alias TimerCallback = void delegate(TimerID); alias FileChangesCallback = void delegate(WatcherID, in FileChange[] changes); @system alias DataInitializer = void function(void*); @@ -284,6 +294,11 @@ enum FileChangeKind { modified } +enum SignalStatus { + ok, + error +} + /** Describes a single change in a watched directory. */ @@ -328,4 +343,5 @@ alias EventID = Handle!("Event", FD); alias TimerID = Handle!("Timer", int); alias WatcherID = Handle!("Watcher", int); alias EventWaitID = Handle!("EventWait", int); +alias SignalListenID = Handle!("Signal", int); alias DNSLookupID = Handle!("DNS", int); diff --git a/source/eventcore/drivers/posix.d b/source/eventcore/drivers/posix.d index 62b79d4..8f0a661 100644 --- a/source/eventcore/drivers/posix.d +++ b/source/eventcore/drivers/posix.d @@ -1055,18 +1055,71 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop) : EventDriverEvents { final class PosixEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals { @safe: /*@nogc:*/ nothrow: + import core.sys.posix.signal; + import core.sys.linux.sys.signalfd; + private Loop m_loop; this(Loop loop) { m_loop = loop; } - final override void wait(int sig, SignalCallback on_signal) + override SignalListenID listen(int sig, SignalCallback on_signal) { - assert(false, "TODO!"); + auto fd = () @trusted { + sigset_t sset; + sigemptyset(&sset); + sigaddset(&sset, sig); + + if (sigprocmask(SIG_BLOCK, &sset, null) != 0) + return SignalListenID.invalid; + + return SignalListenID(signalfd(-1, &sset, SFD_NONBLOCK)); + } (); + + + m_loop.initFD(cast(FD)fd); + m_loop.m_fds[fd].readCallback = () @trusted { return cast(IOCallback)on_signal; } (); // FIXME: avoid unsafe cast + m_loop.registerFD(cast(FD)fd, EventMask.read); + m_loop.setNotifyCallback!(EventType.read)(cast(FD)fd, &onSignal); + + onSignal(cast(FD)fd); + + return fd; } - final override void cancelWait(int sig) + override void addRef(SignalListenID descriptor) { - assert(false, "TODO!"); + assert(m_loop.m_fds[descriptor].refCount > 0, "Adding reference to unreferenced event FD."); + m_loop.m_fds[descriptor].refCount++; + } + + override void releaseRef(SignalListenID descriptor) + { + FD fd = cast(FD)descriptor; + assert(m_loop.m_fds[fd].refCount > 0, "Releasing reference to unreferenced event FD."); + if (--m_loop.m_fds[fd].refCount == 0) { + m_loop.unregisterFD(fd); + m_loop.clearFD(fd); + close(fd); + } + } + + private void onSignal(FD fd) + { + SignalListenID lid = cast(SignalListenID)fd; + auto cb = () @trusted { return cast(SignalCallback)m_loop.m_fds[fd].readCallback; } (); + signalfd_siginfo nfo; + do { + auto ret = () @trusted { return read(fd, &nfo, nfo.sizeof); } (); + if (ret == -1 && errno == EAGAIN) + break; + if (ret != nfo.sizeof) { + cb(lid, SignalStatus.error, -1); + return; + } + addRef(lid); + cb(lid, SignalStatus.ok, nfo.ssi_signo); + releaseRef(lid); + } while (m_loop.m_fds[fd].refCount > 0); } } diff --git a/tests/0-signal.d b/tests/0-signal.d new file mode 100644 index 0000000..79379ae --- /dev/null +++ b/tests/0-signal.d @@ -0,0 +1,37 @@ +/++ dub.sdl: + name "test" + dependency "eventcore" path=".." ++/ +module test; + +import eventcore.core; +import std.stdio : writefln; +import core.stdc.signal; +import core.sys.posix.signal : SIGRTMIN; +import core.time : msecs; + +bool s_done; + +void main() +{ + auto id = eventDriver.signals.listen(SIGRTMIN+1, (id, status, sig) { + assert(!s_done); + assert(status == SignalStatus.ok); + assert(sig == () @trusted { return SIGRTMIN+1; } ()); + s_done = true; + eventDriver.core.exit(); + }); + + auto tm = eventDriver.timers.create(); + eventDriver.timers.set(tm, 500.msecs); + eventDriver.timers.wait(tm, (tm) { + () @trusted { raise(SIGRTMIN+1); } (); + }); + + ExitReason er; + do er = eventDriver.core.processEvents(); + while (er == ExitReason.idle); + //assert(er == ExitReason.outOfWaiters); // FIXME: see above + assert(s_done); + s_done = false; +}