Improve locking for batched mouse and gamepad sensor events

By unlocking the mutex before we enqueue the new entry, we can avoid the input thread immediately contending on the mutex after the new item wakes it up.
This commit is contained in:
Cameron Gutman
2025-11-25 17:30:01 -06:00
parent 20c05eda6a
commit b126e481a1

View File

@@ -724,9 +724,14 @@ int LiSendMouseMoveEvent(short deltaX, short deltaY) {
// Queue a packet holder if this is the only pending relative mouse event
if (!currentRelativeMouseState.dirty) {
// Set the dirty flag to claim ownership of inserting the packet holder
// and unlock to allow other threads to enqueue or process input.
currentRelativeMouseState.dirty = true;
PltUnlockMutex(&batchedInputMutex);
holder = allocatePacketHolder(0);
if (holder == NULL) {
PltUnlockMutex(&batchedInputMutex);
currentRelativeMouseState.dirty = false;
return -1;
}
@@ -747,22 +752,21 @@ int LiSendMouseMoveEvent(short deltaX, short deltaY) {
// Remaining fields are set in the input thread based on the latest currentRelativeMouseState values
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
if (err == LBQ_SUCCESS) {
currentRelativeMouseState.dirty = true;
}
else {
if (err != LBQ_SUCCESS) {
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
Limelog("Input queue reached maximum size limit\n");
freePacketHolder(holder);
// We weren't able to insert the entry, so let the next call try again
currentRelativeMouseState.dirty = false;
}
}
else {
// There's already a packet holder queued to send this event
PltUnlockMutex(&batchedInputMutex);
err = 0;
}
PltUnlockMutex(&batchedInputMutex);
return err;
}
@@ -785,9 +789,14 @@ int LiSendMousePositionEvent(short x, short y, short referenceWidth, short refer
// Queue a packet holder if this is the only pending absolute mouse event
if (!currentAbsoluteMouseState.dirty) {
// Set the dirty flag to claim ownership of inserting the packet holder
// and unlock to allow other threads to enqueue or process input.
currentAbsoluteMouseState.dirty = true;
PltUnlockMutex(&batchedInputMutex);
holder = allocatePacketHolder(0);
if (holder == NULL) {
PltUnlockMutex(&batchedInputMutex);
currentAbsoluteMouseState.dirty = false;
return -1;
}
@@ -803,22 +812,21 @@ int LiSendMousePositionEvent(short x, short y, short referenceWidth, short refer
// Remaining fields are set in the input thread based on the latest currentAbsoluteMouseState values
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
if (err == LBQ_SUCCESS) {
currentAbsoluteMouseState.dirty = true;
}
else {
if (err != LBQ_SUCCESS) {
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
Limelog("Input queue reached maximum size limit\n");
freePacketHolder(holder);
// We weren't able to insert the entry, so let the next call try again
currentAbsoluteMouseState.dirty = false;
}
}
else {
// There's already a packet holder queued to send this event
PltUnlockMutex(&batchedInputMutex);
err = 0;
}
PltUnlockMutex(&batchedInputMutex);
// This is not thread safe, but it's not a big deal because callers that want to
// use LiSendRelativeMotionAsMousePositionEvent() must not mix these function
// without synchronization (otherwise the state of the cursor on the host is
@@ -1536,9 +1544,14 @@ int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, fl
// Queue a packet holder if this is the only pending sensor event
if (!currentGamepadSensorState[controllerNumber][motionType - 1].dirty) {
// Set the dirty flag to claim ownership of inserting the packet holder
// and unlock to allow other threads to enqueue or process input.
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = true;
PltUnlockMutex(&batchedInputMutex);
holder = allocatePacketHolder(0);
if (holder == NULL) {
PltUnlockMutex(&batchedInputMutex);
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = false;
return -1;
}
@@ -1554,22 +1567,21 @@ int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, fl
// Remaining fields are set in the input thread based on the latest currentGamepadSensorState values
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
if (err == LBQ_SUCCESS) {
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = true;
}
else {
if (err != LBQ_SUCCESS) {
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
Limelog("Input queue reached maximum size limit\n");
freePacketHolder(holder);
// We weren't able to insert the entry, so let the next call try again
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = false;
}
}
else {
// There's already a packet holder queued to send this event
PltUnlockMutex(&batchedInputMutex);
err = 0;
}
PltUnlockMutex(&batchedInputMutex);
return err;
}