-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New PR for #19(Watchdog terminates the driver when debugging longer than 10s) #33
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -25,6 +25,7 @@ | |||||||
* on unix sockets | ||||||||
*/ | ||||||||
#include "bridge.hpp" | ||||||||
#include "../VRDriver.hpp" | ||||||||
#ifdef __linux__ | ||||||||
#include "unix-sockets.hpp" | ||||||||
#include <string_view> | ||||||||
|
@@ -73,98 +74,157 @@ BasicLocalClient client{}; | |||||||
inline constexpr int BUFFER_SIZE = 1024; | ||||||||
using ByteBuffer = std::array<uint8_t, BUFFER_SIZE>; | ||||||||
ByteBuffer byteBuffer; | ||||||||
} | ||||||||
|
||||||||
SlimeVRDriver::Bridge::Bridge(VRDriver *i_pDriver) | ||||||||
: m_pDriver(i_pDriver), m_pPipe(nullptr), m_pBuffer(nullptr), | ||||||||
m_eState(EState::BRIDGE_DISCONNECTED), m_bStop(false) { | ||||||||
} | ||||||||
|
||||||||
SlimeVRDriver::Bridge::~Bridge() { | ||||||||
this->stop(); | ||||||||
} | ||||||||
|
||||||||
void SlimeVRDriver::Bridge::run() { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This appears to consist entirely of platform independent code, but is duplicated between windows pipes and unix sockets. |
||||||||
|
||||||||
messages::ProtobufMessage oMsg; | ||||||||
|
||||||||
while (!this->m_bStop) { | ||||||||
std::this_thread::sleep_for(std::chrono::milliseconds(3)); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, we'd wait for the OS to notify us that there's a message incoming, or we have a message in the queue that needs to be sent instead of sleeping for <arbitrary amount of time>. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The sleep is a common practice to allow the cpu to switch the context to avoid high utilization. In my eyes it would be the best to remove the pipes completely and send the data over TCP e.g. with Asio. Then you have only one implementation that works for windows and linux. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm talking about using select or WaitForMultipleObjects instead of sleeping, meaning the OS can wake us up as soon as either there's an incoming message, or another thread sends a notification that there's an outgoing message, and we don't need to periodically wake-up to poll the outgoing queue. This would still be applicable to a TCP based implementation, unless you had the other thread just send the message directly. (But that optimization would also apply to this implementation, so eh) This doesn't need to be done right now, so I'll resolve the conversation after adding a TODO. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
|
||||||||
switch (this->m_eState) { | ||||||||
case EState::BRIDGE_DISCONNECTED: | ||||||||
attemptPipeConnect(); | ||||||||
break; | ||||||||
case EState::BRIDGE_ERROR: | ||||||||
resetPipe(); | ||||||||
break; | ||||||||
case EState::BRIDGE_CONNECTED: | ||||||||
default: | ||||||||
break; | ||||||||
} | ||||||||
|
||||||||
client.UpdateOnce(); | ||||||||
|
||||||||
// Read all msg | ||||||||
while (this->fetchNextBridgeMessage(oMsg)) { | ||||||||
std::lock_guard<std::recursive_mutex> lk(this->m_oMutex); | ||||||||
this->m_aRecvQueue.push(std::move(oMsg)); | ||||||||
}; | ||||||||
|
||||||||
|
||||||||
while (true) { | ||||||||
{ // Custom scope for send queue mutex | ||||||||
std::lock_guard<std::recursive_mutex> lk(this->m_oMutex); | ||||||||
if (this->m_aSendQueue.empty()) { | ||||||||
break; | ||||||||
} | ||||||||
|
||||||||
oMsg = std::move(this->m_aSendQueue.front()); | ||||||||
this->m_aSendQueue.pop(); | ||||||||
} | ||||||||
|
||||||||
this->sendBridgeMessageFromQueue(oMsg); | ||||||||
} | ||||||||
|
||||||||
} | ||||||||
} | ||||||||
void SlimeVRDriver::Bridge::sendBridgeMessageFromQueue(messages::ProtobufMessage &i_oMessage) { | ||||||||
if (!client.IsOpen()) { | ||||||||
this->m_pDriver->Log("bridge send error: failed to write header"); | ||||||||
this->setBridgeError(); | ||||||||
}; | ||||||||
|
||||||||
const auto bufBegin = byteBuffer.begin(); | ||||||||
const auto bufferSize = static_cast<int>(std::distance(bufBegin, byteBuffer.end())); | ||||||||
const auto msgSize = static_cast<int>(i_oMessage.ByteSizeLong()); | ||||||||
const std::optional msgBeginIt = WriteHeader(bufBegin, bufferSize, msgSize); | ||||||||
if (!msgBeginIt) { | ||||||||
this->m_pDriver->Log("bridge send error: failed to write header"); | ||||||||
return; | ||||||||
} | ||||||||
if (!i_oMessage.SerializeToArray(&(**msgBeginIt), msgSize)) { | ||||||||
this->m_pDriver->Log("bridge send error: failed to serialize"); | ||||||||
return; | ||||||||
} | ||||||||
int bytesToSend = static_cast<int>(std::distance(bufBegin, *msgBeginIt + msgSize)); | ||||||||
if (bytesToSend <= 0) { | ||||||||
this->m_pDriver->Log("bridge send error: empty message"); | ||||||||
return; | ||||||||
} | ||||||||
if (bytesToSend > bufferSize) { | ||||||||
this->m_pDriver->Log("bridge send error: message too big"); | ||||||||
return; | ||||||||
} | ||||||||
try { | ||||||||
client.Send(bufBegin, bytesToSend); | ||||||||
} catch (const std::exception& e) { | ||||||||
this->setBridgeError(); | ||||||||
this->m_pDriver->Log("bridge send error: " + std::string(e.what())); | ||||||||
return; | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
bool getNextBridgeMessage(messages::ProtobufMessage& message, SlimeVRDriver::VRDriver& driver) { | ||||||||
bool SlimeVRDriver::Bridge::fetchNextBridgeMessage(messages::ProtobufMessage &i_oMessage) { | ||||||||
if (!client.IsOpen()) return false; | ||||||||
|
||||||||
int bytesRecv = 0; | ||||||||
try { | ||||||||
bytesRecv = client.RecvOnce(byteBuffer.begin(), HEADER_SIZE); | ||||||||
} catch (const std::exception& e) { | ||||||||
client.Close(); | ||||||||
driver.Log("bridge send error: " + std::string(e.what())); | ||||||||
this->m_pDriver->Log("bridge send error: " + std::string(e.what())); | ||||||||
return false; | ||||||||
} | ||||||||
if (bytesRecv == 0) return false; // no message waiting | ||||||||
|
||||||||
int msgSize = 0; | ||||||||
const std::optional msgBeginIt = ReadHeader(byteBuffer.begin(), bytesRecv, msgSize); | ||||||||
if (!msgBeginIt) { | ||||||||
driver.Log("bridge recv error: invalid message header or size"); | ||||||||
this->m_pDriver->Log("bridge recv error: invalid message header or size"); | ||||||||
return false; | ||||||||
} | ||||||||
if (msgSize <= 0) { | ||||||||
driver.Log("bridge recv error: empty message"); | ||||||||
this->m_pDriver->Log("bridge recv error: empty message"); | ||||||||
return false; | ||||||||
} | ||||||||
try { | ||||||||
if (!client.RecvAll(*msgBeginIt, msgSize)) { | ||||||||
driver.Log("bridge recv error: client closed"); | ||||||||
this->m_pDriver->Log("bridge recv error: client closed"); | ||||||||
return false; | ||||||||
} | ||||||||
} catch (const std::exception& e) { | ||||||||
client.Close(); | ||||||||
driver.Log("bridge send error: " + std::string(e.what())); | ||||||||
this->m_pDriver->Log("bridge send error: " + std::string(e.what())); | ||||||||
return false; | ||||||||
} | ||||||||
if (!message.ParseFromArray(&(**msgBeginIt), msgSize)) { | ||||||||
driver.Log("bridge recv error: failed to parse"); | ||||||||
if (!i_oMessage.ParseFromArray(&(**msgBeginIt), msgSize)) { | ||||||||
this->m_pDriver->Log("bridge recv error: failed to parse"); | ||||||||
return false; | ||||||||
} | ||||||||
|
||||||||
return true; | ||||||||
} | ||||||||
|
||||||||
bool sendBridgeMessage(messages::ProtobufMessage& message, SlimeVRDriver::VRDriver& driver) { | ||||||||
if (!client.IsOpen()) return false; | ||||||||
const auto bufBegin = byteBuffer.begin(); | ||||||||
const auto bufferSize = static_cast<int>(std::distance(bufBegin, byteBuffer.end())); | ||||||||
const auto msgSize = static_cast<int>(message.ByteSizeLong()); | ||||||||
const std::optional msgBeginIt = WriteHeader(bufBegin, bufferSize, msgSize); | ||||||||
if (!msgBeginIt) { | ||||||||
driver.Log("bridge send error: failed to write header"); | ||||||||
return false; | ||||||||
} | ||||||||
if (!message.SerializeToArray(&(**msgBeginIt), msgSize)) { | ||||||||
driver.Log("bridge send error: failed to serialize"); | ||||||||
return false; | ||||||||
} | ||||||||
int bytesToSend = static_cast<int>(std::distance(bufBegin, *msgBeginIt + msgSize)); | ||||||||
if (bytesToSend <= 0) { | ||||||||
driver.Log("bridge send error: empty message"); | ||||||||
return false; | ||||||||
} | ||||||||
if (bytesToSend > bufferSize) { | ||||||||
driver.Log("bridge send error: message too big"); | ||||||||
return false; | ||||||||
} | ||||||||
try { | ||||||||
return client.Send(bufBegin, bytesToSend); | ||||||||
} catch (const std::exception& e) { | ||||||||
client.Close(); | ||||||||
driver.Log("bridge send error: " + std::string(e.what())); | ||||||||
return false; | ||||||||
} | ||||||||
void SlimeVRDriver::Bridge::setBridgeError() { | ||||||||
this->m_eState = EState::BRIDGE_ERROR; | ||||||||
} | ||||||||
|
||||||||
BridgeStatus runBridgeFrame(SlimeVRDriver::VRDriver& driver) { | ||||||||
void SlimeVRDriver::Bridge::resetPipe() { | ||||||||
try { | ||||||||
if (!client.IsOpen()) { | ||||||||
client.Open(SOCKET_PATH); | ||||||||
} | ||||||||
client.UpdateOnce(); | ||||||||
|
||||||||
if (!client.IsOpen()) { | ||||||||
return BRIDGE_DISCONNECTED; | ||||||||
} | ||||||||
return BRIDGE_CONNECTED; | ||||||||
client.Close(); | ||||||||
client.Open(SOCKET_PATH); | ||||||||
this->m_pDriver->Log("Pipe was reset"); | ||||||||
} catch (const std::exception& e) { | ||||||||
client.Close(); | ||||||||
driver.Log("bridge error: " + std::string(e.what())); | ||||||||
return BRIDGE_ERROR; | ||||||||
this->m_pDriver->Log("bridge error: " + std::string(e.what())); | ||||||||
setBridgeError(); | ||||||||
} | ||||||||
|
||||||||
} | ||||||||
void SlimeVRDriver::Bridge::attemptPipeConnect() { | ||||||||
// Open pipe only called the first time | ||||||||
this->resetPipe(); | ||||||||
} | ||||||||
|
||||||||
|
||||||||
#endif // linux |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, we do not use (systems) hungarian notation in the driver. To be fair though, this is somewhat of a nitpick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should've included the suggestion in the original comment.
I don't want to make suggestions for every occurrence, because basically every single variable/parameter that you added does not follow the style of the rest of the driver, and I don't want to spam the PR.