mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-08-05 01:18:36 +02:00
Communication: Add pause feature
Removed redirect read
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user