Release 1.18.0

- [API Change] Can specify clock granularity in engine settings.
- [BUGFIX] Pacer uses fixed clock granularity.  Since the change on
  2018-04-09, it is not necessary to try to approximate the next tick
  time in the pacer: it can use fix clock granularity specified by
  the user.
- [BUGFIX] Do not tick constantly before handshake is done.
- [BUGFIX] Do not exit tick prematurely: reuse packet with ACK.  Even
  if we cannot allocate *more* packets, we could still be able to write
  to one already allocated.
- [BUGFIX] Do not schedule pacer if there are no lost packets.
This commit is contained in:
Dmitri Tikhonov 2019-01-28 15:41:28 -05:00
parent 1b3a179789
commit 6aba801d42
18 changed files with 99 additions and 142 deletions

View file

@ -1,3 +1,16 @@
2019-01-28
- 1.18.0
- [API Change] Can specify clock granularity in engine settings.
- [BUGFIX] Pacer uses fixed clock granularity. Since the change on
2018-04-09, it is not necessary to try to approximate the next tick
time in the pacer: it can use fix clock granularity specified by
the user.
- [BUGFIX] Do not tick constantly before handshake is done.
- [BUGFIX] Do not exit tick prematurely: reuse packet with ACK. Even
if we cannot allocate *more* packets, we could still be able to write
to one already allocated.
- [BUGFIX] Do not schedule pacer if there are no lost packets.
2019-01-17 2019-01-17
- 1.17.15 - 1.17.15
- [BUGFIX] http_client: make sure only one read per on_read() callback - [BUGFIX] http_client: make sure only one read per on_read() callback

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 1 #define LSQUIC_MAJOR_VERSION 1
#define LSQUIC_MINOR_VERSION 17 #define LSQUIC_MINOR_VERSION 18
#define LSQUIC_PATCH_VERSION 15 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -245,6 +245,9 @@ struct lsquic_stream_if {
/** By default, packets are paced */ /** By default, packets are paced */
#define LSQUIC_DF_PACE_PACKETS 1 #define LSQUIC_DF_PACE_PACKETS 1
/** Default clock granularity is 1000 microseconds */
#define LSQUIC_DF_CLOCK_GRANULARITY 1000
struct lsquic_engine_settings { struct lsquic_engine_settings {
/** /**
* This is a bit mask wherein each bit corresponds to a value in * This is a bit mask wherein each bit corresponds to a value in
@ -432,6 +435,11 @@ struct lsquic_engine_settings {
*/ */
int es_pace_packets; int es_pace_packets;
/**
* Clock granularity information is used by the pacer. The value
* is in microseconds; default is @ref LSQUIC_DF_CLOCK_GRANULARITY.
*/
unsigned es_clock_granularity;
}; };
/* Initialize `settings' to default values */ /* Initialize `settings' to default values */

View file

@ -219,6 +219,7 @@ lsquic_engine_init_settings (struct lsquic_engine_settings *settings,
settings->es_rw_once = LSQUIC_DF_RW_ONCE; settings->es_rw_once = LSQUIC_DF_RW_ONCE;
settings->es_proc_time_thresh= LSQUIC_DF_PROC_TIME_THRESH; settings->es_proc_time_thresh= LSQUIC_DF_PROC_TIME_THRESH;
settings->es_pace_packets = LSQUIC_DF_PACE_PACKETS; settings->es_pace_packets = LSQUIC_DF_PACE_PACKETS;
settings->es_clock_granularity = LSQUIC_DF_CLOCK_GRANULARITY;
} }

View file

@ -124,7 +124,7 @@ void
lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t cid, uint32_t stream_id, lsquic_ev_log_rst_stream_frame_in (lsquic_cid_t cid, uint32_t stream_id,
uint64_t offset, uint32_t error_code) uint64_t offset, uint32_t error_code)
{ {
LCID("RST_FRAME frame in: error code %"PRIu32", stream %"PRIu32 LCID("RST_STREAM frame in: error code %"PRIu32", stream %"PRIu32
", offset: %"PRIu64, error_code, stream_id, offset); ", offset: %"PRIu64, error_code, stream_id, offset);
} }

View file

@ -1877,7 +1877,7 @@ static unsigned
process_packet_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in, process_packet_frame (struct full_conn *conn, lsquic_packet_in_t *packet_in,
const unsigned char *p, size_t len) const unsigned char *p, size_t len)
{ {
enum QUIC_FRAME_TYPE type = conn->fc_conn.cn_pf->pf_parse_frame_type(p[0]); enum quic_frame_type type = conn->fc_conn.cn_pf->pf_parse_frame_type(p[0]);
packet_in->pi_frame_types |= 1 << type; packet_in->pi_frame_types |= 1 << type;
recent_packet_hist_frames(conn, 0, 1 << type); recent_packet_hist_frames(conn, 0, 1 << type);
return process_frames[type](conn, packet_in, p, len); return process_frames[type](conn, packet_in, p, len);
@ -2954,8 +2954,6 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now)
if (n > 0) if (n > 0)
CLOSE_IF_NECESSARY(); CLOSE_IF_NECESSARY();
RETURN_IF_OUT_OF_PACKETS();
if (conn->fc_conn.cn_flags & LSCONN_SEND_BLOCKED) if (conn->fc_conn.cn_flags & LSCONN_SEND_BLOCKED)
{ {
if (generate_blocked_frame(conn, 0)) if (generate_blocked_frame(conn, 0))
@ -3531,9 +3529,21 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn)
return 1; return 1;
if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams)) if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams))
return 1; return 1;
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams, next_write_stream) if (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
if (lsquic_stream_write_avail(stream)) {
return 1; TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
next_write_stream)
if (lsquic_stream_write_avail(stream))
return 1;
}
else
{
TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
next_write_stream)
if (LSQUIC_STREAM_HANDSHAKE == stream->id
&& lsquic_stream_write_avail(stream))
return 1;
}
} }
TAILQ_FOREACH(stream, &conn->fc_pub.read_streams, next_read_stream) TAILQ_FOREACH(stream, &conn->fc_pub.read_streams, next_read_stream)

View file

