Skip to content

Make MVEC Lib tolerant to dropped messages. #14

Open
Ryanbahl9 wants to merge 3 commits intomainfrom
ryan/mvec_remove_future_queue
Open

Make MVEC Lib tolerant to dropped messages. #14
Ryanbahl9 wants to merge 3 commits intomainfrom
ryan/mvec_remove_future_queue

Conversation

@Ryanbahl9
Copy link

Description:

  • MVEC lib previously had a future queue that would be filled with expected responses.

The Problem:

  • If a single message got lost on the CAN bus, then the queue of futures would become de-synced compared to the actual resonances arriving from the MVEC.
  • This would cause the futures to be left hanging in the queue for responses that will never come.

The Solution:

  • Get rid of the queue in favor of a single member variable per response type.
  • When the client sends out a message that expects a reply, then MVEC Lib will store a future in the corresponding member variable.
  • If the client tries to send out a new message of the same type while there is an unresolved future for that message type, then:
    • If the future is older than an a configurable timeout (default 500 ms), then:
      • It will return a nullopt to the old future
      • Delete the old future
      • Send the new message and store the new future
    • Else if the future is still waiting and is not past the timeout
      • It will immediately return a nullopt to the current request in favor of waiting for the old request to clear or timeout

Pros for this Method:

  • For the MVEC, there is no need to queue messages. There are only 3 types of messages that expect a response,
    • Relay State Query (Doesn't change the state, no need to queue msgs)
    • Population Query (Doesn't change the state, no need to queue msgs)
    • Relay Command (Sets every relay at once, so each command effectively over writes the last. So queuing is also not needed)

Cons for this Method:

  • Clients need to handle nullopts in futures with relay_query_result.has_value() or relay_query_result == std::nullopt (both will work)
  • Can't queue messages, it will be upto the client / driver to handle sending multiple messages immediately after each other.

TBH, I'm not entirely sure how hard this will be to code around, so lmk if you have suggestions

… internally for each message response type.
auto query_frame = relay_impl_.getRelayQueryMessage();
std::lock_guard<std::mutex> lock(query_mutex_);

// If a request is already in-flight, check if it has timed out
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this logic necessary? Do we actually care to reject or would we rather just resend? The other promise would just time out because we resent and the new one would be used.

But since we only have 1 request in flight at a time, that should not matter?


// Create a new promise and get its future
std::promise<MvecRelayQueryReply> promise;
std::promise<std::optional<MvecRelayQueryReply>> promise;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why promise optional? That seems redundant?

/// @brief Mutex protecting promise queues for thread safety
std::mutex promises_mutex_;
/// @brief Single-slot promise for population query response
std::optional<std::promise<std::optional<MvecPopulationReply>>> population_reply_promise_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if you need optional here! Just don't fulfill the promise, the future will take care of not having a value!

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.

2 participants