-
Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathGCParallelTask.h
162 lines (134 loc) · 5.21 KB
/
GCParallelTask.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gc_GCParallelTask_h
#define gc_GCParallelTask_h
#include "mozilla/Move.h"
#include "js/TypeDecls.h"
#include "threading/ProtectedData.h"
namespace js {
class AutoLockHelperThreadState;
// A generic task used to dispatch work to the helper thread system.
// Users supply a function pointer to call.
//
// Note that we don't use virtual functions here because destructors can write
// the vtable pointer on entry, which can causes races if synchronization
// happens there.
class GCParallelTask {
public:
using TaskFunc = void (*)(GCParallelTask*);
private:
JSRuntime* const runtime_;
TaskFunc func_;
// The state of the parallel computation.
enum class State { NotStarted, Dispatched, Finishing, Finished };
UnprotectedData<State> state_;
// Amount of time this task took to execute.
MainThreadOrGCTaskData<mozilla::TimeDuration> duration_;
explicit GCParallelTask(const GCParallelTask&) = delete;
protected:
// A flag to signal a request for early completion of the off-thread task.
mozilla::Atomic<bool, mozilla::MemoryOrdering::ReleaseAcquire,
mozilla::recordreplay::Behavior::DontPreserve>
cancel_;
public:
explicit GCParallelTask(JSRuntime* runtime, TaskFunc func)
: runtime_(runtime),
func_(func),
state_(State::NotStarted),
duration_(nullptr),
cancel_(false) {}
GCParallelTask(GCParallelTask&& other)
: runtime_(other.runtime_),
func_(other.func_),
state_(other.state_),
duration_(nullptr),
cancel_(false) {}
// Derived classes must override this to ensure that join() gets called
// before members get destructed.
~GCParallelTask();
JSRuntime* runtime() { return runtime_; }
// Time spent in the most recent invocation of this task.
mozilla::TimeDuration duration() const { return duration_; }
// The simple interface to a parallel task works exactly like pthreads.
MOZ_MUST_USE bool start();
void join();
// If multiple tasks are to be started or joined at once, it is more
// efficient to take the helper thread lock once and use these methods.
MOZ_MUST_USE bool startWithLockHeld(AutoLockHelperThreadState& locked);
void joinWithLockHeld(AutoLockHelperThreadState& locked);
// Instead of dispatching to a helper, run the task on the current thread.
void runFromMainThread(JSRuntime* rt);
void joinAndRunFromMainThread(JSRuntime* rt);
// If the task is not already running, either start it or run it on the main
// thread if that fails.
void startOrRunIfIdle(AutoLockHelperThreadState& lock);
// Dispatch a cancelation request.
void cancelAndWait() {
cancel_ = true;
join();
}
// Check if a task is running and has not called setFinishing().
bool isRunningWithLockHeld(const AutoLockHelperThreadState& lock) const {
return isDispatched(lock);
}
bool isRunning() const;
private:
void assertNotStarted() const {
// Don't lock here because that adds extra synchronization in debug
// builds that may hide bugs. There's no race if the assertion passes.
MOZ_ASSERT(state_ == State::NotStarted);
}
bool isNotStarted(const AutoLockHelperThreadState& lock) const {
return state_ == State::NotStarted;
}
bool isDispatched(const AutoLockHelperThreadState& lock) const {
return state_ == State::Dispatched;
}
bool isFinished(const AutoLockHelperThreadState& lock) const {
return state_ == State::Finished;
}
void setDispatched(const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(state_ == State::NotStarted);
state_ = State::Dispatched;
}
void setFinished(const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(state_ == State::Dispatched || state_ == State::Finishing);
state_ = State::Finished;
}
void setNotStarted(const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(state_ == State::Finished);
state_ = State::NotStarted;
}
void runTask() { func_(this); }
protected:
// Can be called to indicate that although the task is still
// running, it is about to finish.
void setFinishing(const AutoLockHelperThreadState& lock) {
MOZ_ASSERT(state_ == State::NotStarted || state_ == State::Dispatched);
if (state_ == State::Dispatched) {
state_ = State::Finishing;
}
}
// This should be friended to HelperThread, but cannot be because it
// would introduce several circular dependencies.
public:
void runFromHelperThread(AutoLockHelperThreadState& locked);
};
// CRTP template to handle cast to derived type when calling run().
template <typename Derived>
class GCParallelTaskHelper : public GCParallelTask {
public:
explicit GCParallelTaskHelper(JSRuntime* runtime)
: GCParallelTask(runtime, &runTaskTyped) {}
GCParallelTaskHelper(GCParallelTaskHelper&& other)
: GCParallelTask(std::move(other)) {}
private:
static void runTaskTyped(GCParallelTask* task) {
static_cast<Derived*>(task)->run();
}
};
} /* namespace js */
#endif /* gc_GCParallelTask_h */