@ -2,9 +2,6 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdint.h> #include <stdint.h>
#ifndef NDEBUG
#include <stdlib.h> /* getenv */
#endif
#include <string.h> #include <string.h>
#ifdef WIN32 #ifdef WIN32
#include <vc_compat.h> #include <vc_compat.h>
@ -27,20 +24,12 @@
void void
pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned max_intertick) pacer_init (struct pacer *pacer, lsquic_cid_t cid, unsigned clock_granularity)
{ {
memset(pacer, 0, sizeof(*pacer)); memset(pacer, 0, sizeof(*pacer));
pacer->pa_burst_tokens = 10; pacer->pa_burst_tokens = 10;
pacer->pa_cid = cid; pacer->pa_cid = cid;
pacer->pa_max_intertick = max_intertick; pacer->pa_clock_granularity = clock_granularity;
#ifndef NDEBUG
const char *val;
if ((val = getenv("LSQUIC_PACER_INTERTICK")))
{
pacer->pa_flags |= PA_CONSTANT_INTERTICK;
pacer->pa_intertick_avg = atoi(val);
}
#endif
} }
@ -113,13 +102,6 @@ pacer_loss_event (struct pacer *pacer)
} }
static unsigned
clock_granularity (const struct pacer *pacer)
{
return pacer->pa_intertick_avg;
}
int int
pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight) pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
{ {
@ -127,7 +109,7 @@ pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
if (pacer->pa_burst_tokens > 0 || n_in_flight == 0) if (pacer->pa_burst_tokens > 0 || n_in_flight == 0)
can = 1; can = 1;
else if (pacer->pa_next_sched > pacer->pa_now + clock_granularity(pacer)) else if (pacer->pa_next_sched > pacer->pa_now + pacer->pa_clock_granularity)
{ {
pacer->pa_flags |= PA_LAST_SCHED_DELAYED; pacer->pa_flags |= PA_LAST_SCHED_DELAYED;
can = 0; can = 0;
@ -140,52 +122,9 @@ pacer_can_schedule (struct pacer *pacer, unsigned n_in_flight)
} }
#define ALPHA_SHIFT 3
#define BETA_SHIFT 2
static void
update_avg_intertick (struct pacer *pacer, unsigned intertick)
{
unsigned diff;
#ifndef NDEBUG
if (pacer->pa_flags & PA_CONSTANT_INTERTICK)
return;
#endif
if (pacer->pa_intertick_avg)
{
if (intertick > pacer->pa_intertick_avg)
diff = intertick - pacer->pa_intertick_avg;
else
diff = pacer->pa_intertick_avg - intertick;
pacer->pa_intertick_var -= pacer->pa_intertick_var >> BETA_SHIFT;
pacer->pa_intertick_var += diff >> BETA_SHIFT;
pacer->pa_intertick_avg -= pacer->pa_intertick_avg >> ALPHA_SHIFT;
pacer->pa_intertick_avg += intertick >> ALPHA_SHIFT;
}
else
{
pacer->pa_intertick_avg = intertick;
pacer->pa_intertick_var = intertick >> 1;
}
}
void void
pacer_tick (struct pacer *pacer, lsquic_time_t now) pacer_tick (struct pacer *pacer, lsquic_time_t now)
{ {
unsigned intertick;
assert(now >= pacer->pa_now); assert(now >= pacer->pa_now);
if (pacer->pa_now)
{
assert(now - pacer->pa_now < (1ULL << sizeof(unsigned) * 8));
intertick = now - pacer->pa_now;
LSQ_DEBUG("intertick estimate: %u; real value: %u; error: %d",
clock_granularity(pacer), intertick,
(int) clock_granularity(pacer) - (int) intertick);
update_avg_intertick(pacer, intertick);
}
pacer->pa_now = now; pacer->pa_now = now;
} }

View file

@ -11,23 +11,12 @@ struct pacer
/* All tick times are in microseconds */ /* All tick times are in microseconds */
unsigned pa_max_intertick; /* Maximum intertick time */ unsigned pa_clock_granularity;
/* We keep an average of intertick times, which is our best estimate unsigned pa_burst_tokens;
* for the time when the connection ticks next. This estimate is used
* to see whether a packet can be scheduled or not.
*/
unsigned pa_intertick_avg; /* Smoothed average */
unsigned pa_intertick_var; /* Variance */
unsigned short pa_packet_size;
unsigned char pa_burst_tokens;
enum { enum {
PA_LAST_SCHED_DELAYED = (1 << 0), PA_LAST_SCHED_DELAYED = (1 << 0),
#ifndef NDEBUG } pa_flags;
PA_CONSTANT_INTERTICK = (1 << 1), /* Use fake intertick time for testing */
#endif
} pa_flags:8;
#ifndef NDEBUG #ifndef NDEBUG
struct { struct {
unsigned n_scheduled; unsigned n_scheduled;
@ -39,7 +28,7 @@ struct pacer
typedef lsquic_time_t (*tx_time_f)(void *ctx); typedef lsquic_time_t (*tx_time_f)(void *ctx);
void void
pacer_init (struct pacer *, lsquic_cid_t, unsigned max_intertick); pacer_init (struct pacer *, lsquic_cid_t, unsigned clock_granularity);
void void
pacer_cleanup (struct pacer *); pacer_cleanup (struct pacer *);

View file

@ -16,7 +16,7 @@ enum PACKET_PUBLIC_FLAGS
PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7, PACKET_PUBLIC_FLAGS_TWO_OR_MORE_BYTES = 1 << 7,
}; };
enum QUIC_FRAME_TYPE enum quic_frame_type
{ {
QUIC_FRAME_INVALID, QUIC_FRAME_INVALID,

View file

@ -114,7 +114,7 @@ int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out, lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm, struct lsquic_mm *mm,
struct lsquic_stream *new_stream, struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE frame_type, enum quic_frame_type frame_type,
unsigned short off, unsigned short len) unsigned short off, unsigned short len)
{ {
struct stream_rec_arr *srec_arr; struct stream_rec_arr *srec_arr;
@ -348,23 +348,6 @@ lsquic_packet_out_chop_regen (lsquic_packet_out_t *packet_out)
} }
int
lsquic_packet_out_has_hsk_frames (struct lsquic_packet_out *packet_out)
{
struct packet_out_srec_iter posi;
struct stream_rec *srec;
for (srec = posi_first(&posi, packet_out); srec; srec = posi_next(&posi))
if (srec->sr_frame_type == QUIC_FRAME_STREAM
&& LSQUIC_STREAM_HANDSHAKE == srec->sr_stream->id)
{
return 1;
}
return 0;
}
void void
lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out) lsquic_packet_out_ack_streams (lsquic_packet_out_t *packet_out)
{ {

View file

@ -38,7 +38,7 @@ struct stream_rec {
struct lsquic_stream *sr_stream; struct lsquic_stream *sr_stream;
unsigned short sr_off, unsigned short sr_off,
sr_len; sr_len;
enum QUIC_FRAME_TYPE sr_frame_type:16; enum quic_frame_type sr_frame_type:16;
}; };
#define srec_taken(srec) ((srec)->sr_frame_type) #define srec_taken(srec) ((srec)->sr_frame_type)
@ -214,7 +214,7 @@ int
lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out, lsquic_packet_out_add_stream (lsquic_packet_out_t *packet_out,
struct lsquic_mm *mm, struct lsquic_mm *mm,
struct lsquic_stream *new_stream, struct lsquic_stream *new_stream,
enum QUIC_FRAME_TYPE, enum quic_frame_type,
unsigned short off, unsigned short len); unsigned short off, unsigned short len);
unsigned unsigned
@ -227,9 +227,6 @@ lsquic_packet_out_split_in_two (struct lsquic_mm *, lsquic_packet_out_t *,
void void
lsquic_packet_out_chop_regen (lsquic_packet_out_t *); lsquic_packet_out_chop_regen (lsquic_packet_out_t *);
int
lsquic_packet_out_has_hsk_frames (struct lsquic_packet_out *);
void void
lsquic_packet_out_ack_streams (struct lsquic_packet_out *); lsquic_packet_out_ack_streams (struct lsquic_packet_out *);

View file

@ -71,7 +71,7 @@ struct parse_funcs
void void
(*pf_parse_packet_in_finish) (struct lsquic_packet_in *packet_in, (*pf_parse_packet_in_finish) (struct lsquic_packet_in *packet_in,
struct packin_parse_state *); struct packin_parse_state *);
enum QUIC_FRAME_TYPE enum quic_frame_type
(*pf_parse_frame_type) (unsigned char); (*pf_parse_frame_type) (unsigned char);
/* Return used buffer length or a negative value if there was not enough /* Return used buffer length or a negative value if there was not enough
* room to write the stream frame. In the latter case, the negative of * room to write the stream frame. In the latter case, the negative of
@ -186,7 +186,7 @@ int
lsquic_iquic_parse_packet_in_short_begin (struct lsquic_packet_in *, lsquic_iquic_parse_packet_in_short_begin (struct lsquic_packet_in *,
size_t length, int is_server, struct packin_parse_state *state); size_t length, int is_server, struct packin_parse_state *state);
enum QUIC_FRAME_TYPE enum quic_frame_type
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte); parse_frame_type_gquic_Q035_thru_Q039 (unsigned char first_byte);
size_t size_t

View file

@ -137,7 +137,7 @@ lsquic_gquic_parse_packet_in_begin (struct lsquic_packet_in *packet_in,
} }
static const enum QUIC_FRAME_TYPE byte2frame_type_Q035_thru_Q039[0x100] = static const enum quic_frame_type byte2frame_type_Q035_thru_Q039[0x100] =
{ {
[0x00] = QUIC_FRAME_PADDING, [0x00] = QUIC_FRAME_PADDING,
[0x01] = QUIC_FRAME_RST_STREAM, [0x01] = QUIC_FRAME_RST_STREAM,
@ -398,7 +398,7 @@ static const enum QUIC_FRAME_TYPE byte2frame_type_Q035_thru_Q039[0x100] =
}; };
enum QUIC_FRAME_TYPE enum quic_frame_type
parse_frame_type_gquic_Q035_thru_Q039 (unsigned char b) parse_frame_type_gquic_Q035_thru_Q039 (unsigned char b)
{ {
return byte2frame_type_Q035_thru_Q039[b]; return byte2frame_type_Q035_thru_Q039[b];

View file

@ -252,7 +252,8 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
lsquic_senhist_init(&ctl->sc_senhist); lsquic_senhist_init(&ctl->sc_senhist);
lsquic_cubic_init(&ctl->sc_cubic, LSQUIC_LOG_CONN_ID); lsquic_cubic_init(&ctl->sc_cubic, LSQUIC_LOG_CONN_ID);
if (ctl->sc_flags & SC_PACE) if (ctl->sc_flags & SC_PACE)
pacer_init(&ctl->sc_pacer, LSQUIC_LOG_CONN_ID, 100000); pacer_init(&ctl->sc_pacer, LSQUIC_LOG_CONN_ID,
enpub->enp_settings.es_clock_granularity);
for (i = 0; i < sizeof(ctl->sc_buffered_packets) / for (i = 0; i < sizeof(ctl->sc_buffered_packets) /
sizeof(ctl->sc_buffered_packets[0]); ++i) sizeof(ctl->sc_buffered_packets[0]); ++i)
TAILQ_INIT(&ctl->sc_buffered_packets[i].bpq_packets); TAILQ_INIT(&ctl->sc_buffered_packets[i].bpq_packets);
@ -833,18 +834,32 @@ lsquic_send_ctl_smallest_unacked (lsquic_send_ctl_t *ctl)
static struct lsquic_packet_out * static struct lsquic_packet_out *
send_ctl_next_lost (lsquic_send_ctl_t *ctl) send_ctl_next_lost (lsquic_send_ctl_t *ctl)
{ {
lsquic_packet_out_t *lost_packet = TAILQ_FIRST(&ctl->sc_lost_packets); struct lsquic_packet_out *lost_packet;
get_next_lost:
lost_packet = TAILQ_FIRST(&ctl->sc_lost_packets);
if (lost_packet) if (lost_packet)
{ {
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
if (lost_packet->po_frame_types & (1 << QUIC_FRAME_STREAM)) if (lost_packet->po_frame_types & (1 << QUIC_FRAME_STREAM))
{ {
lsquic_packet_out_elide_reset_stream_frames(lost_packet, 0); lsquic_packet_out_elide_reset_stream_frames(lost_packet, 0);
if (lost_packet->po_regen_sz >= lost_packet->po_data_sz)
{
LSQ_DEBUG("Dropping packet %"PRIu64" from lost queue",
lost_packet->po_packno);
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
send_ctl_destroy_packet(ctl, lost_packet);
goto get_next_lost;
}
} }
return lost_packet;
if (!lsquic_send_ctl_can_send(ctl))
return NULL;
TAILQ_REMOVE(&ctl->sc_lost_packets, lost_packet, po_next);
} }
else
return NULL; return lost_packet;
} }
@ -1117,9 +1132,6 @@ lsquic_send_ctl_next_packet_to_send (lsquic_send_ctl_t *ctl)
{ {
if (packet_out->po_regen_sz < packet_out->po_data_sz) if (packet_out->po_regen_sz < packet_out->po_data_sz)
{ {
#if LSQUIC_CONN_STATS
++ctl->sc_conn_pub->conn_stats->out.retx_packets;
#endif
update_for_resending(ctl, packet_out); update_for_resending(ctl, packet_out);
packet_out->po_flags &= ~PO_REPACKNO; packet_out->po_flags &= ~PO_REPACKNO;
} }
@ -1304,21 +1316,15 @@ lsquic_send_ctl_reschedule_packets (lsquic_send_ctl_t *ctl)
lsquic_packet_out_t *packet_out; lsquic_packet_out_t *packet_out;
unsigned n = 0; unsigned n = 0;
while (lsquic_send_ctl_can_send(ctl) && while ((packet_out = send_ctl_next_lost(ctl)))
(packet_out = send_ctl_next_lost(ctl)))
{ {
if (packet_out->po_regen_sz < packet_out->po_data_sz) assert(packet_out->po_regen_sz < packet_out->po_data_sz);
{ ++n;
++n; #if LSQUIC_CONN_STATS
update_for_resending(ctl, packet_out); ++ctl->sc_conn_pub->conn_stats->out.retx_packets;
lsquic_send_ctl_scheduled_one(ctl, packet_out); #endif
} update_for_resending(ctl, packet_out);
else lsquic_send_ctl_scheduled_one(ctl, packet_out);
{
LSQ_DEBUG("Dropping packet %"PRIu64" from unacked queue",
packet_out->po_packno);
send_ctl_destroy_packet(ctl, packet_out);
}
} }
if (n) if (n)

View file

@ -1088,6 +1088,7 @@ main (int argc, char **argv)
fprintf(stats_fh, "%.2Lf reqs/sec; %.0Lf bytes/sec\n", fprintf(stats_fh, "%.2Lf reqs/sec; %.0Lf bytes/sec\n",
(long double) s_stat_req.n / elapsed, (long double) s_stat_req.n / elapsed,
(long double) s_stat_downloaded_bytes / elapsed); (long double) s_stat_downloaded_bytes / elapsed);
fprintf(stats_fh, "read handler count %lu\n", prog.prog_read_count);
} }
prog_cleanup(&prog); prog_cleanup(&prog);

View file

@ -151,6 +151,10 @@ prog_print_common_options (const struct prog *prog, FILE *out)
} }
fprintf(out,
" -i USECS Clock granularity in microseconds. Defaults to %u.\n",
LSQUIC_DF_CLOCK_GRANULARITY
);
fprintf(out, fprintf(out,
" -h Print this help screen and exit\n" " -h Print this help screen and exit\n"
); );
@ -193,6 +197,9 @@ prog_set_opt (struct prog *prog, int opt, const char *arg)
case 'o': case 'o':
return set_engine_option(&prog->prog_settings, return set_engine_option(&prog->prog_settings,
&prog->prog_version_cleared, arg); &prog->prog_version_cleared, arg);
case 'i':
prog->prog_settings.es_clock_granularity = atoi(arg);
return 0;
case 's': case 's':
if (0 == (prog->prog_engine_flags & LSENG_SERVER) && if (0 == (prog->prog_engine_flags & LSENG_SERVER) &&
!TAILQ_EMPTY(prog->prog_sports)) !TAILQ_EMPTY(prog->prog_sports))
@ -286,10 +293,11 @@ prog_process_conns (struct prog *prog)
if (lsquic_engine_earliest_adv_tick(prog->prog_engine, &diff)) if (lsquic_engine_earliest_adv_tick(prog->prog_engine, &diff))
{ {
if (diff < 4000) if (diff < 0
|| (unsigned) diff < prog->prog_settings.es_clock_granularity)
{ {
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 4000; timeout.tv_usec = prog->prog_settings.es_clock_granularity;
} }
else else
{ {

View file

@ -21,6 +21,7 @@ struct prog
unsigned prog_packout_max; unsigned prog_packout_max;
unsigned short prog_max_packet_size; unsigned short prog_max_packet_size;
int prog_version_cleared; int prog_version_cleared;
unsigned long prog_read_count;
struct event_base *prog_eb; struct event_base *prog_eb;
struct event *prog_timer, struct event *prog_timer,
*prog_send, *prog_send,
@ -41,7 +42,7 @@ prog_init (struct prog *, unsigned lsquic_engine_flags, struct sport_head *,
# define IP_DONTFRAG_FLAG "" # define IP_DONTFRAG_FLAG ""
#endif #endif
#define PROG_OPTS "m:c:y:L:l:o:H:s:S:Y:z:" IP_DONTFRAG_FLAG #define PROG_OPTS "i:m:c:y:L:l:o:H:s:S:Y:z:" IP_DONTFRAG_FLAG
/* Returns: /* Returns:
* 0 Applied * 0 Applied

View file

@ -560,6 +560,7 @@ read_handler (evutil_socket_t fd, short flags, void *ctx)
n_batches = 0; n_batches = 0;
iter.ri_sport = sport; iter.ri_sport = sport;
sport->sp_prog->prog_read_count += 1;
do do
{ {
iter.ri_off = 0; iter.ri_off = 0;

0
tools/bench/lsqb.sh Normal file → Executable file
View file