[API Change, OPTIMIZATION] Only process conns that need to be processed

The API is simplified: do not expose the user code to several
queues.  A "connection queue" is now an internal concept.
The user processes connections using the single function
lsquic_engine_process_conns().  When this function is called,
only those connections are processed that need to be processed.
A connection needs to be processed when:

    1. New incoming packets have been fed to the connection.
    2. User wants to read from a stream that is readable.
    3. User wants to write to a stream that is writeable.
    4. There are buffered packets that can be sent out.  (This
       means that the user wrote to a stream outside of the
       lsquic library callback.)
    5. A control frame (such as BLOCKED) needs to be sent out.
    6. A stream needs to be serviced or delayed stream needs to
       be created.
    7. An alarm rings.
    8. Pacer timer expires.

To achieve this, the library places the connections into two
priority queues (min heaps):

    1. Tickable Queue; and
    2. Advisory Tick Time queue (ATTQ).

Each time lsquic_engine_process_conns() is called, the Tickable
Queue is emptied.  After the connections have been ticked, they are
queried again: if a connection is not being closed, it is placed
either in the Tickable Queue if it is ready to be ticked again or
it is placed in the Advisory Tick Time Queue.  It is assumed that
a connection always has at least one timer set (the idle alarm).

The connections in the Tickable Queue are arranged in the least
recently ticked order.  This lets connections that have been quiet
longer to get their packets scheduled first.

This change means that the library no longer needs to be ticked
periodically.  The user code can query the library when is the
next tick event and schedule it exactly.  When connections are
processed, only the tickable connections are processed, not *all*
the connections.  When there are no tick events, it means that no
timer event is necessary -- only the file descriptor READ event
is active.

The following are improvements and simplifications that have
been triggered:

    - Queue of connections with incoming packets is gone.
    - "Pending Read/Write Events" Queue is gone (along with its
      history and progress checks).  This queue has become the
      Tickable Queue.
    - The connection hash no longer needs to track the connection
      insertion order.
This commit is contained in:
Dmitri Tikhonov 2018-04-09 09:39:38 -04:00
parent eef4f2fcfb
commit e8bd737db4
27 changed files with 621 additions and 890 deletions

View file

@ -178,9 +178,6 @@ struct lsquic_stream_if {
/** By default, infinite loop checks are turned on */
#define LSQUIC_DF_PROGRESS_CHECK 1000
/** By default, Pending RW Queue infinite loop checks are turned on: */
#define LSQUIC_DF_PENDRW_CHECK 10
/** By default, read/write events are dispatched in a loop */
#define LSQUIC_DF_RW_ONCE 0
@ -331,23 +328,6 @@ struct lsquic_engine_settings {
*/
unsigned es_progress_check;
/**
* A non-zero value enables internal checks to identify suspected
* infinite loops in Pending RW Queue logic. The value of this
* setting is the number of times a connection on Pending RW Queue
* is allowed to be processed without making progress before it is
* banished from Pending RW Queue.
*
* Progress is considered to have happened if any of the following
* occurs:
* - User reads data, FIN, or new error (due to a reset) from a
* stream.
* - A new stream-related frame is packetized.
*
* The defaut value is @ref LSQUIC_DF_PENDRW_CHECK.
*/
unsigned es_pendrw_check;
/**
* A non-zero value make stream dispatch its read-write events once
* per call.
@ -364,19 +344,15 @@ struct lsquic_engine_settings {
/**
* If set, this value specifies that number of microseconds that
* functions @ref lsquic_engine_proc_all(),
* @ref lsquic_engine_process_conns_with_incoming(),
* @ref lsquic_engine_process_conns_to_tick(), and
* @ref lsquic_engine_process_conns_with_pend_rw() are allowed
* to spend before returning.
* @ref lsquic_engine_process_conns() is allowed to spend before
* returning.
*
* This is not an exact science and the connections must make
* progress, so the deadline is checked after all connections get
* a chance to tick and at least one batch of packets is sent out.
*
* When processing function runs out of its time slice, immediate
* calls to @ref lsquic_engine_has_pend_rw() and
* @ref lsquic_engine_has_unsent_packets() return false.
* calls to @ref lsquic_engine_has_unsent_packets() return false.
*
* The default value is @ref LSQUIC_DF_PROC_TIME_THRESH.
*/
@ -503,50 +479,11 @@ lsquic_engine_packet_in (lsquic_engine_t *,
void *peer_ctx);
/**
* Process all connections. This function must be called often enough so
* Process tickable connections. This function must be called often enough so
* that packets and connections do not expire.
*/
void
lsquic_engine_proc_all (lsquic_engine_t *engine);
/**
* Process connections that have incoming packets. Call this after adding
* one or more incoming packets using lsquic_engine_packet_in().
*/
void
lsquic_engine_process_conns_with_incoming (lsquic_engine_t *);
/**
* Process connections in Advisory Tick Time queue whose tick times are in
* the past.
*/
void
lsquic_engine_process_conns_to_tick (lsquic_engine_t *);
/**
* Returns true if engine has connections that have pending read or write
* events.
*
* Connections with pending read or write events are those that have at
* least one stream whose state changed outside of the regular callback
* mechanism. The simplest example is writing directly to the stream
* object when data comes in.
*
* A call to @ref lsquic_engine_proc_all,
* @ref lsquic_engine_process_conns_with_incoming,
* @ref lsquic_engine_process_conns_to_tick, or
* @ref lsquic_engine_process_conns_with_pend_rw removes processed connection
* from Pending RW queue.
*/
int
lsquic_engine_has_pend_rw (lsquic_engine_t *);
/**
* Process connections that have pending read or write events (@see
* lsquic_engine_has_pend_rw for description).
*/
void
lsquic_engine_process_conns_with_pend_rw (lsquic_engine_t *);
lsquic_engine_process_conns (lsquic_engine_t *engine);
/**
* Returns true if engine has some unsent packets. This happens if
@ -895,10 +832,10 @@ void
lsquic_conn_abort (lsquic_conn_t *c);
/**
* Returns true if there is a connection on the Advisory Tick Time queue,
* false otherwise. If true, `diff' is set to the difference between
* the earliest advisory tick time and now. If the former is in the past,
* the value of `diff' is negative.
* Returns true if there are connections to be processed, false otherwise.
* If true, `diff' is set to the difference between the earliest advisory
* tick time and now. If the former is in the past, the value of `diff'
* is negative.
*/
int
lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff);