Skip to content
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

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

bakerstu
Copy link
Owner

@bakerstu bakerstu commented Jan 16, 2025

  • Added support on the server side.
  • Added support on the client side.
  • Added unit test coverage.

@bakerstu bakerstu requested a review from atanisoft January 16, 2025 23:31
Copy link
Collaborator

@atanisoft atanisoft left a 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?

src/openlcb/MemoryConfig.hxx Outdated Show resolved Hide resolved
src/openlcb/MemoryConfig.hxx Outdated Show resolved Hide resolved
src/openlcb/MemoryConfig.hxx Outdated Show resolved Hide resolved
src/openlcb/MemoryConfigClient.hxx Outdated Show resolved Hide resolved
@bobjacobsen
Copy link
Collaborator

bobjacobsen commented Jan 17, 2025

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.

@bakerstu bakerstu changed the title [WIP] Memory Config Timeout Support (Datagrams) Memory Config Timeout Support (Datagrams) Jan 18, 2025
@bakerstu bakerstu requested a review from balazsracz January 18, 2025 20:29
@atanisoft
Copy link
Collaborator

With the latest code I'm seeing the following warning/deprecation notice:

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());

Not sure how best we should handle this other than using casting to an integer and then back to ResponseFlag.

@bakerstu
Copy link
Owner Author

With the latest code I'm seeing the following warning/deprecation notice:

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());

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:

  1. Static cast the operation.
  2. Create a custom operator override.
  3. Rework the enumerated types to be a bit more consistent and compatible, maybe even built off each other a bit. Possibly consider structs with bitfields.
    1. This would still likely require some custom operator overrides.
    2. One problem with bitfields is that there is no standard bit ordering enforced by the C/C++ standards. It happens that pretty much everyone uses the same order though, so maybe this is not so much an issue.

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?

@atanisoft
Copy link
Collaborator

atanisoft commented Jan 20, 2025 via email

"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());"
@bakerstu
Copy link
Owner Author

I added the static_cast for both read and write timeout cases.

*
* CANbus datagram parser and renderer flows.
* Datagram parser and renderer flows.
Copy link
Collaborator

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.
Copy link
Collaborator

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.
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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())));
Copy link
Collaborator

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(
Copy link
Collaborator

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.
Copy link
Collaborator

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())));
Copy link
Collaborator

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())
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants