serialqueue: Reduce time transmit_requests lock is held

Only hold the lock in the serialqueue thread when moving messages to
the ready queue and when setting the next need_kick_clock.

Only set the need_kick_clock just prior to sleeping.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2025-10-18 15:07:56 -04:00
parent ad55dcad42
commit 6d5fd9164e

View File

@@ -535,8 +535,12 @@ build_and_send_command(struct serialqueue *sq, uint8_t *buf, int pending
static uint64_t
check_upcoming_queues(struct serialqueue *sq, uint64_t ack_clock)
{
if (ack_clock < sq->transmit_requests.min_release_clock)
pthread_mutex_lock(&sq->transmit_requests.lock);
sq->transmit_requests.need_kick_clock = 0;
if (ack_clock < sq->transmit_requests.min_release_clock) {
pthread_mutex_unlock(&sq->transmit_requests.lock);
return sq->transmit_requests.min_release_clock;
}
uint64_t min_stalled_clock = MAX_CLOCK;
struct command_queue *cq, *_ncq;
@@ -564,6 +568,7 @@ check_upcoming_queues(struct serialqueue *sq, uint64_t ack_clock)
list_add_tail(&cq->ready.node, &sq->ready_queues);
}
sq->transmit_requests.min_release_clock = min_stalled_clock;
pthread_mutex_unlock(&sq->transmit_requests.lock);
return min_stalled_clock;
}
@@ -588,9 +593,21 @@ check_send_command(struct serialqueue *sq, int pending, double eventtime)
double idletime = eventtime > sq->idle_time ? eventtime : sq->idle_time;
idletime += calculate_bittime(sq, pending + MESSAGE_MIN);
uint64_t ack_clock = clock_from_time(&sq->ce, idletime);
pthread_mutex_lock(&sq->transmit_requests.lock);
uint64_t min_stalled_clock = check_upcoming_queues(sq, ack_clock);
// Check if a block is fully ready to send
if (sq->ready_bytes >= MESSAGE_PAYLOAD_MAX)
return PR_NOW;
if (! sq->ce.est_freq) {
// Clock unknown during initial startup - recheck on each add
if (sq->ready_bytes)
return PR_NOW;
pthread_mutex_lock(&sq->transmit_requests.lock);
sq->transmit_requests.need_kick_clock = MAX_CLOCK;
pthread_mutex_unlock(&sq->transmit_requests.lock);
return PR_NEVER;
}
// Check if it is still needed to send messages from the ready_queues
uint64_t min_ready_clock = MAX_CLOCK;
struct command_queue *cq;
@@ -606,29 +623,26 @@ check_send_command(struct serialqueue *sq, int pending, double eventtime)
if (req_clock < min_ready_clock)
min_ready_clock = req_clock;
}
// Check for messages to send
if (sq->ready_bytes >= MESSAGE_PAYLOAD_MAX)
goto now;
if (! sq->ce.est_freq) {
if (sq->ready_bytes)
goto now;
sq->transmit_requests.need_kick_clock = MAX_CLOCK;
pthread_mutex_unlock(&sq->transmit_requests.lock);
return PR_NEVER;
}
uint64_t reqclock_delta = MIN_REQTIME_DELTA * sq->ce.est_freq;
if (min_ready_clock <= ack_clock + reqclock_delta)
goto now;
return PR_NOW;
// Determine next wakeup time
if (pending)
// Caller wont sleep anyway - just return
return eventtime;
uint64_t wantclock = min_ready_clock - reqclock_delta;
if (min_stalled_clock < wantclock)
wantclock = min_stalled_clock;
pthread_mutex_lock(&sq->transmit_requests.lock);
if (wantclock > sq->transmit_requests.min_release_clock) {
// Raced with add of new command - avoid sleeping
pthread_mutex_unlock(&sq->transmit_requests.lock);
return eventtime;
}
sq->transmit_requests.need_kick_clock = wantclock;
pthread_mutex_unlock(&sq->transmit_requests.lock);
return idletime + (wantclock - ack_clock) / sq->ce.est_freq;
now:
pthread_mutex_unlock(&sq->transmit_requests.lock);
return PR_NOW;
}
// Callback timer to send data to the serial port