Communication: Add pause feature

Removed redirect read
This commit is contained in:
Yasser Yassine
2024-05-23 21:23:45 +00:00
parent 9a1cd1124d
commit 659fcf633c
13 changed files with 99 additions and 70 deletions
+44 -47
View File
@@ -20,8 +20,6 @@ using namespace icsneo;
int Communication::messageCallbackIDCounter = 1;
Communication::~Communication() {
if(redirectingRead)
clearRedirectRead();
if(isOpen())
close();
}
@@ -44,6 +42,11 @@ void Communication::spawnThreads() {
void Communication::joinThreads() {
closing = true;
if(pauseReadTask) {
resumeReads();
}
if(readTaskThread.joinable())
readTaskThread.join();
closing = false;
@@ -96,23 +99,6 @@ bool Communication::sendCommand(ExtendedCommand cmd, std::vector<uint8_t> argume
return sendCommand(Command::Extended, arguments);
}
bool Communication::redirectRead(std::function<void(std::vector<uint8_t>&&)> redirectTo) {
if(redirectingRead)
return false;
redirectionFn = redirectTo;
redirectingRead = true;
return true;
}
void Communication::clearRedirectRead() {
if(!redirectingRead)
return;
// The mutex is required to clear the redirection, but not to set it
std::lock_guard<std::mutex> lk(redirectingReadMutex);
redirectingRead = false;
redirectionFn = std::function<void(std::vector<uint8_t>&&)>();
}
bool Communication::getSettingsSync(std::vector<uint8_t>& data, std::chrono::milliseconds timeout) {
static const std::shared_ptr<MessageFilter> filter = std::make_shared<MessageFilter>(Network::NetID::ReadSettings);
std::shared_ptr<Message> msg = waitForMessageSync([this]() {
@@ -261,44 +247,55 @@ void Communication::dispatchMessage(const std::shared_ptr<Message>& msg) {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
}
void Communication::readTask() {
std::vector<uint8_t> readBytes;
void Communication::pauseReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTask = true;
}
void Communication::resumeReads() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
if(!pauseReadTask) {
return;
}
pauseReadTask = false;
lk.unlock();
pauseReadTaskCv.notify_one();
}
bool Communication::readsArePaused() {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
return pauseReadTask;
}
void Communication::readTask() {
EventManager::GetInstance().downgradeErrorsOnCurrentThread();
while(!closing) {
readBytes.clear();
if(pauseReadTask) {
std::unique_lock<std::mutex> lk(pauseReadTaskMutex);
pauseReadTaskCv.wait(lk, [this]() { return !pauseReadTask; });
}
if(driver->readAvailable()) {
handleInput(*packetizer, readBytes);
if(pauseReadTask) {
/**
* Reads could have paused while the driver was not available
*/
continue;
}
handleInput(*packetizer);
}
}
}
void Communication::handleInput(Packetizer& p, std::vector<uint8_t>& readBytes) {
if(redirectingRead) {
// redirectingRead is an atomic so it can be set without acquiring a mutex
// However, we do not clear it without the mutex. The idea is that if another
// thread calls clearRedirectRead(), it will block until the redirectionFn
// finishes, and after that the redirectionFn will not be called again.
std::unique_lock<std::mutex> lk(redirectingReadMutex);
// So after we acquire the mutex, we need to check the atomic again, and
// if it has become cleared, we *can not* run the redirectionFn.
if(redirectingRead) {
redirectionFn(std::move(readBytes));
} else {
// The redirectionFn got cleared while we were acquiring the lock
lk.unlock(); // We don't need the lock anymore
handleInput(p, readBytes); // and we might as well process this input ourselves
}
} else {
if(p.input(driver->getReadBuffer())) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
void Communication::handleInput(Packetizer& p) {
if(p.input(driver->getReadBuffer())) {
for(const auto& packet : p.output()) {
std::shared_ptr<Message> msg;
if(!decoder->decode(msg, packet))
continue;
dispatchMessage(msg);
}
dispatchMessage(msg);
}
}
}
+24 -5
View File
@@ -8,6 +8,26 @@
using namespace icsneo;
bool Driver::writeToReadBuffer(const uint8_t* buf, size_t numReceived) {
bool ret = readBuffer.write(buf, numReceived);
if(hasRxWaitRequest) {
rxWaitRequestCv.notify_one();
}
return ret;
}
bool Driver::waitForRx(size_t minBytes, std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> lk(rxWaitMutex);
hasRxWaitRequest = true;
auto ret = rxWaitRequestCv.wait_for(lk, timeout, [this, minBytes]{ return readBuffer.size() >= minBytes; });
hasRxWaitRequest = false;
return ret;
}
bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds timeout, size_t limit) {
// A limit of zero indicates no limit
if(limit == 0)
@@ -16,14 +36,13 @@ bool Driver::readWait(std::vector<uint8_t>& bytes, std::chrono::milliseconds tim
if(limit > (readBuffer.size() + 4))
limit = (readBuffer.size() + 4);
bytes.resize(limit);
// wait until we have enough data, or the timout occurs
const auto timeoutTime = std::chrono::steady_clock::now() + timeout;
while (readBuffer.size() < limit && std::chrono::steady_clock::now() < timeoutTime) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
waitForRx(limit, timeout);
size_t actuallyRead = std::min(readBuffer.size(), limit);
bytes.resize(actuallyRead);
readBuffer.read(bytes.data(), 0, actuallyRead);
readBuffer.pop(actuallyRead);
bytes.resize(actuallyRead);
+5 -2
View File
@@ -177,8 +177,11 @@ void MultiChannelCommunication::vnetReadTask(size_t vnetIndex) {
if(queue.wait_dequeue_timed(payloadBytes, std::chrono::milliseconds(250))) {
if(closing)
break;
handleInput(*vnetPacketizer, payloadBytes);
auto& ringBuffer = driver->getReadBuffer();
ringBuffer.write(payloadBytes);
handleInput(*vnetPacketizer);
}
}
}