quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

MULTI-EV.md (5472B)


      1 <!--
      2 Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      3 
      4 SPDX-License-Identifier: curl
      5 -->
      6 
      7 # Multi Event Based
      8 
      9 A libcurl multi is operating "event based" when the application uses
     10 and event library like `libuv` to monitor the sockets and file descriptors
     11 libcurl uses to trigger transfer operations. How that works from the
     12 applications point of view is described in libcurl-multi(3).
     13 
     14 This documents is about the internal handling.
     15 
     16 ## Source Locations
     17 
     18 All code related to event based handling is found in `lib/multi_ev.c`
     19 and `lib/multi_ev.h`. The header defines a set of internal functions
     20 and `struct curl_multi_ev` that is embedded in each multi handle.
     21 
     22 There is `Curl_multi_ev_init()` and `Curl_multi_ev_cleanup()` to manage
     23 the overall life cycle, call on creation and destruction of the multi
     24 handle.
     25 
     26 ## Tracking Events
     27 
     28 First, the various functions in `lib/multi_ev.h` only ever really do
     29 something when the libcurl application has registered its callback
     30 in `multi->socket_cb`.
     31 
     32 This is important as this callback gets informed about *changes* to sockets.
     33 When a new socket is added, an existing is removed, or the `POLLIN/OUT`
     34 flags change, `multi->socket_cb` needs to be invoked. `multi_ev` has to
     35 track what it already reported to detect changes.
     36 
     37 Most applications are expected to go "event based" right from the start,
     38 but the libcurl API does not prohibit an application to start another
     39 way and then go for events later on, even in the middle of a transfer.
     40 
     41 ### Transfer Events
     42 
     43 Most event that happen are in connection with a transfer. A transfer
     44 opens a connection, which opens a socket, and waits for this socket
     45 to become writable (`POLLOUT`) when using TCP, for example.
     46 
     47 The multi then calls `Curl_multi_ev_assess_xfer(multi, data)` to
     48 let the multi event code detect what sockets the transfer is interested in.
     49 If indeed a `multi->socket_cb` is set, the *current* transfer pollset is
     50 retrieved via `Curl_multi_getsock()`. This current pollset is then
     51 compared to the *previous* pollset. If relevant changes are detected,
     52 `multi->socket_cb` gets informed about those. These can be:
     53 
     54  * a socket is in the current set, but not the previous one
     55  * a socket was also in the previous one, but IN/OUT flags changed
     56  * a socket in the previous one is no longer part of the current
     57 
     58 `multi_ev.c` keeps a `struct mev_sh_entry` for each sockets in a hash
     59 with the socket as key. It tracks in each entry which transfers are
     60 interested in this particular socket. How many transfer want to read
     61 and/or write and what the summarized `POLLIN/POLLOUT` action, that
     62 had been reported to `multi->socket_cb` was.
     63 
     64 This is necessary as a socket may be in use by several transfers
     65 at the same time (think HTTP/2 on the same connection). When a transfer
     66 is done and gets removed from the socket entry, it decrements
     67 the reader and/or writer count (depending on what it was last
     68 interested in). This *may* result in the entry's summarized action
     69 to change, or not.
     70 
     71 ### Connection Events
     72 
     73 There are also events not connected to any transfer that need to be tracked.
     74 The multi connection cache, concerned with clean shutdowns of connections,
     75 is interested in socket events during the shutdown.
     76 
     77 To allow use of the libcurl infrastructure, the connection cache operates
     78 using an *internal* easy handle that is not a transfer as such. The
     79 internal handle is used for all connection shutdown operations, being tied
     80 to a particular connection only for a short time. This means tracking
     81 the last pollset for an internal handle is useless.
     82 
     83 Instead, the connection cache uses `Curl_multi_ev_assess_conn()` to have
     84 multi event handling check the connection and track a "last pollset"
     85 for the connection alone.
     86 
     87 ## Event Processing
     88 
     89 When the libcurl application is informed by the event library that
     90 a particular socket has an event, it calls `curl_multi_socket_action()`
     91 to make libcurl react to it. This internally invokes
     92 `Curl_multi_ev_expire_xfers()` which expires all transfers that
     93 are interested in the given socket, so the multi handle runs them.
     94 
     95 In addition `Curl_multi_ev_expire_xfers()` returns a `bool` to let
     96 the multi know that connections are also interested in the socket, so
     97 the connection pool should be informed as well.
     98 
     99 ## All Things Pass
    100 
    101 When a transfer is done, e.g. removed from its multi handle, the
    102 multi calls `Curl_multi_ev_xfer_done()`. This cleans up the pollset
    103 tracking for the transfer.
    104 
    105 When a connection is done, and before it is destroyed,
    106 `Curl_multi_ev_conn_done()` is called. This cleans up the pollset
    107 tracking for this connection.
    108 
    109 When a socket is about to be closed, `Curl_multi_ev_socket_done()`
    110 is called to cleanup the socket entry and all information kept there.
    111 
    112 These calls do not have to happen in any particular order. A transfer's
    113 socket may be around while the transfer is ongoing. Or it might disappear
    114 in the middle of things. Also, a transfer might be interested in several
    115 sockets at the same time (resolving, eye balling, ftp are all examples of
    116 those).
    117 
    118 ### And Come Again
    119 
    120 While transfer and connection identifier are practically unique in a
    121 libcurl application, sockets are not. Operating systems are keen on reusing
    122 their resources, and the next socket may get the same identifier as
    123 one just having been closed with high likelihood.
    124 
    125 This means that multi event handling needs to be informed *before* a close,
    126 clean up all its tracking and be ready to see that same socket identifier
    127 again right after.