eventcore/source/eventcore/drivers/posix/signals.d
2018-10-24 11:38:14 +02:00

121 lines
3.1 KiB
D

module eventcore.drivers.posix.signals;
@safe:
import eventcore.driver;
import eventcore.drivers.posix.driver;
import eventcore.internal.utils : nogc_assert;
import std.algorithm.comparison : among;
final class SignalFDEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals {
@safe: /*@nogc:*/ nothrow:
import core.stdc.errno : errno, EAGAIN, EINPROGRESS;
import core.sys.posix.signal;
import core.sys.posix.unistd : close, read, write;
import core.sys.linux.sys.signalfd;
private Loop m_loop;
this(Loop loop) @nogc { m_loop = loop; }
override SignalListenID listen(int sig, SignalCallback on_signal)
{
return listenInternal(sig, on_signal, false);
}
package SignalListenID listenInternal(int sig, SignalCallback on_signal, bool is_internal = true)
{
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 | SFD_CLOEXEC));
} ();
m_loop.initFD(cast(FD)fd, is_internal ? FDFlags.internal : FDFlags.none, SignalSlot(on_signal));
m_loop.registerFD(cast(FD)fd, EventMask.read);
m_loop.setNotifyCallback!(EventType.read)(cast(FD)fd, &onSignal);
onSignal(cast(FD)fd);
return fd;
}
override void addRef(SignalListenID descriptor)
{
assert(m_loop.m_fds[descriptor].common.refCount > 0, "Adding reference to unreferenced event FD.");
m_loop.m_fds[descriptor].common.refCount++;
}
override bool releaseRef(SignalListenID descriptor)
{
FD fd = cast(FD)descriptor;
nogc_assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD.");
if (--m_loop.m_fds[fd].common.refCount == 1) { // NOTE: 1 because setNotifyCallback adds a second reference
m_loop.setNotifyCallback!(EventType.read)(fd, null);
m_loop.unregisterFD(fd, EventMask.read);
m_loop.clearFD!SignalSlot(fd);
close(cast(int)fd);
return false;
}
return true;
}
private void onSignal(FD fd)
{
SignalListenID lid = cast(SignalListenID)fd;
signalfd_siginfo nfo;
do {
auto ret = () @trusted { return read(cast(int)fd, &nfo, nfo.sizeof); } ();
if (ret == -1 && errno.among!(EAGAIN, EINPROGRESS))
break;
auto cb = m_loop.m_fds[fd].signal.callback;
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].common.refCount > 0);
}
}
final class DummyEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals {
@safe: /*@nogc:*/ nothrow:
private Loop m_loop;
this(Loop loop) { m_loop = loop; }
override SignalListenID listen(int sig, SignalCallback on_signal)
{
return listenInternal(sig, on_signal, false);
}
package SignalListenID listenInternal(int sig, SignalCallback on_signal, bool is_internal = true)
{
assert(false);
}
override void addRef(SignalListenID descriptor)
{
assert(false);
}
override bool releaseRef(SignalListenID descriptor)
{
assert(false);
}
}
package struct SignalSlot {
alias Handle = SignalListenID;
SignalCallback callback;
}