1 module eventcore.drivers.posix.signals;
2 @safe:
3 
4 import eventcore.driver;
5 import eventcore.drivers.posix.driver;
6 import eventcore.internal.utils : nogc_assert;
7 
8 import std.algorithm.comparison : among;
9 
10 
11 final class SignalFDEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals {
12 @safe: /*@nogc:*/ nothrow:
13 	import core.stdc.errno : errno, EAGAIN, EINPROGRESS;
14 	import core.sys.posix.signal;
15 	import core.sys.posix.unistd : close, read, write;
16 	import core.sys.linux.sys.signalfd;
17 
18 	private Loop m_loop;
19 
20 	this(Loop loop) @nogc { m_loop = loop; }
21 
22 	override SignalListenID listen(int sig, SignalCallback on_signal)
23 	{
24 		return listenInternal(sig, on_signal, false);
25 	}
26 
27 	package SignalListenID listenInternal(int sig, SignalCallback on_signal, bool is_internal = true)
28 	{
29 		auto sigfd = () @trusted {
30 			sigset_t sset;
31 			sigemptyset(&sset);
32 			sigaddset(&sset, sig);
33 
34 			if (sigprocmask(SIG_BLOCK, &sset, null) != 0)
35 				return -1;
36 
37 			return signalfd(-1, &sset, SFD_NONBLOCK | SFD_CLOEXEC);
38 		} ();
39 
40 
41 		auto fd = m_loop.initFD!SignalListenID(sigfd, is_internal ? FDFlags.internal : FDFlags.none, SignalSlot(on_signal));
42 		m_loop.registerFD(cast(FD)fd, EventMask.read);
43 		m_loop.setNotifyCallback!(EventType.read)(cast(FD)fd, &onSignal);
44 
45 		onSignal(cast(FD)fd);
46 
47 		return fd;
48 	}
49 
50 	override bool isValid(SignalListenID handle)
51 	const {
52 		if (handle.value >= m_loop.m_fds.length) return false;
53 		return m_loop.m_fds[handle.value].common.validationCounter == handle.validationCounter;
54 	}
55 
56 	override void addRef(SignalListenID descriptor)
57 	{
58 		if (!isValid(descriptor)) return;
59 
60 		assert(m_loop.m_fds[descriptor].common.refCount > 0, "Adding reference to unreferenced event FD.");
61 		m_loop.m_fds[descriptor].common.refCount++;
62 	}
63 
64 	override bool releaseRef(SignalListenID descriptor)
65 	{
66 		if (!isValid(descriptor)) return true;
67 
68 		FD fd = cast(FD)descriptor;
69 		nogc_assert(m_loop.m_fds[fd].common.refCount > 0, "Releasing reference to unreferenced event FD.");
70 		if (--m_loop.m_fds[fd].common.refCount == 1) { // NOTE: 1 because setNotifyCallback adds a second reference
71 			m_loop.setNotifyCallback!(EventType.read)(fd, null);
72 			m_loop.unregisterFD(fd, EventMask.read);
73 			m_loop.clearFD!SignalSlot(fd);
74 			close(cast(int)fd);
75 			return false;
76 		}
77 		return true;
78 	}
79 
80 	private void onSignal(FD fd)
81 	{
82 		SignalListenID lid = cast(SignalListenID)fd;
83 		signalfd_siginfo nfo;
84 		do {
85 			auto ret = () @trusted { return read(cast(int)fd, &nfo, nfo.sizeof); } ();
86 			if (ret == -1 && errno.among!(EAGAIN, EINPROGRESS))
87 				break;
88 			auto cb = m_loop.m_fds[fd].signal.callback;
89 			if (ret != nfo.sizeof) {
90 				cb(lid, SignalStatus.error, -1);
91 				return;
92 			}
93 			addRef(lid);
94 			cb(lid, SignalStatus.ok, nfo.ssi_signo);
95 			releaseRef(lid);
96 		} while (m_loop.m_fds[fd].common.refCount > 0);
97 	}
98 }
99 
100 final class DummyEventDriverSignals(Loop : PosixEventLoop) : EventDriverSignals {
101 @safe: /*@nogc:*/ nothrow:
102 
103 	private Loop m_loop;
104 
105 	this(Loop loop) { m_loop = loop; }
106 
107 	override SignalListenID listen(int sig, SignalCallback on_signal)
108 	{
109 		return listenInternal(sig, on_signal, false);
110 	}
111 
112 	package SignalListenID listenInternal(int sig, SignalCallback on_signal, bool is_internal = true)
113 	{
114 		assert(false);
115 	}
116 
117 	override bool isValid(SignalListenID handle)
118 	const {
119 		return false;
120 	}
121 
122 	override void addRef(SignalListenID descriptor)
123 	{
124 		assert(false);
125 	}
126 
127 	override bool releaseRef(SignalListenID descriptor)
128 	{
129 		assert(false);
130 	}
131 }
132 
133 package struct SignalSlot {
134 	alias Handle = SignalListenID;
135 	SignalCallback callback;
136 }