-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathevent_loop.html
222 lines (182 loc) · 13.8 KB
/
event_loop.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
<html>
<head><title>Dasynq manual - event_loop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="content">
<h1>event_loop, event_loop_n, event_loop_th</h1>
<pre>
#include "dasynq.h"
namespace dasynq {
// generic event loop
template <typename T_Mutex> class event_loop;
// non-threadsafe event loop
typedef event_loop<null_mutex> event_loop_n;
// threadsafe event loop
typedef event_loop<std::mutex> event_loop_th;
}
</pre>
<p><b>Brief</b>: The <i class="code-name">event_loop</i> templates defines an event loop multiplexor which can be used to
listen or poll for events. A number of different types of event are handled, including I/O readiness on
file descriptors, POSIX signals, child process status changes, and timer expiry. To receive an event the
client first registers a <i>watcher</i> for the event and event source they are interested in, and then calls
the <i class="code-name">run</i> or <i class="code-name">poll</i> methods of the event loop. The <i>callback</i>
function of the watcher will be called during event processing to respond to the event.</p>
<h2>Template parameters</h2>
<ul>
<li><i class="code-name">T_Mutex</i> — specifies a mutex class to be used wherever a mutex is required. The
interface should conform to that of <i class="code-name">std::mutex</i>, with <i class="code-name">lock</i>,
<i class="code-name">unlock</i> and <i class="code-name">try_lock</i> functions.
<br><span class="note"><i>Note:</i> it is required
that the functions of <i class="code-name">T_Mutex</i> do not throw exceptions if used properly. Technically
<i class="code-name">std::mutex</i> may throw an exception if a system-level error occurs, but realistically this should only
occur in the case of application error (memory corruption, double lock etc). If it does, it will cause the
client application to terminate.</span></li>
</ul>
Additional template parameters are implementation internal.
<h2>Members</h2>
<div class="small-indent">
<h3>Constructors</h3>
<ul>
<li><i class="code-name">event_loop()</i> - default constructor; may throw <i class="code-name">std::bad_alloc</i>
or <i class="code-name">std::system_error</i>. See details section.</li>
<li><i class="code-name">event_loop(delayed_init) noexcept</i> - constructor for delayed initialisation. The event
loop instance must be initialised by calling the <i class="code-name">init</i> function before other functions
are called or watchers are registered.</li>
</ul>
<h3>Types</h3>
<ul>
<li><i class="code-name"><a href="#loop_traits_t">loop_traits_t</a></i> — loop traits.</li>
<li><i class="code-name">mutex_t</i> — alias for <i class="code-name">T_Mutex</i></li>
<li><i class="code-name"><a href="fd_watcher.html">fd_watcher</a></i> — a watcher type for watching file descriptor events</li>
<li><i class="code-name"><a href="fd_watcher.html#fd_watcher_impl">fd_watcher_impl</a></i> [template] — a template for implementing <i class="code-name"><a href="fd_watcher.html">fd_watcher</a></i> subclasses</li>
<li><i class="code-name"><a href="bidi_fd_watcher.html">bidi_fd_watcher</a></i> — a watcher type for
watching file descriptor events, supporting both input and output events simultaneously</li>
<li><i class="code-name"><a href="bidi_fd_watcher.html#bidi_fd_watcher_impl">bidi_fd_watcher_impl</a></i> [template] — a template for implementing
<i class="code-name"><a href="bidi_fd_watcher.html">bidi_fd_watcher</a></i> subclasses</li>
<li><i class="code-name"><a href="signal_watcher.html">signal_watcher</a></i> — a watcher type for watching POSIX signal reception</li>
<li><i class="code-name"><a href="signal_watcher.html#signal_watcher_impl">signal_watcher_impl</a></i> [template] — a template for implementing
<i class="code-name"><a href="signal_watcher.html">signal_watcher</a></i> subclasses</li>
<li><i class="code-name"><a href="child_proc_watcher.html">child_proc_watcher</a></i> — a watcher type for watching child process status changes</li>
<li><i class="code-name"><a href="child_proc_watcher.html#child_proc_watcher_impl">child_proc_watcher_impl</a></i> [template] — a template for implementing
<i class="code-name"><a href="child_proc_watcher.html">child_proc_watcher</a></i> subclasses</li>
<li><i class="code-name"><a href="timer.html">timer</a></i> — a timer implementation and watcher for timer expiry</li>
<li><i class="code-name"><a href="timer.html#timer_impl">timer_impl</a></i> [template] — a template for implementing <i class="code-name">timer</i> subclasses</li>
</ul>
<h3>Functions</h3>
<ul>
<li><i class="code-name">void init()</i> - initialise an event loop that has not yet been initialised (i.e.
which was constructed with the <i class="code-name">delayed_init</i> tag and for which <i class="code-name">init</i>
has not yet been called. Must not be called on an initialised event loop instance. May throw
<i class="code-name">std::bad_alloc</i> or <i class="code-name">std::system_error</i>.</li>
<li><i class="code-name">void run(int limit = -1) noexcept</i> - wait while there are no events pending;
queue and process pending events, up to the number specified by <i class="code-name">limit</i> (no
limit if the default value of -1 is specified).</li>
<li><i class="code-name">void poll(int limit = -1) noexcept</i> - queue and process any currently pending
events, up to the number specified by <i class="code-name">limit</i> (no limit if the default value of
-1 is specified).</li>
<li><i class="code-name">void get_time(timespec &ts, clock_type clock, bool force_update = false) noexcept</i><br>
<i class="code-name">void get_time(<a href="dasynq-namespace.html#time_val">time_val</a> &tv, clock_type clock, bool force_update = false) noexcept</i><br>
— get the current
time for the specified clock. The clock times may be cached for performance reasons; specify
<i class="code-name">force_update</i> as true to avoid using a cached value (in general this should not be necessary).</li>
</ul>
<h3>Destructor</h3>
<ul>
<li><i class="code-name">~event_loop()</i> — the destructor does nothing other than deallocate
internal resources. Note in particular that watchers do <i>not</i> have their <i class="code-name">watch_removed</i>
functions called.</li>
</ul>
</div>
<h2>Details and usage</h2>
<p>Instances can be constructed using the default constructor (which performs initialisation) or the delayed
initialisation constructor. If the delayed initialisation constructor is used, the instance remains uninitialised
until the <i class="code-name">init</i> function is called. Only one instance can be active (initialised) at a time
in a process; libraries that require event loop functionality are encouraged to allow an
<i class="code-name">event_loop reference</i> to be passed as a parameter from the application. Construction of a
second event loop while a first is still active has undefined behaviour.<br>
<span class="note"><i>Note:</i> This limitation is a consequence of limitations in the backends (signals require handlers to
be setup on some systems, and child process status requires watching for a signal).</span></p>
<p>Watchers for events of interest should be registered with the event loop, and their callbacks will be
notified when the events are processed. Event processing occurs only during execution of the
<i class="code-name">run()</i> and <i class="code-name">poll()</i> functions. Watcher callbacks can return a
<i class="code-name"><a href="dasynq-namespace.html#rearm">rearm</a></i> value to perform various actions, including to re-enable the watcher in order
to detect future events.</p>
<p>Most operations involving event watchers, such as registration, removing, enabling, and disabling, are performed via
the watcher instance; consult the documentation for the various watcher types. In general, watcher operations other
than registration can not fail and will not throw exceptions. This means that critical watchers can be registered early
and then enabled when needed; resource issues will be identified early, at the time of registration.</p>
<h3>Event loop initialisation</h3>
<p>Because the event loop may need to mask certain signals for the whole process, and because there is no
straight-forward way to do this once multiple threads have been created, the event loop must be initialised
in the initial application thread — or alternatively, these signals must be blocked by the
application on the initial thread (the signals in question are <i class="code-name">SIGCHLD</i> and
<i class="code-name">SIGALRM</i>). An event loop with a <i class="code-name">null_mutex</i> mutex type will
use <i class="code-name">sigprocmask</i> to mask signals and therefore has (according to POSIX) unpredictable behaviour in
multi-threaded applications; an event loop with any other mutex type will instead use
<i class="code-name">pthread_sigmask</i>, which requires linking against the pthreads library. It is
therefore not possible to use a single-threaded event loop in a multi-threaded program, even if it would be
used only from a single thread.</p>
<h3 id="watcher-constraints">Watcher constraints and state cycle</h3>
<p>Watchers are initially in a <i>waiting</i> state once enabled.
An enabled watcher for which a watched event is detected (during the execution of the <i class="code-name">run</i>
or <i class="code-name">poll</i> functions) is disabled and becomes <i>queued</i> for notification until its callback
is run; during execution of the callback, the watcher is in the <i>processing</i> state.</p>
<p>A watcher must not be re-enabled while in the <i>queued</i> or <i>processing</i> state, unless the
event loop will not be polled (via <i class="code-name">run</i> or <i class="code-name">poll</i>) before the associated
event is processed. For a client program which polls the event loop from multiple threads, this (roughly) means that
watchers should not be enabled, once they have become disabled, except by returning a suitable
<i class="code-name"><a href="dasynq-namespace.html#rearm">rearm</a></i> code from the callback.<br>
<span class="note"><i>Note:</i> Technically, the requirement is that polling must not be performed if an event may be detected
via an enabled watcher that is not in the <i>waiting</i> state.</span></p>
<p>Watchers may not be registered with more than one event loop at the same time (nor with the same loop more than once at the
same time).</p>
<h3>Event batching</h3>
<p>Events are queued and processed in batches. Watchers can be assigned a priority value and processing of
queued events occurs in priority order; however, incoming events currently do not cause additional
watchers to be queued while in the processing phase, meaning that events may be processed slightly out
of priority order. To mitigate this a processing batch size limit can be specified as an argument to the
<i class="code-name">run</i> or <i class="code-name">poll</i> function. A smaller batch limit gives more
accurate priority ordering, at a cost to throughput.</p>
<p>Note that watchers which re-queue immediately after processing (by returning <i class="code-name">rearm::REQUEUE</i>
from the callback function, or due to emulation mode of an <i class="code-name"><a href="fd_watcher.html">fd_watcher</a></i>
watching readiness state of a regular file) would cause an unlimited processing cycle if no specific batch limit was
specified; to mitigate this, the limit is reduced to the number of currently queued events, which should generally
prevent a watcher from being processed more than once. However, high-priority watchers which requeue themselves may still
be processed more than once since they will continue to jump to the start of the queue, and this may cause starvation
for lower priority watchers.</p>
<h3>Removal of watchers</h3>
<p>Watchers can be requested to be removed from an <i class="code-name">event_loop</i>, however removal does not necessarily
occur immediately (except for non-threadsafe event loops; for threadsafe event loops, any delay is due to the watcher
currently having its callback executed). All watcher types have a <i class="code-name">watch_removed</i> function:</p>
<pre>
virtual void watch_removed() noexcept
</pre>
<p>This function can be overridden by watch implementations and will be called once the watcher has been removed; at this
point it is safe to <i class="code-name">delete</i> the watcher. Naturally watcher instances must not end their lifetime
while they are registered to an event loop.</p>
<h3>Forking processes</h3>
<p>The event loop state in the child process after forking is unspecified. The event loop should be destroyed and
reconstructed in the child before it is used again.</p>
<h3>Signals</h3>
<p>The event loop may internally use certain signals, including <i class="code-name">SIGCHLD</i> and
<i class="code-name">SIGALRM</i>. Blocking or installing handlers for these signals may interfere with the
function of the event loop. Other signals should be safe for application use, although these can be handled
easily using the event loop's support via signal watchers.</p>
<hr>
<h2 id="loop_traits_t">loop_traits_t (dasynq::event_loop<T>::loop_traits_t)</h2>
<pre>
// Member of dasynq::event_loop
typedef <i>unspecified</i> loop_traits_t;
</pre>
<p><b>Brief</b>: this traits class provides details of the supported functionality of the event loop.</p>
<h2>Data members</h2>
<ul>
<li><i class="code-name">bool has_separate_rw_fd_watches</i> [static constexpr] — indicates whether the
loop requires read and write watches for the same file descriptor to be added separately.</li>
<li><i class="code-name">bool full_timer_support</i> [static constexpr] — if false, the event loop
might not differentiate between the system and monotonic clock, and timers against the system clock might
not expire at the correct time if the system time is altered after the timer is set.</li>
</ul>
</div>
</body></html>