litespeed-quic/src/liblsquic/lsquic_alarmset.h

110 lines
3.2 KiB
C
Raw Normal View History

2022-05-06 16:49:46 +00:00
/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
2017-09-22 21:00:03 +00:00
/*
* lsquic_alarmset.h -- A set of alarms
*/
#ifndef LSQUIC_ALARM_H
#define LSQUIC_ALARM_H 1
#include "lsquic_int_types.h"
enum alarm_id;
struct lsquic_conn;
typedef void (*lsquic_alarm_cb_f)(enum alarm_id, void *cb_ctx,
2017-09-22 21:00:03 +00:00
lsquic_time_t expiry, lsquic_time_t now);
typedef struct lsquic_alarm {
lsquic_alarm_cb_f callback;
void *cb_ctx;
} lsquic_alarm_t;
enum alarm_id {
AL_HANDSHAKE,
AL_RETX_INIT,
AL_RETX_HSK = AL_RETX_INIT + PNS_HSK,
AL_RETX_APP = AL_RETX_INIT + PNS_APP,
2017-09-22 21:00:03 +00:00
AL_PING,
AL_MTU_PROBE,
2017-09-22 21:00:03 +00:00
AL_IDLE,
AL_ACK_APP,
AL_RET_CIDS,
AL_CID_THROT,
AL_PATH_CHAL,
AL_PATH_CHAL_0 = AL_PATH_CHAL,
AL_PATH_CHAL_1,
AL_PATH_CHAL_2,
AL_PATH_CHAL_3,
AL_SESS_TICKET,
AL_BLOCKED_KA, /* Blocked Keep-Alive */
AL_PACK_TOL, /* Calculate packet tolerance */
2017-09-22 21:00:03 +00:00
MAX_LSQUIC_ALARMS
};
enum alarm_id_bit {
ALBIT_HANDSHAKE = 1 << AL_HANDSHAKE,
ALBIT_RETX_INIT = 1 << AL_RETX_INIT,
ALBIT_RETX_HSK = 1 << AL_RETX_HSK,
ALBIT_RETX_APP = 1 << AL_RETX_APP,
ALBIT_ACK_APP = 1 << AL_ACK_APP,
2017-09-22 21:00:03 +00:00
ALBIT_PING = 1 << AL_PING,
ALBIT_IDLE = 1 << AL_IDLE,
ALBIT_RET_CIDS = 1 << AL_RET_CIDS,
ALBIT_CID_THROT = 1 << AL_CID_THROT,
ALBIT_PATH_CHAL_0 = 1 << AL_PATH_CHAL_0,
ALBIT_PATH_CHAL_1 = 1 << AL_PATH_CHAL_1,
ALBIT_PATH_CHAL_2 = 1 << AL_PATH_CHAL_2,
ALBIT_PATH_CHAL_3 = 1 << AL_PATH_CHAL_3,
ALBIT_SESS_TICKET = 1 << AL_SESS_TICKET,
ALBIT_BLOCKED_KA = 1 << AL_BLOCKED_KA,
ALBIT_MTU_PROBE = 1 << AL_MTU_PROBE,
ALBIT_PACK_TOL = 1 << AL_PACK_TOL,
2017-09-22 21:00:03 +00:00
};
typedef struct lsquic_alarmset {
enum alarm_id_bit as_armed_set;
lsquic_time_t as_expiry[MAX_LSQUIC_ALARMS];
const struct lsquic_conn *as_conn; /* Used for logging */
2017-09-22 21:00:03 +00:00
struct lsquic_alarm as_alarms[MAX_LSQUIC_ALARMS];
} lsquic_alarmset_t;
void
lsquic_alarmset_init (lsquic_alarmset_t *, const struct lsquic_conn *);
2017-09-22 21:00:03 +00:00
void
lsquic_alarmset_init_alarm (lsquic_alarmset_t *, enum alarm_id,
lsquic_alarm_cb_f, void *cb_ctx);
#define lsquic_alarmset_set(alarmset, al_id, exp) do { \
(alarmset)->as_armed_set |= 1 << (al_id); \
(alarmset)->as_expiry[al_id] = exp; \
} while (0)
#define lsquic_alarmset_unset(alarmset, al_id) do { \
(alarmset)->as_armed_set &= ~(1 << (al_id)); \
} while (0)
#define lsquic_alarmset_is_set(alarmset, al_id) \
((alarmset)->as_armed_set & (1 << (al_id)))
#define lsquic_alarmset_are_set(alarmset, flags) \
((alarmset)->as_armed_set & (flags))
#define lsquic_alarmset_is_inited(alarmset_, al_id_) ( \
(alarmset_)->as_alarms[al_id_].callback)
2017-09-22 21:00:03 +00:00
/* Timers "fire," alarms "ring." */
void
lsquic_alarmset_ring_expired (lsquic_alarmset_t *, lsquic_time_t now);
[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.
2018-04-09 13:39:38 +00:00
lsquic_time_t
lsquic_alarmset_mintime (const lsquic_alarmset_t *, enum alarm_id *);
extern const char *const lsquic_alid2str[];
[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.
2018-04-09 13:39:38 +00:00
2017-09-22 21:00:03 +00:00
#endif