Implement listening for signals based on signalfd().

This commit is contained in:
Sönke Ludwig 2016-10-16 21:06:11 +02:00
parent 0cce1123fc
commit bdaff3264f
4 changed files with 116 additions and 8 deletions

View file

@ -30,11 +30,13 @@ USDS | yes | yes | no | no
DNS | yes | yes | no | no DNS | yes | yes | no | no
Timers | yes | yes | no | no Timers | yes | yes | no | no
Events | yes | yes | no | no Events | yes | yes | no | no
Signals | no | no | no | no Signals | yes² | yes² | no | no
Files | yes | yes | no | no Files | yes | yes | no | no
UI Integration | no | no | no | no UI Integration | no | no | no | no
File watcher | no | no | no | no File watcher | no | no | no | no
² Currently only supported on Linux
Open questions Open questions
-------------- --------------

View file

@ -160,8 +160,18 @@ interface EventDriverEvents {
interface EventDriverSignals { interface EventDriverSignals {
@safe: /*@nogc:*/ nothrow: @safe: /*@nogc:*/ nothrow:
void wait(int sig, SignalCallback on_signal); SignalListenID listen(int sig, SignalCallback on_signal);
void cancelWait(int sig);
/** 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 { interface EventDriverTimers {
@ -212,7 +222,7 @@ alias DatagramIOCallback = void delegate(DatagramSocketFD, IOStatus, size_t, sco
alias DNSLookupCallback = void delegate(DNSLookupID, DNSStatus, scope Address[]); alias DNSLookupCallback = void delegate(DNSLookupID, DNSStatus, scope Address[]);
alias FileIOCallback = void delegate(FileFD, IOStatus, size_t); alias FileIOCallback = void delegate(FileFD, IOStatus, size_t);
alias EventCallback = void delegate(EventID); alias EventCallback = void delegate(EventID);
alias SignalCallback = void delegate(int); alias SignalCallback = void delegate(SignalListenID, SignalStatus, int);
alias TimerCallback = void delegate(TimerID); alias TimerCallback = void delegate(TimerID);
alias FileChangesCallback = void delegate(WatcherID, in FileChange[] changes); alias FileChangesCallback = void delegate(WatcherID, in FileChange[] changes);
@system alias DataInitializer = void function(void*); @system alias DataInitializer = void function(void*);
@ -284,6 +294,11 @@ enum FileChangeKind {
modified modified
} }
enum SignalStatus {
ok,
error
}
/** Describes a single change in a watched directory. /** Describes a single change in a watched directory.
*/ */
@ -328,4 +343,5 @@ alias EventID = Handle!("Event", FD);
alias TimerID = Handle!("Timer", int); alias TimerID = Handle!("Timer", int);
alias WatcherID = Handle!("Watcher", int); alias WatcherID = Handle!("Watcher", int);
alias EventWaitID = Handle!("EventWait", int); alias EventWaitID = Handle!("EventWait", int);
alias SignalListenID = Handle!("Signal", int);
alias DNSLookupID = Handle!("DNS", int); alias DNSLookupID = Handle!("DNS", int);

View file

@ -1055,18 +1055,71 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop) : EventDriverEvents {
final class PosixEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals { final class PosixEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals {
@safe: /*@nogc:*/ nothrow: @safe: /*@nogc:*/ nothrow:
import core.sys.posix.signal;
import core.sys.linux.sys.signalfd;
private Loop m_loop; private Loop m_loop;
this(Loop loop) { m_loop = 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);
} }
} }

37
tests/0-signal.d Normal file
View file

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