-
Notifications
You must be signed in to change notification settings - Fork 30
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
Memory Config Timeout Support (Datagrams) #809
base: master
Are you sure you want to change the base?
Conversation
bakerstu
commented
Jan 16, 2025
•
edited
Loading
edited
- Added support on the server side.
- Added support on the client side.
- Added unit test coverage.
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.
Looks good, will do some testing over this weekend.
@bobjacobsen can you confirm if JMRI is similarly handling timeout hints on datagram responses so I can test through that as well?
@atanisoft I'm away from my setup at the moment, but I don't think JMRI (OpenLCB_Java) is doing this correctly. Will check, but it might be a day or two. If not, that needs to be fixed. There are some other things in the handling of the memory configuration protocol that need fixing too, so I'll try to do them all at the same time. |
With the latest code I'm seeing the following warning/deprecation notice:
Not sure how best we should handle this other than using casting to an integer and then back to ResponseFlag. |
@atanisoft This is a good find. We should fix this. I can think of three possible solutions:
I very much would like to cleanup these enumerated types. I think they are not very well laid out, and could have been better designed. That said, I think it is too much of a change at this point. Therefore, I prefer solution 1. It is brute force, but at least self documenting in what the operation is doing. WDYT? |
I'm fine with opting for static_cast for now and seeing what we can do outside this PR as a better fit. I know C++20 has a bit better handling of enums, which may rely on extending standard variable types like uint8_t or similar.
|
"src/openlcb/MemoryConfig.hxx:1011:43: warning: bitwise operation between different enumeration types ‘openlcb::DatagramClient::ResponseFlag’ and ‘openlcb::DatagramDefs::Flag’ is deprecated [-Wdeprecated-enum-enum-conversion] 1011 | DatagramClient::REPLY_PENDING | space->get_write_timeout());"
I added the static_cast for both read and write timeout cases. |
* | ||
* CANbus datagram parser and renderer flows. | ||
* Datagram parser and renderer flows. |
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.
// These are independent of the transport method. For transport-specific renderers, see DatagranCan.cxx and Datagram Tcp.cxx.
@@ -84,6 +84,21 @@ public: | |||
{ | |||
return true; | |||
} | |||
|
|||
/// Get the read timeout. Default is no timeout. | |||
/// @return The read timeout to reply with. |
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.
// , for example DatagramDefs::TIMEOUT_16 for 16 seconds.
* MemorySpace::ERROR_AGAIN, and calls the Notifiable @param again when a | ||
* re-try makes sense. The caller should call write once more, with the | ||
* offset adjusted with the previously returned bytes. */ | ||
/// Write data to the address space. |
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.
// Called by the memory config service for incoming write requests.
/// @param destination memory space offset address to write to | ||
/// @param data data to write | ||
/// @param len length of write data in bytes | ||
/// @param error Error code that can be passed back. if != 0, the operation |
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.
I think this is a case where it is language-wise unclear whether an imperative statement applies to the caller or the callee. For this reason there is a rule in the Google style guide that function comments always have to be phrased in third person singular, which makes it clear that the description is about the function's implementation (i.e., the callee).
// @param error is the output argument for the error code. If the operation succeeded,
// sets *error to zero. If the operation failed, sets *error to non-zero. If the operation needs
// to be continued, then sets error to ERROR_AGAIN, and later calls the Notifiable when ready
// to continue.
/// @param error Error code that can be passed back. if != 0, the operation | ||
/// has failed. If the operation needs to be continued, then set | ||
/// error to ERROR_AGAIN, and later call the Notifiable to continue. | ||
/// @param again notify() when the caller should call the write again, with |
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.
@param again Used only when *error was set to ERROR_AGAIN. Calls again.notify() when the operation is ready to be continued. The caller should then call the write again, with the offset (source) adjusted with the previously returned bytes.
EXPECT_CALL( | ||
tSpace_, write(0, _, test_payload.size(), _, NotifyCapture(&done))) | ||
.WillOnce(DoAll( | ||
SetArgPointee<3>(0x3FFF), Return(test_payload.size()))); |
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.
I think this should be Return(0) since you are not actually accepting the payload
clock.advance(MSEC_TO_NSEC(3200)); | ||
wait(); | ||
EXPECT_EQ(Defs::OPENMRN_TIMEOUT, b->data()->resultCode); | ||
EXPECT_TRUE( |
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.
EXPECT_NE(0, memcmp())
testing::Mock::VerifyAndClearExpectations(&tSpace_); | ||
|
||
// | ||
// Pass back timeout "32 seconds". Wait 31 seconds to reply. |
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.
this replies inline instead of after 31 seconds. You would need to capture done, wait 31 seconds, then call done notifiable.
This would need two expected calls on write(...) with the first one returning 0.
EXPECT_CALL( | ||
tSpace_, write(0, _, test_payload.size(), _, NotifyCapture(&done))) | ||
.WillOnce(DoAll( | ||
SetArgPointee<3>(0x3FFF), Return(test_payload.size()))); |
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.
Return(0)
@@ -402,7 +405,8 @@ private: | |||
|
|||
Action read_response_timeout() | |||
{ | |||
if (responseCode_ & DatagramClient::OPERATION_PENDING) | |||
if (responseCode_ & DatagramClient::OPERATION_PENDING || | |||
!timer_.is_triggered()) |
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.
if the read finishes upon the first attempt, the timer is never started.
If this code is really needed, then we have to call timer_.set_triggered() on that path.