skalibs

Mirror/fork of https://skarnet.org/software/skalibs/
git clone https://ccx.te2000.cz/git/skalibs
Log | Files | Refs | README | LICENSE

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>