iopause.html (8917B)
1 <html> 2 <head> 3 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 5 <meta http-equiv="Content-Language" content="en" /> 6 <title>skalibs: the iopause library interface</title> 7 <meta name="Description" content="skalibs: the iopause library interface" /> 8 <meta name="Keywords" content="skalibs c unix iopause library libstddjb" /> 9 <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> --> 10 </head> 11 <body> 12 13 <p> 14 <a href="index.html">libstddjb</a><br /> 15 <a href="../libskarnet.html">libskarnet</a><br /> 16 <a href="../index.html">skalibs</a><br /> 17 <a href="//skarnet.org/software/">Software</a><br /> 18 <a href="//skarnet.org/">skarnet.org</a> 19 </p> 20 21 <h1> The <tt>iopause</tt> library interface </h1> 22 23 <p> 24 The following functions are declared in the <tt>skalibs/iopause.h</tt> header, 25 and implemented in the <tt>libskarnet.a</tt> or <tt>libskarnet.so</tt> library. 26 </p> 27 28 <h2> General information </h2> 29 30 <p> 31 <tt>iopause</tt> is the skalibs API for event loop selection. It's a 32 wrapper around the system's <tt>ppoll()</tt> or 33 <a href="https://www.opengroup.org/onlinepubs/9699919799/functions/poll.html">poll()</a> 34 (if available) or 35 <a href="https://www.opengroup.org/onlinepubs/9699919799/functions/select.html">select()</a> 36 (if neither <tt>ppoll()</tt> nor <tt>poll()</tt> is available) function. 37 It works around some system-dependent quirks; also it works with 38 <em>absolute dates</em> instead of timeouts. This is a good thing: 39 see below. 40 </p> 41 42 <p> 43 <tt>iopause</tt> is a derived work from Dan J. Bernstein's 44 <a href="https://cr.yp.to/lib/iopause.html">iopause</a> library, but the 45 skalibs implementation is subtly different. 46 </p> 47 48 <h2> Data structures </h2> 49 50 <p> 51 An <tt>iopause_fd</tt> structure is similar to a 52 <a href="https://www.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html">struct pollfd</a> 53 structure, and must be filled the same way. Usually, the user declares 54 an array of <tt>iopause_fd</tt> and fills it, one element per descriptor 55 to select on. If <em>x</em> is an <tt>iopause_fd</tt>: 56 </p> 57 58 <ul> 59 <li> <em>x</em>.fd must be set to the fd number to select on. </li> 60 <li> <em>x</em>.events must be a disjunction of the following flags: 61 <ul> 62 <li> IOPAUSE_READ if the fd is to be selected for reading. </li> 63 <li> IOPAUSE_WRITE if the fd is to be selected for writing. </li> 64 </ul> </li> 65 <li> When the selection function returns, <em>x</em>.revents contains 66 a disjunction of the following flags: 67 <ul> 68 <li> IOPAUSE_READ if the fd is readable (this includes reception of an EOF). </li> 69 <li> IOPAUSE_WRITE if the fd is writable. </li> 70 <li> IOPAUSE_EXCEPT if an exception (such as EOF or an error) occurred on the fd. </li> 71 </ul> </li> 72 </ul> 73 74 <p> 75 Unlike <tt>poll()</tt> or <tt>select()</tt>, which use a <em>timeout</em> 76 argument, the <tt>iopause()</tt> function uses a <em>deadline</em> argument, 77 i.e. an absolute time at which it must return 0 if no event has happened 78 so far, as well as a <em>stamp</em> argument, i.e. an absolute time meaning 79 <em>now</em>. Those arguments are stored in 80 <a href="tai.html">tain</a>s. Here is why: 81 </p> 82 83 <p> 84 The event loop pattern is mostly used to multiplex several asynchronous 85 events that can happen independently, at the same time or not. Let's 86 say you have 3 events, <em>x</em>, <em>y</em> and <em>z</em>. Each of 87 those has a separate timeout: if <em>x</em> happens before <em>x-timeout</em> 88 milliseconds, you call the <em>x-event-handler</em> function, but 89 if <em>x-timeout</em> milliseconds elapse without <em>x</em> happening, 90 you call <em>x-timeout-handler</em> function. And similarly with <em>y</em> 91 and <em>z</em>. 92 </p> 93 94 <p> 95 But the selection function returning does not mean <em>x</em> has happened 96 or that <em>x</em> has timed out. It might also mean that <em>y</em> has 97 happened, that <em>y</em> has timed out, that <em>z</em> has happened, that 98 <em>z</em> has timed out, or something else entirely. In the post-selection 99 part of the loop, the proper handler is called for the event or timeout 100 that has happened; then the loop is executed again, and in the 101 pre-selection part of the loop, the array describing the events is filled, 102 and the selection timeout is computed. 103 </p> 104 105 <p> 106 How are you going to compute that global selection timeout? Easy: it's the 107 shortest of the three. But we just spent some amount of time waiting, so the 108 individual timeouts must be recomputed! This means: 109 <ul> 110 <li> You need a way to know the time spent in a selection primitive, which 111 basically means getting a timestamp before the selection and once again 112 after the timestamp. </li> 113 <li> You need to recompute every individual timeout everytime you enter 114 the loop. </li> 115 </ul> 116 117 <p> 118 That is really cumbersome. A much simpler way of doing things is: 119 </p> 120 121 <ul> 122 <li> Always keep a reasonably accurate estimation of the current 123 time in a <em>stamp</em> variable. That means getting the current time 124 at the start of the process, and updating it <em>right after</em> any 125 action that takes a significant amount of time. When to update <em>stamp</em> 126 can be difficult to estimate in CPU-bound processes; fortunately, most 127 processes using an event loop are IO-bound, and the only actions that take 128 a non-negligible amount of time in an IO-bound process are the blocking 129 primitives. So, <em>stamp</em> must be updated <em>right after a selection 130 function returns</em>, and if the program has been correctly written and 131 cannot block anywhere else, it's the only place where it needs to be. </li> 132 <li> For every event, compute the <em>deadline</em> of that event: 133 <em>x-deadline</em> is <em>x-timeout</em> added to the current <em>stamp</em> 134 value when <em>x</em> enters the loop. This is done only once per event: 135 you never have to recompute event deadlines - unlike timeouts, which diminish 136 over time, deadlines do not change. </li> 137 <li> At every iteration, the selection deadline is the earliest of all the 138 available event deadlines. </li> 139 <li> As an added bonus: after the selection function returns and 140 <em>stamp</em> has been updated, it is easy to check which events have 141 timed out and which have not: <em>x</em> has timed out iff <em>x-deadline</em> 142 is earlier than <em>stamp</em>. </li> 143 </ul> 144 145 <p> 146 Maintaining a global timestamp and using absolute times instead of relative 147 times really is the right way to work with event loops, and the <tt>iopause</tt> 148 interface reflects that. Of course, you need a reliable, bug-free time library 149 and a monotonic, constant system clock to handle absolute times correctly; 150 that is why <tt>iopause</tt> relies on the <a href="tai.html">tai</a> library. 151 </p> 152 153 <h2> Functions </h2> 154 155 <p> 156 <code> int iopause (iopause_fd *x, unsigned int len, tain const *deadline, tain const *stamp) </code> <br /> 157 Blocks until one of the events described in the <em>x</em> array, of length 158 <em>len</em>, happens, or until the absolute date *<em>deadline</em> is 159 reached. <em>deadline</em> may be null, in which case the function blocks 160 indefinitely until an event happens. If <em>deadline</em> is not null, then 161 <em>stamp</em> must not be null, and must contain an accurate estimation 162 of the current time. The function returns the number of events that have 163 happened, 0 for a timeout, or -1 (and sets errno) for an error. 164 </p> 165 166 <p> 167 <code> int iopause_stamp (iopause_fd *x, unsigned int len, tain const *deadline, tain *stamp) </code> <br /> 168 Like <tt>iopause()</tt>, but if <em>stamp</em> is not null, it is updated 169 right before the function returns. This helps the user always keep a 170 reasonably accurate estimation of the current time in <em>stamp</em>; 171 it is recommended to use this function instead of the lower-level 172 <tt>iopause()</tt>. 173 </p> 174 175 <h3> Underlying implementations </h3> 176 177 <p> 178 <tt>iopause</tt> is an alias to one of <tt>iopause_ppoll</tt>, <tt>iopause_poll</tt> or 179 <tt>iopause_select</tt>. It is always aliased to <tt>iopause_ppoll</tt> if 180 the <tt>ppoll()</tt> function is available on the system; else, it's aliased to 181 <tt>iopause_poll</tt> by default, and users can alias it to <tt>iopause_select</tt> 182 instead if they configure skalibs with the <tt>--enable-iopause-select</tt> option. 183 </p> 184 185 <p> 186 <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html">poll()</a> 187 has a more comfortable API than 188 <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html">select()</a>, 189 but its maximum precision is 1 millisecond, which might not be enough for some applications; 190 using <tt>select()</tt> instead incurs some CPU overhead for the API conversion, but has a 191 1 microsecond precision. 192 <a href="https://man7.org/linux/man-pages/man2/poll.2.html">ppoll()</a> gets the best of 193 both worlds with the same interface model as <tt>poll()</tt> and a 1 nanosecond precision, 194 which is why skalibs always uses it when available. 195 </p> 196 197 </body> 198 </html>