mirror of
https://github.com/CalcProgrammer1/OpenRGB.git
synced 2026-09-15 14:47:14 -04:00
Implement fair_mutex in i2c_smbus so that i2c controllers are given access to the bus in a more fair manner, which should help with high frame rates on slow buses
This commit is contained in:
1 parent
999cbd2578
commit
3e290907d5
3 files changed
+120
-1
No files matched your search
@@ -166,6 +166,7 @@ HEADERS +=
|
||||
Colors.h \
|
||||
dependencies/ColorWheel/ColorWheel.h \
|
||||
dependencies/json/nlohmann/json.hpp \
|
||||
fair_mutex.h \
|
||||
JsonUtils.h \
|
||||
LogManager.h \
|
||||
NetworkClient.h \
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*---------------------------------------------------------*\
|
||||
| fair_mutex.h |
|
||||
| |
|
||||
| Fair (FIFO) mutex for serialising I2C/SMBus transfers |
|
||||
| Services callers in the order they lock the mutex |
|
||||
| |
|
||||
| Adam Honse (CalcProgrammer1) 02 Sep 2026 |
|
||||
| |
|
||||
| This file is part of the OpenRGB project |
|
||||
| SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
\*---------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
/*---------------------------------------------------------*\
|
||||
| fair_mutex |
|
||||
| |
|
||||
| A fair (FIFO) mutex that services lock requests in the |
|
||||
| order they are received, preventing any single caller |
|
||||
| from monopolising the mutex at the expense of callers |
|
||||
| that have been waiting longer. |
|
||||
| |
|
||||
| This is implemented as a ticket lock: every caller draws |
|
||||
| a sequentially-increasing ticket number and then waits |
|
||||
| until that ticket is at the head of the queue. An |
|
||||
| internal std::mutex guards the ticket counters; a |
|
||||
| std::condition_variable wakes exactly one waiter when its |
|
||||
| ticket reaches the front of the queue. |
|
||||
| |
|
||||
| The class satisfies the BasicLockable requirements |
|
||||
| (lock / unlock) so it can be used with std::lock_guard |
|
||||
| and std::unique_lock, or with direct lock() / unlock() |
|
||||
| calls. |
|
||||
\*---------------------------------------------------------*/
|
||||
class fair_mutex
|
||||
{
|
||||
public:
|
||||
fair_mutex() = default;
|
||||
~fair_mutex() = default;
|
||||
|
||||
fair_mutex(const fair_mutex&) = delete;
|
||||
fair_mutex& operator=(const fair_mutex&) = delete;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Acquire the mutex. |
|
||||
| |
|
||||
| Draws the next ticket and blocks until that ticket |
|
||||
| becomes the one being served, guaranteeing FIFO |
|
||||
| ordering among all waiting threads. |
|
||||
\*-----------------------------------------------------*/
|
||||
void lock()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(internal_mutex);
|
||||
|
||||
const unsigned int ticket = next_ticket++;
|
||||
|
||||
while(ticket != serving)
|
||||
{
|
||||
cv.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Release the mutex. |
|
||||
| |
|
||||
| Increments the "serving" counter so that the next |
|
||||
| ticket in the queue becomes eligible to proceed, then |
|
||||
| wakes exactly one waiting thread. |
|
||||
\*-----------------------------------------------------*/
|
||||
void unlock()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(internal_mutex);
|
||||
|
||||
serving++;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------*\
|
||||
| Guards the ticket counters. This is only held for |
|
||||
| very brief critical sections (incrementing the |
|
||||
| counters) so it does not interfere with the fairness |
|
||||
| of the outer mutex. |
|
||||
\*-----------------------------------------------------*/
|
||||
std::mutex internal_mutex;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Sleeps waiters until their ticket reaches the front |
|
||||
| of the queue. notify_one() is used because only the |
|
||||
| single thread at the head of the queue should be |
|
||||
| woken. |
|
||||
\*-----------------------------------------------------*/
|
||||
std::condition_variable cv;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| Sequentially-assigned ticket number. Incremented |
|
||||
| atomically (under internal_mutex) each time a thread |
|
||||
| calls lock(). |
|
||||
| |
|
||||
| Wrapped unsigned arithmetic is safe here: the FIFO |
|
||||
| property is preserved even on wrap-around, and the |
|
||||
| probability of approaching 2^32 acquisitions is |
|
||||
| negligible for this use case. |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned int next_ticket = 0;
|
||||
|
||||
/*-----------------------------------------------------*\
|
||||
| The ticket number currently being served. A waiter |
|
||||
| whose ticket equals this value is permitted to |
|
||||
| proceed. |
|
||||
\*-----------------------------------------------------*/
|
||||
unsigned int serving = 0;
|
||||
};
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "fair_mutex.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
@@ -118,7 +120,7 @@ public:
|
||||
virtual s32 i2c_xfer(u8 addr, char read_write, int* size, u8* data) = 0;
|
||||
|
||||
private:
|
||||
std::mutex i2c_smbus_xfer_mutex;
|
||||
fair_mutex i2c_smbus_xfer_mutex;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user