Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b043479

Browse files
authored
[orc-rt] Fix some Session::shutdown bugs. (#177528)
All calls to Session::shutdown were enquing their on-shutdown-complete callbacks in Session's ShutdownInfo struct, but this queue is only drained once by the thread that initiates shutdown. After the queue is drained, subsequent calls to Session::shutdown were enquing their callbacks in a queue that would never be drained. This patch updates Session::shutdown to check whether shutdown has completed already and, if so, run the on-shutdown-complete immediately. This patch also fixes a concurrency bug: Session::shutdownComplete was accessing SI->OnCompletes outside the session mutex, but this could lead to corruption of SI->OnCompletes if a concurrent call to Session::shutdown tried to enqueue a new callback to SI->OnCompletes concurrently. This has been fixed by moving the SI->OnCompletes queue to a new variable under the Session mutex, then draining the new queue outside the mutex. (No testcase yet: this was discovered by observation, and replicating the bug would depend on timing).
1 parent dcd6468 commit b043479

2 files changed

Lines changed: 40 additions & 8 deletions

File tree

orc-rt/lib/executor/Session.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,40 @@ Session::ControllerAccess::~ControllerAccess() = default;
1919
Session::~Session() { waitForShutdown(); }
2020

2121
void Session::shutdown(OnShutdownCompleteFn OnShutdownComplete) {
22+
assert(OnShutdownComplete && "OnShutdownComplete must be set");
23+
2224
// Safe to call concurrently / redundantly.
2325
detachFromController();
2426

2527
{
2628
std::scoped_lock<std::mutex> Lock(M);
2729
if (SI) {
30+
// SI exists: someone called shutdown already. If the shutdown is not yet
31+
// complete then just add OnShutdownComplete to the list of pending
32+
// callbacks for the in-progress shutdown, then return.
33+
// (If the shutdown is already complete then we'll run the handler
34+
// directly below).
35+
if (!SI->Complete)
36+
return SI->OnCompletes.push_back(std::move(OnShutdownComplete));
37+
} else {
38+
// SI does not exist: We're the first to call shutdown. Create a
39+
// ShutdownInfo struct and add OnShutdownComplete to the list of pending
40+
// callbacks, then call shutdownNext below (outside the lock).
41+
SI = std::make_unique<ShutdownInfo>();
2842
SI->OnCompletes.push_back(std::move(OnShutdownComplete));
29-
return;
43+
std::swap(SI->ResourceMgrs, ResourceMgrs);
3044
}
31-
32-
SI = std::make_unique<ShutdownInfo>();
33-
SI->OnCompletes.push_back(std::move(OnShutdownComplete));
34-
std::swap(SI->ResourceMgrs, ResourceMgrs);
3545
}
3646

47+
// OnShutdownComplete is set (i.e. not moved into the list of pending
48+
// callbacks). This can only happen if shutdown is already complete. Call
49+
// OnComplete directly and return.
50+
if (OnShutdownComplete)
51+
return OnShutdownComplete();
52+
53+
// OnShutdownComplete is _not_ set (i.e. was moved into the list of pending
54+
// handlers), and we didn't return under the lock above, so we must be
55+
// responsible for the shutdown. Call shutdownNext.
3756
shutdownNext(Error::success());
3857
}
3958

@@ -87,14 +106,16 @@ void Session::shutdownComplete() {
87106

88107
TmpDispatcher->shutdown();
89108

90-
for (auto &OnShutdownComplete : SI->OnCompletes)
91-
OnShutdownComplete();
92-
109+
std::vector<OnShutdownCompleteFn> OnCompletes;
93110
{
94111
std::lock_guard<std::mutex> Lock(M);
95112
SI->Complete = true;
113+
OnCompletes = std::move(SI->OnCompletes);
96114
}
97115

116+
for (auto &OnShutdownComplete : OnCompletes)
117+
OnShutdownComplete();
118+
98119
SI->CompleteCV.notify_all();
99120
}
100121

orc-rt/unittests/SessionTest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,14 @@ TEST(ControllerAccessTest, CallFromController) {
464464

465465
S.waitForShutdown();
466466
}
467+
468+
TEST(ControllerAccessTest, RedundantAsyncShutdown) {
469+
// Check that redundant calls to shutdown have their callbacks run.
470+
std::deque<std::unique_ptr<Task>> Tasks;
471+
Session S(std::make_unique<EnqueueingDispatcher>(Tasks), noErrors);
472+
S.waitForShutdown();
473+
474+
bool RedundantCallbackRan = false;
475+
S.shutdown([&]() { RedundantCallbackRan = true; });
476+
EXPECT_TRUE(RedundantCallbackRan);
477+
}

0 commit comments

Comments
 (0)