defer triggering dqueue thread until end-of-packet
There appears to be a minor performance benefit to not waking up the delivery thread (if used) immediately upon enqueueing the first sample, but rather to wait (typically) until the end of the packet. In a latency measurement it probably makes little difference: one shouldn't use asynchronous delivery if one needs the lowest possible latency, and the end of the packet is reached rather quickly normally. Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
parent
c92820677d
commit
ec0062542c
3 changed files with 52 additions and 10 deletions
|
@ -228,6 +228,8 @@ seqno_t nn_reorder_next_seq (const struct nn_reorder *reorder);
|
||||||
|
|
||||||
struct nn_dqueue *nn_dqueue_new (const char *name, uint32_t max_samples, nn_dqueue_handler_t handler, void *arg);
|
struct nn_dqueue *nn_dqueue_new (const char *name, uint32_t max_samples, nn_dqueue_handler_t handler, void *arg);
|
||||||
void nn_dqueue_free (struct nn_dqueue *q);
|
void nn_dqueue_free (struct nn_dqueue *q);
|
||||||
|
bool nn_dqueue_enqueue_deferred_wakeup (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres);
|
||||||
|
void dd_dqueue_enqueue_trigger (struct nn_dqueue *q);
|
||||||
void nn_dqueue_enqueue (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres);
|
void nn_dqueue_enqueue (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres);
|
||||||
void nn_dqueue_enqueue1 (struct nn_dqueue *q, const nn_guid_t *rdguid, struct nn_rsample_chain *sc, nn_reorder_result_t rres);
|
void nn_dqueue_enqueue1 (struct nn_dqueue *q, const nn_guid_t *rdguid, struct nn_rsample_chain *sc, nn_reorder_result_t rres);
|
||||||
void nn_dqueue_enqueue_callback (struct nn_dqueue *q, nn_dqueue_callback_t cb, void *arg);
|
void nn_dqueue_enqueue_callback (struct nn_dqueue *q, nn_dqueue_callback_t cb, void *arg);
|
||||||
|
|
|
@ -2541,6 +2541,26 @@ static int nn_dqueue_enqueue_locked (struct nn_dqueue *q, struct nn_rsample_chai
|
||||||
return must_signal;
|
return must_signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nn_dqueue_enqueue_deferred_wakeup (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres)
|
||||||
|
{
|
||||||
|
bool signal;
|
||||||
|
assert (rres > 0);
|
||||||
|
assert (sc->first);
|
||||||
|
assert (sc->last->next == NULL);
|
||||||
|
ddsrt_mutex_lock (&q->lock);
|
||||||
|
ddsrt_atomic_add32 (&q->nof_samples, (uint32_t) rres);
|
||||||
|
signal = nn_dqueue_enqueue_locked (q, sc);
|
||||||
|
ddsrt_mutex_unlock (&q->lock);
|
||||||
|
return signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dd_dqueue_enqueue_trigger (struct nn_dqueue *q)
|
||||||
|
{
|
||||||
|
ddsrt_mutex_lock (&q->lock);
|
||||||
|
ddsrt_cond_broadcast (&q->cond);
|
||||||
|
ddsrt_mutex_unlock (&q->lock);
|
||||||
|
}
|
||||||
|
|
||||||
void nn_dqueue_enqueue (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres)
|
void nn_dqueue_enqueue (struct nn_dqueue *q, struct nn_rsample_chain *sc, nn_reorder_result_t rres)
|
||||||
{
|
{
|
||||||
assert (rres > 0);
|
assert (rres > 0);
|
||||||
|
@ -2622,6 +2642,8 @@ void nn_dqueue_wait_until_empty_if_full (struct nn_dqueue *q)
|
||||||
if (count >= q->max_samples)
|
if (count >= q->max_samples)
|
||||||
{
|
{
|
||||||
ddsrt_mutex_lock (&q->lock);
|
ddsrt_mutex_lock (&q->lock);
|
||||||
|
/* In case the wakeups are were all deferred */
|
||||||
|
ddsrt_cond_broadcast (&q->cond);
|
||||||
while (ddsrt_atomic_ld32 (&q->nof_samples) > 0)
|
while (ddsrt_atomic_ld32 (&q->nof_samples) > 0)
|
||||||
ddsrt_cond_wait (&q->cond, &q->lock);
|
ddsrt_cond_wait (&q->cond, &q->lock);
|
||||||
ddsrt_mutex_unlock (&q->lock);
|
ddsrt_mutex_unlock (&q->lock);
|
||||||
|
|
|
@ -2151,7 +2151,7 @@ static void clean_defrag (struct proxy_writer *pwr)
|
||||||
nn_defrag_notegap (pwr->defrag, 1, seq);
|
nn_defrag_notegap (pwr->defrag, 1, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_regular (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const Data_DataFrag_common_t *msg, const struct nn_rsample_info *sampleinfo, uint32_t fragnum, struct nn_rdata *rdata)
|
static void handle_regular (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const Data_DataFrag_common_t *msg, const struct nn_rsample_info *sampleinfo, uint32_t fragnum, struct nn_rdata *rdata, struct nn_dqueue **deferred_wakeup)
|
||||||
{
|
{
|
||||||
struct proxy_writer *pwr;
|
struct proxy_writer *pwr;
|
||||||
struct nn_rsample *rsample;
|
struct nn_rsample *rsample;
|
||||||
|
@ -2251,10 +2251,23 @@ static void handle_regular (struct receiver_state *rst, nn_etime_t tnow, struct
|
||||||
receive thread's data gets interleaved -- arguably delivery
|
receive thread's data gets interleaved -- arguably delivery
|
||||||
needn't be exactly in-order, which would allow us to do this
|
needn't be exactly in-order, which would allow us to do this
|
||||||
without pwr->e.lock held. */
|
without pwr->e.lock held. */
|
||||||
if (!pwr->deliver_synchronously)
|
if (pwr->deliver_synchronously)
|
||||||
nn_dqueue_enqueue (pwr->dqueue, &sc, rres);
|
{
|
||||||
else
|
/* FIXME: just in case the synchronous delivery runs into a delay caused
|
||||||
|
by the current mishandling of resource limits */
|
||||||
|
if (*deferred_wakeup)
|
||||||
|
dd_dqueue_enqueue_trigger (*deferred_wakeup);
|
||||||
deliver_user_data_synchronously (&sc);
|
deliver_user_data_synchronously (&sc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (nn_dqueue_enqueue_deferred_wakeup (pwr->dqueue, &sc, rres))
|
||||||
|
{
|
||||||
|
if (*deferred_wakeup && *deferred_wakeup != pwr->dqueue)
|
||||||
|
dd_dqueue_enqueue_trigger (*deferred_wakeup);
|
||||||
|
*deferred_wakeup = pwr->dqueue;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pwr->n_readers_out_of_sync > 0)
|
if (pwr->n_readers_out_of_sync > 0)
|
||||||
{
|
{
|
||||||
/* Those readers catching up with TL but in sync with the proxy
|
/* Those readers catching up with TL but in sync with the proxy
|
||||||
|
@ -2384,7 +2397,7 @@ static void drop_oversize (struct receiver_state *rst, struct nn_rmsg *rmsg, con
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_Data (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const Data_t *msg, size_t size, struct nn_rsample_info *sampleinfo, unsigned char *datap)
|
static int handle_Data (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const Data_t *msg, size_t size, struct nn_rsample_info *sampleinfo, unsigned char *datap, struct nn_dqueue **deferred_wakeup)
|
||||||
{
|
{
|
||||||
DDS_TRACE("DATA(%x:%x:%x:%x -> %x:%x:%x:%x #%"PRId64"",
|
DDS_TRACE("DATA(%x:%x:%x:%x -> %x:%x:%x:%x #%"PRId64"",
|
||||||
PGUIDPREFIX (rst->src_guid_prefix), msg->x.writerId.u,
|
PGUIDPREFIX (rst->src_guid_prefix), msg->x.writerId.u,
|
||||||
|
@ -2421,14 +2434,14 @@ static int handle_Data (struct receiver_state *rst, nn_etime_t tnow, struct nn_r
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handle_regular (rst, tnow, rmsg, &msg->x, sampleinfo, ~0u, rdata);
|
handle_regular (rst, tnow, rmsg, &msg->x, sampleinfo, ~0u, rdata, deferred_wakeup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DDS_TRACE(")");
|
DDS_TRACE(")");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_DataFrag (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const DataFrag_t *msg, size_t size, struct nn_rsample_info *sampleinfo, unsigned char *datap)
|
static int handle_DataFrag (struct receiver_state *rst, nn_etime_t tnow, struct nn_rmsg *rmsg, const DataFrag_t *msg, size_t size, struct nn_rsample_info *sampleinfo, unsigned char *datap, struct nn_dqueue **deferred_wakeup)
|
||||||
{
|
{
|
||||||
DDS_TRACE("DATAFRAG(%x:%x:%x:%x -> %x:%x:%x:%x #%"PRId64"/[%u..%u]",
|
DDS_TRACE("DATAFRAG(%x:%x:%x:%x -> %x:%x:%x:%x #%"PRId64"/[%u..%u]",
|
||||||
PGUIDPREFIX (rst->src_guid_prefix), msg->x.writerId.u,
|
PGUIDPREFIX (rst->src_guid_prefix), msg->x.writerId.u,
|
||||||
|
@ -2494,7 +2507,7 @@ static int handle_DataFrag (struct receiver_state *rst, nn_etime_t tnow, struct
|
||||||
wrong, it'll simply generate a request for retransmitting a
|
wrong, it'll simply generate a request for retransmitting a
|
||||||
non-existent fragment. The other side SHOULD be capable of
|
non-existent fragment. The other side SHOULD be capable of
|
||||||
dealing with that. */
|
dealing with that. */
|
||||||
handle_regular (rst, tnow, rmsg, &msg->x, sampleinfo, msg->fragmentStartingNum + msg->fragmentsInSubmessage - 2, rdata);
|
handle_regular (rst, tnow, rmsg, &msg->x, sampleinfo, msg->fragmentStartingNum + msg->fragmentsInSubmessage - 2, rdata, deferred_wakeup);
|
||||||
}
|
}
|
||||||
DDS_TRACE(")");
|
DDS_TRACE(")");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -2681,6 +2694,7 @@ static int handle_submsg_sequence
|
||||||
nn_ddsi_time_t timestamp;
|
nn_ddsi_time_t timestamp;
|
||||||
size_t submsg_size = 0;
|
size_t submsg_size = 0;
|
||||||
unsigned char * end = msg + len;
|
unsigned char * end = msg + len;
|
||||||
|
struct nn_dqueue *deferred_wakeup = NULL;
|
||||||
|
|
||||||
/* Receiver state is dynamically allocated with lifetime bound to
|
/* Receiver state is dynamically allocated with lifetime bound to
|
||||||
the message. Updates cause a new copy to be created if the
|
the message. Updates cause a new copy to be created if the
|
||||||
|
@ -2845,7 +2859,7 @@ static int handle_submsg_sequence
|
||||||
goto malformed;
|
goto malformed;
|
||||||
sampleinfo.timestamp = timestamp;
|
sampleinfo.timestamp = timestamp;
|
||||||
sampleinfo.reception_timestamp = tnowWC;
|
sampleinfo.reception_timestamp = tnowWC;
|
||||||
handle_DataFrag (rst, tnowE, rmsg, &sm->datafrag, submsg_size, &sampleinfo, datap);
|
handle_DataFrag (rst, tnowE, rmsg, &sm->datafrag, submsg_size, &sampleinfo, datap, &deferred_wakeup);
|
||||||
rst_live = 1;
|
rst_live = 1;
|
||||||
ts_for_latmeas = 0;
|
ts_for_latmeas = 0;
|
||||||
}
|
}
|
||||||
|
@ -2860,7 +2874,7 @@ static int handle_submsg_sequence
|
||||||
goto malformed;
|
goto malformed;
|
||||||
sampleinfo.timestamp = timestamp;
|
sampleinfo.timestamp = timestamp;
|
||||||
sampleinfo.reception_timestamp = tnowWC;
|
sampleinfo.reception_timestamp = tnowWC;
|
||||||
handle_Data (rst, tnowE, rmsg, &sm->data, submsg_size, &sampleinfo, datap);
|
handle_Data (rst, tnowE, rmsg, &sm->data, submsg_size, &sampleinfo, datap, &deferred_wakeup);
|
||||||
rst_live = 1;
|
rst_live = 1;
|
||||||
ts_for_latmeas = 0;
|
ts_for_latmeas = 0;
|
||||||
}
|
}
|
||||||
|
@ -2958,6 +2972,8 @@ static int handle_submsg_sequence
|
||||||
}
|
}
|
||||||
thread_state_asleep (ts1);
|
thread_state_asleep (ts1);
|
||||||
assert (thread_is_asleep ());
|
assert (thread_is_asleep ());
|
||||||
|
if (deferred_wakeup)
|
||||||
|
dd_dqueue_enqueue_trigger (deferred_wakeup);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
malformed:
|
malformed:
|
||||||
|
@ -2966,6 +2982,8 @@ malformed:
|
||||||
malformed_asleep:
|
malformed_asleep:
|
||||||
assert (thread_is_asleep ());
|
assert (thread_is_asleep ());
|
||||||
malformed_packet_received (msg, submsg, len, state, state_smkind, hdr->vendorid);
|
malformed_packet_received (msg, submsg, len, state, state_smkind, hdr->vendorid);
|
||||||
|
if (deferred_wakeup)
|
||||||
|
dd_dqueue_enqueue_trigger (deferred_wakeup);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue