Skip to content

Commit 39e0692

Browse files
committed
remove dispatchOutstandingMUs and other cleanups to scheduling
1 parent 629e3e2 commit 39e0692

File tree

4 files changed

+26
-60
lines changed

4 files changed

+26
-60
lines changed

llvm/include/llvm/ExecutionEngine/Orc/Core.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,8 +1707,6 @@ class ExecutionSession {
17071707
logAllUnhandledErrors(std::move(Err), errs(), "JIT session error: ");
17081708
}
17091709

1710-
void dispatchOutstandingMUs();
1711-
17121710
static std::unique_ptr<MaterializationResponsibility>
17131711
createMaterializationResponsibility(ResourceTracker &RT,
17141712
SymbolFlagsMap Symbols,
@@ -1815,13 +1813,6 @@ class ExecutionSession {
18151813
std::vector<JITDylibSP> JDs;
18161814
WaitingOnGraph G;
18171815

1818-
// FIXME: Remove this (and runOutstandingMUs) once the linking layer works
1819-
// with callbacks from asynchronous queries.
1820-
mutable std::recursive_mutex OutstandingMUsMutex;
1821-
std::vector<std::pair<std::unique_ptr<MaterializationUnit>,
1822-
std::unique_ptr<MaterializationResponsibility>>>
1823-
OutstandingMUs;
1824-
18251816
mutable std::mutex JITDispatchHandlersMutex;
18261817
DenseMap<ExecutorAddr, std::shared_ptr<JITDispatchHandlerFunction>>
18271818
JITDispatchHandlers;

llvm/include/llvm/ExecutionEngine/Orc/TaskDispatch.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ class LLVM_ABI DynamicThreadPoolTaskDispatcher : public TaskDispatcher {
162162
bool Shutdown = false;
163163
size_t Outstanding = 0;
164164
std::condition_variable OutstandingCV;
165+
SmallVector<future_base *> WaitingFutures;
165166

166167
std::optional<size_t> MaxMaterializationThreads;
167168
size_t NumMaterializationThreads = 0;
@@ -171,13 +172,16 @@ class LLVM_ABI DynamicThreadPoolTaskDispatcher : public TaskDispatcher {
171172

172173
#endif // LLVM_ENABLE_THREADS
173174

175+
/// ORC-TaskDispatch aware promise/future class that can help with task dispatch while waiting
176+
// TODO: docs
177+
174178
/// Status for future/promise state
175179
enum class FutureStatus : uint8_t { NotReady = 0, Ready = 1, NotValid = 2 };
176180

177181
/// Type-erased base class for futures
178182
class future_base {
179183
public:
180-
bool is_ready() const {
184+
bool ready() const {
181185
return state_->status_.load(std::memory_order_acquire) !=
182186
FutureStatus::NotReady;
183187
}
@@ -191,9 +195,9 @@ class future_base {
191195
/// Wait for the future to be ready, helping with task dispatch
192196
void wait(TaskDispatcher &D) {
193197
// Keep helping with task dispatch until our future is ready
194-
if (!is_ready())
198+
if (!ready())
195199
D.work_until(*this);
196-
assert(is_ready());
200+
assert(ready());
197201
}
198202

199203
protected:

llvm/lib/ExecutionEngine/Orc/Core.cpp

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,11 +1784,6 @@ void ExecutionSession::lookup(
17841784
});
17851785
});
17861786

1787-
// lookup can be re-entered recursively if running on a single thread. Run any
1788-
// outstanding MUs in case this query depends on them, otherwise this lookup
1789-
// will starve waiting for a result from an MU that is stuck in the queue.
1790-
dispatchOutstandingMUs();
1791-
17921787
auto Unresolved = std::move(Symbols);
17931788
auto Q = std::make_shared<AsynchronousSymbolQuery>(Unresolved, RequiredState,
17941789
std::move(NotifyComplete));
@@ -2041,32 +2036,6 @@ bool ExecutionSession::verifySessionState(Twine Phase) {
20412036
}
20422037
#endif // EXPENSIVE_CHECKS
20432038

2044-
void ExecutionSession::dispatchOutstandingMUs() {
2045-
LLVM_DEBUG(dbgs() << "Dispatching MaterializationUnits...\n");
2046-
while (true) {
2047-
std::optional<std::pair<std::unique_ptr<MaterializationUnit>,
2048-
std::unique_ptr<MaterializationResponsibility>>>
2049-
JMU;
2050-
2051-
{
2052-
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
2053-
if (!OutstandingMUs.empty()) {
2054-
JMU.emplace(std::move(OutstandingMUs.back()));
2055-
OutstandingMUs.pop_back();
2056-
}
2057-
}
2058-
2059-
if (!JMU)
2060-
break;
2061-
2062-
assert(JMU->first && "No MU?");
2063-
LLVM_DEBUG(dbgs() << " Dispatching \"" << JMU->first->getName() << "\"\n");
2064-
dispatchTask(std::make_unique<MaterializationTask>(std::move(JMU->first),
2065-
std::move(JMU->second)));
2066-
}
2067-
LLVM_DEBUG(dbgs() << "Done dispatching MaterializationUnits.\n");
2068-
}
2069-
20702039
Error ExecutionSession::removeResourceTracker(ResourceTracker &RT) {
20712040
LLVM_DEBUG({
20722041
dbgs() << "In " << RT.getJITDylib().getName() << " removing tracker "
@@ -2640,8 +2609,6 @@ void ExecutionSession::OL_completeLookup(
26402609

26412610
// Move the collected MUs to the OutstandingMUs list.
26422611
if (!CollectedUMIs.empty()) {
2643-
std::lock_guard<std::recursive_mutex> Lock(OutstandingMUsMutex);
2644-
26452612
LLVM_DEBUG(dbgs() << "Adding MUs to dispatch:\n");
26462613
for (auto &KV : CollectedUMIs) {
26472614
LLVM_DEBUG({
@@ -2653,8 +2620,9 @@ void ExecutionSession::OL_completeLookup(
26532620
auto MR = createMaterializationResponsibility(
26542621
*UMI->RT, std::move(UMI->MU->SymbolFlags),
26552622
std::move(UMI->MU->InitSymbol));
2656-
OutstandingMUs.push_back(
2657-
std::make_pair(std::move(UMI->MU), std::move(MR)));
2623+
LLVM_DEBUG(dbgs() << " Dispatching \"" << UMI->MU->getName() << "\"\n");
2624+
dispatchTask(std::make_unique<MaterializationTask>(std::move(UMI->MU),
2625+
std::move(MR)));
26582626
}
26592627
}
26602628
} else
@@ -2680,8 +2648,6 @@ void ExecutionSession::OL_completeLookup(
26802648
LLVM_DEBUG(dbgs() << "Completing query\n");
26812649
Q->handleComplete(*this);
26822650
}
2683-
2684-
dispatchOutstandingMUs();
26852651
}
26862652

26872653
void ExecutionSession::OL_completeLookupFlags(

llvm/lib/ExecutionEngine/Orc/TaskDispatch.cpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void InPlaceTaskDispatcher::shutdown() {
4040
}
4141

4242
void InPlaceTaskDispatcher::work_until(future_base &F) {
43-
while (!F.is_ready()) {
43+
while (!F.ready()) {
4444
// First, process any tasks in our local queue
4545
// Process in LIFO order (most recently added first) to avoid deadlocks
4646
// when tasks have dependencies on each other
@@ -54,7 +54,7 @@ void InPlaceTaskDispatcher::work_until(future_base &F) {
5454
{
5555
std::lock_guard<std::mutex> Lock(DispatchMutex);
5656
bool ShouldNotify = llvm::any_of(
57-
WaitingFutures, [](future_base *F) { return F->is_ready(); });
57+
WaitingFutures, [](future_base *F) { return F->ready(); });
5858
if (ShouldNotify) {
5959
WaitingFutures.clear();
6060
WorkFinishedCV.notify_all();
@@ -63,23 +63,23 @@ void InPlaceTaskDispatcher::work_until(future_base &F) {
6363
#endif
6464

6565
// Check if our future is now ready
66-
if (F.is_ready())
66+
if (F.ready())
6767
return;
6868
}
6969

7070
// If we get here, our queue is empty but the future isn't ready
71-
// We need to wait for other threads to finish work that might complete our
71+
// We need to wait for other threads to finish work that should complete our
7272
// future
7373
#if LLVM_ENABLE_THREADS
7474
{
7575
std::unique_lock<std::mutex> Lock(DispatchMutex);
7676
WaitingFutures.push_back(&F);
77-
WorkFinishedCV.wait(Lock, [&F]() { return F.is_ready(); });
77+
WorkFinishedCV.wait(Lock, [&F]() { return F.ready(); });
7878
}
7979
#else
8080
// Without threading, if our queue is empty and future isn't ready,
81-
// we can't make progress
82-
return;
81+
// the library must have forgotten to schedule it, causing deadlock here
82+
report_fatal_error("waiting for future that was never dispatched");
8383
#endif
8484
}
8585
}
@@ -140,6 +140,13 @@ void DynamicThreadPoolTaskDispatcher::dispatch(std::unique_ptr<Task> T) {
140140
--NumMaterializationThreads;
141141
--Outstanding;
142142

143+
bool ShouldNotify = Outstanding == 0 || llvm::any_of(
144+
WaitingFutures, [](future_base *F) { return F->ready(); });
145+
if (ShouldNotify) {
146+
WaitingFutures.clear();
147+
OutstandingCV.notify_all();
148+
}
149+
143150
if (!MaterializationTaskQueue.empty() && canRunMaterializationTaskNow()) {
144151
// If there are any materialization tasks running then steal that work.
145152
T = std::move(MaterializationTaskQueue.front());
@@ -153,8 +160,6 @@ void DynamicThreadPoolTaskDispatcher::dispatch(std::unique_ptr<Task> T) {
153160
TaskKind = Idle;
154161
++Outstanding;
155162
} else {
156-
if (Outstanding == 0)
157-
OutstandingCV.notify_all();
158163
return;
159164
}
160165
}
@@ -178,9 +183,9 @@ bool DynamicThreadPoolTaskDispatcher::canRunIdleTaskNow() {
178183
}
179184

180185
void DynamicThreadPoolTaskDispatcher::work_until(future_base &F) {
181-
// TODO: Implement efficient work_until for DynamicThreadPoolTaskDispatcher
182186
std::unique_lock<std::mutex> Lock(DispatchMutex);
183-
OutstandingCV.wait(Lock, [this]() { return Outstanding == 0; });
187+
WaitingFutures.push_back(&F);
188+
OutstandingCV.wait(Lock, [&F]() { return F.ready(); });
184189
}
185190

186191
#endif

0 commit comments

Comments
 (0)