Skip to content

Commit b84d773

Browse files
committed
[Parallel] Revert sequential task changes
https://reviews.llvm.org/D148728 introduced `bool Sequential` to unify `execute` and the old `spawn` without argument. However, sequential tasks might be executed by any worker thread (non-deterministic), leading to non-determinism output for ld.lld -z nocombreloc (see https://reviews.llvm.org/D133003). In addition, the extra member variables have overhead. This sequential task has only been used for lld parallel relocation scanning. This patch restores the behavior before https://reviews.llvm.org/D148728 . Fix #105958 Pull Request: #109084
1 parent 0659fd9 commit b84d773

File tree

4 files changed

+48
-71
lines changed

4 files changed

+48
-71
lines changed

lld/ELF/Relocations.cpp

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,30 +1647,44 @@ template <class ELFT> void elf::scanRelocations() {
16471647
bool serial = !config->zCombreloc || config->emachine == EM_MIPS ||
16481648
config->emachine == EM_PPC64;
16491649
parallel::TaskGroup tg;
1650-
for (ELFFileBase *f : ctx.objectFiles) {
1651-
auto fn = [f]() {
1650+
auto outerFn = [&]() {
1651+
for (ELFFileBase *f : ctx.objectFiles) {
1652+
auto fn = [f]() {
1653+
RelocationScanner scanner;
1654+
for (InputSectionBase *s : f->getSections()) {
1655+
if (s && s->kind() == SectionBase::Regular && s->isLive() &&
1656+
(s->flags & SHF_ALLOC) &&
1657+
!(s->type == SHT_ARM_EXIDX && config->emachine == EM_ARM))
1658+
scanner.template scanSection<ELFT>(*s);
1659+
}
1660+
};
1661+
if (serial)
1662+
fn();
1663+
else
1664+
tg.spawn(fn);
1665+
}
1666+
auto scanEH = [] {
16521667
RelocationScanner scanner;
1653-
for (InputSectionBase *s : f->getSections()) {
1654-
if (s && s->kind() == SectionBase::Regular && s->isLive() &&
1655-
(s->flags & SHF_ALLOC) &&
1656-
!(s->type == SHT_ARM_EXIDX && config->emachine == EM_ARM))
1657-
scanner.template scanSection<ELFT>(*s);
1668+
for (Partition &part : ctx.partitions) {
1669+
for (EhInputSection *sec : part.ehFrame->sections)
1670+
scanner.template scanSection<ELFT>(*sec, /*isEH=*/true);
1671+
if (part.armExidx && part.armExidx->isLive())
1672+
for (InputSection *sec : part.armExidx->exidxSections)
1673+
if (sec->isLive())
1674+
scanner.template scanSection<ELFT>(*sec);
16581675
}
16591676
};
1660-
tg.spawn(fn, serial);
1661-
}
1662-
1663-
tg.spawn([] {
1664-
RelocationScanner scanner;
1665-
for (Partition &part : ctx.partitions) {
1666-
for (EhInputSection *sec : part.ehFrame->sections)
1667-
scanner.template scanSection<ELFT>(*sec, /*isEH=*/true);
1668-
if (part.armExidx && part.armExidx->isLive())
1669-
for (InputSection *sec : part.armExidx->exidxSections)
1670-
if (sec->isLive())
1671-
scanner.template scanSection<ELFT>(*sec);
1672-
}
1673-
});
1677+
if (serial)
1678+
scanEH();
1679+
else
1680+
tg.spawn(scanEH);
1681+
};
1682+
// If `serial` is true, call `spawn` to ensure that `scanner` runs in a thread
1683+
// with valid getThreadIndex().
1684+
if (serial)
1685+
tg.spawn(outerFn);
1686+
else
1687+
outerFn();
16741688
}
16751689

16761690
static bool handleNonPreemptibleIfunc(Symbol &sym, uint16_t flags) {

llvm/include/llvm/Support/Parallel.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ class TaskGroup {
9797
// Spawn a task, but does not wait for it to finish.
9898
// Tasks marked with \p Sequential will be executed
9999
// exactly in the order which they were spawned.
100-
// Note: Sequential tasks may be executed on different
101-
// threads, but strictly in sequential order.
102-
void spawn(std::function<void()> f, bool Sequential = false);
100+
void spawn(std::function<void()> f);
103101

104102
void sync() const { L.sync(); }
105103

llvm/lib/Support/Parallel.cpp

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "llvm/Support/Threading.h"
1313

1414
#include <atomic>
15-
#include <deque>
1615
#include <future>
1716
#include <thread>
1817
#include <vector>
@@ -39,7 +38,7 @@ namespace {
3938
class Executor {
4039
public:
4140
virtual ~Executor() = default;
42-
virtual void add(std::function<void()> func, bool Sequential = false) = 0;
41+
virtual void add(std::function<void()> func) = 0;
4342
virtual size_t getThreadCount() const = 0;
4443

4544
static Executor *getDefaultExecutor();
@@ -98,56 +97,34 @@ class ThreadPoolExecutor : public Executor {
9897
static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); }
9998
};
10099

101-
void add(std::function<void()> F, bool Sequential = false) override {
100+
void add(std::function<void()> F) override {
102101
{
103102
std::lock_guard<std::mutex> Lock(Mutex);
104-
if (Sequential)
105-
WorkQueueSequential.emplace_front(std::move(F));
106-
else
107-
WorkQueue.emplace_back(std::move(F));
103+
WorkStack.push_back(std::move(F));
108104
}
109105
Cond.notify_one();
110106
}
111107

112108
size_t getThreadCount() const override { return ThreadCount; }
113109

114110
private:
115-
bool hasSequentialTasks() const {
116-
return !WorkQueueSequential.empty() && !SequentialQueueIsLocked;
117-
}
118-
119-
bool hasGeneralTasks() const { return !WorkQueue.empty(); }
120-
121111
void work(ThreadPoolStrategy S, unsigned ThreadID) {
122112
threadIndex = ThreadID;
123113
S.apply_thread_strategy(ThreadID);
124114
while (true) {
125115
std::unique_lock<std::mutex> Lock(Mutex);
126-
Cond.wait(Lock, [&] {
127-
return Stop || hasGeneralTasks() || hasSequentialTasks();
128-
});
116+
Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); });
129117
if (Stop)
130118
break;
131-
bool Sequential = hasSequentialTasks();
132-
if (Sequential)
133-
SequentialQueueIsLocked = true;
134-
else
135-
assert(hasGeneralTasks());
136-
137-
auto &Queue = Sequential ? WorkQueueSequential : WorkQueue;
138-
auto Task = std::move(Queue.back());
139-
Queue.pop_back();
119+
auto Task = std::move(WorkStack.back());
120+
WorkStack.pop_back();
140121
Lock.unlock();
141122
Task();
142-
if (Sequential)
143-
SequentialQueueIsLocked = false;
144123
}
145124
}
146125

147126
std::atomic<bool> Stop{false};
148-
std::atomic<bool> SequentialQueueIsLocked{false};
149-
std::deque<std::function<void()>> WorkQueue;
150-
std::deque<std::function<void()>> WorkQueueSequential;
127+
std::vector<std::function<void()>> WorkStack;
151128
std::mutex Mutex;
152129
std::condition_variable Cond;
153130
std::promise<void> ThreadsCreated;
@@ -214,16 +191,14 @@ TaskGroup::~TaskGroup() {
214191
L.sync();
215192
}
216193

217-
void TaskGroup::spawn(std::function<void()> F, bool Sequential) {
194+
void TaskGroup::spawn(std::function<void()> F) {
218195
#if LLVM_ENABLE_THREADS
219196
if (Parallel) {
220197
L.inc();
221-
detail::Executor::getDefaultExecutor()->add(
222-
[&, F = std::move(F)] {
223-
F();
224-
L.dec();
225-
},
226-
Sequential);
198+
detail::Executor::getDefaultExecutor()->add([&, F = std::move(F)] {
199+
F();
200+
L.dec();
201+
});
227202
return;
228203
}
229204
#endif

llvm/unittests/Support/ParallelTest.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,6 @@ TEST(Parallel, ForEachError) {
9494
EXPECT_EQ(errText, std::string("asdf\nasdf\nasdf"));
9595
}
9696

97-
TEST(Parallel, TaskGroupSequentialFor) {
98-
size_t Count = 0;
99-
{
100-
parallel::TaskGroup tg;
101-
for (size_t Idx = 0; Idx < 500; Idx++)
102-
tg.spawn([&Count, Idx]() { EXPECT_EQ(Count++, Idx); }, true);
103-
}
104-
EXPECT_EQ(Count, 500ul);
105-
}
106-
10797
#if LLVM_ENABLE_THREADS
10898
TEST(Parallel, NestedTaskGroup) {
10999
// This test checks:

0 commit comments

Comments
 (0)