Skip to content

overlay v2: fix ipc concurrency race - #5425

Open
marta-lokhova wants to merge 1 commit into
stellar:overlay-v2-sharedfrom
marta-lokhova:ipcdeadlock
Open

overlay v2: fix ipc concurrency race#5425
marta-lokhova wants to merge 1 commit into
stellar:overlay-v2-sharedfrom
marta-lokhova:ipcdeadlock

Conversation

@marta-lokhova

Copy link
Copy Markdown
Contributor

No description provided.

@SirTyson SirTyson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not super familiar with this codebase yet, so I have a couple questions. It seems incorrect to hold the lock for both "sides" of an operation here, but I could be misunderstanding the threading pattern.

// unblock us. The wait is bounded so a lost response degrades to an
// empty candidate list instead of wedging the main thread.
bool gotResponse =
mRequestCv.wait_for(lock, std::chrono::seconds(5), [this] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it still safe to hardcode this as 5 seconds if we're testing much smaller block times?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this doesn't really have anything to do with block time, it just lets core fail gracefully in case overlay gets stuck (which can happen in this prototype phase). I don't feel too strongly about this timeout, but it does help supercluster fail properly (instead of spinning for hours)

Comment thread src/overlay/OverlayIPC.cpp Outdated
// reset afterwards would discard it and we'd wait forever. Lock order
// (mRequestMutex -> mSendMutex) is safe: the reader's handleMessage only
// takes mRequestMutex, and no other path nests these two.
std::unique_lock<std::mutex> lock(mRequestMutex);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we need to actually need these nesting locks, do we? If I understand correctly, the original race was receiving the payload we requested before the reset. Is changing the order of the operations not enough, such that we can do

{
  lock(request);
  reset();
}

{
  lock(send);
  send();
}

{
  lock(request);
  cv.wait(lock);
}

It seems like the real race was reseting in a window in which we may have already received our payload. Just swapping the calling order achieves this by itself right? It seems like a bad idea to hold both a request/send mutex simultaneously, where we could get some weird stall patterns under queue load.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah the race was related to ordering, i agree the mutex nesting wasn't necessary

getTopTransactions sent the GET_TOP_TXS request before locking
mRequestMutex and resetting mPendingResponse. If the reader thread
delivered TOP_TXS_RESPONSE in that window (round-trip is ~0.4ms, so a
descheduled main thread under load hits this), the reset discarded the
response and the wait -- which had no timeout -- blocked the main
thread forever. In simulation tests this wedges every node in the
process; observed as a permanently stuck 15-node 2000 TPS stress test.

Fix: reset the response slot before sending, holding mRequestMutex
across the send (lock order mRequestMutex -> mSendMutex is acyclic:
the reader only takes mRequestMutex), and bound the wait at 5s so a
lost response degrades to an empty candidate list instead of a hung
validator. requestMetrics had the identical reset-after-send race,
previously masked by its timeout as spurious metric-timeout warnings;
apply the same reset-before-send fix there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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