Introduce non-droppable control messages

Control messages are queued from the main thread and sent to the device
from a separate thread.

When the queue is full, messages are just dropped. This avoids to
accumulate too much delay between the client and the device in case of
network issue.

However, some messages should not be dropped: for example, dropping a
UHID_CREATE message would make invalid all further UHID_INPUT messages.
Therefore, mark these messages as non-droppable.

A non-droppable event is queued anyway (resizing the queue if
necessary, unless the allocation fails).
This commit is contained in:
Romain Vimont 2024-09-06 23:08:08 +02:00
parent c3a4b17842
commit 698e4d1bae
3 changed files with 31 additions and 5 deletions

View file

@ -278,6 +278,13 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
} }
} }
bool
sc_control_msg_is_droppable(const struct sc_control_msg *msg) {
// Cannot drop UHID_CREATE messages, because it would cause all further
// UHID_INPUT messages for this device to be invalid
return msg->type != SC_CONTROL_MSG_TYPE_UHID_CREATE;
}
void void
sc_control_msg_destroy(struct sc_control_msg *msg) { sc_control_msg_destroy(struct sc_control_msg *msg) {
switch (msg->type) { switch (msg->type) {

View file

@ -116,6 +116,11 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf);
void void
sc_control_msg_log(const struct sc_control_msg *msg); sc_control_msg_log(const struct sc_control_msg *msg);
// Even when the buffer is "full", some messages must absolutely not be dropped
// to avoid inconsistencies.
bool
sc_control_msg_is_droppable(const struct sc_control_msg *msg);
void void
sc_control_msg_destroy(struct sc_control_msg *msg); sc_control_msg_destroy(struct sc_control_msg *msg);

View file

@ -4,7 +4,8 @@
#include "util/log.h" #include "util/log.h"
#define SC_CONTROL_MSG_QUEUE_MAX 64 // Drop droppable events above this limit
#define SC_CONTROL_MSG_QUEUE_LIMIT 60
static void static void
sc_controller_receiver_on_ended(struct sc_receiver *receiver, bool error, sc_controller_receiver_on_ended(struct sc_receiver *receiver, bool error,
@ -22,7 +23,9 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
void *cbs_userdata) { void *cbs_userdata) {
sc_vecdeque_init(&controller->queue); sc_vecdeque_init(&controller->queue);
bool ok = sc_vecdeque_reserve(&controller->queue, SC_CONTROL_MSG_QUEUE_MAX); // Add 4 to support 4 non-droppable events without re-allocation
bool ok = sc_vecdeque_reserve(&controller->queue,
SC_CONTROL_MSG_QUEUE_LIMIT + 4);
if (!ok) { if (!ok) {
return false; return false;
} }
@ -93,20 +96,31 @@ sc_controller_push_msg(struct sc_controller *controller,
sc_control_msg_log(msg); sc_control_msg_log(msg);
} }
bool pushed = false;
sc_mutex_lock(&controller->mutex); sc_mutex_lock(&controller->mutex);
bool full = sc_vecdeque_is_full(&controller->queue); size_t size = sc_vecdeque_size(&controller->queue);
if (!full) { if (size < SC_CONTROL_MSG_QUEUE_LIMIT) {
bool was_empty = sc_vecdeque_is_empty(&controller->queue); bool was_empty = sc_vecdeque_is_empty(&controller->queue);
sc_vecdeque_push_noresize(&controller->queue, *msg); sc_vecdeque_push_noresize(&controller->queue, *msg);
pushed = true;
if (was_empty) { if (was_empty) {
sc_cond_signal(&controller->msg_cond); sc_cond_signal(&controller->msg_cond);
} }
} else if (!sc_control_msg_is_droppable(msg)) {
bool ok = sc_vecdeque_push(&controller->queue, *msg);
if (ok) {
pushed = true;
} else {
// A non-droppable event must be dropped anyway
LOG_OOM();
}
} }
// Otherwise (if the queue is full), the msg is discarded // Otherwise (if the queue is full), the msg is discarded
sc_mutex_unlock(&controller->mutex); sc_mutex_unlock(&controller->mutex);
return !full; return pushed;
} }
static bool static bool