Skip to content
This repository was archived by the owner on Dec 15, 2018. It is now read-only.

Commit e27060d

Browse files
committed
Bug 1083101 - Make SyncObject's waiting jobs list lock-free. r=jrmuizel
1 parent 6198b79 commit e27060d

File tree

2 files changed

+41
-20
lines changed

2 files changed

+41
-20
lines changed

gfx/2d/JobScheduler.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ JobScheduler::GetQueueForJob(Job* aJob)
8484
}
8585

8686
Job::Job(SyncObject* aStart, SyncObject* aCompletion, WorkerThread* aThread)
87-
: mStartSync(aStart)
87+
: mNextWaitingJob(nullptr)
88+
, mStartSync(aStart)
8889
, mCompletionSync(aCompletion)
8990
, mPinToThread(aThread)
9091
{
@@ -124,6 +125,7 @@ SetEventJob::~SetEventJob()
124125

125126
SyncObject::SyncObject(uint32_t aNumPrerequisites)
126127
: mSignals(aNumPrerequisites)
128+
, mFirstWaitingJob(nullptr)
127129
#ifdef DEBUG
128130
, mNumPrerequisites(aNumPrerequisites)
129131
, mAddedPrerequisites(0)
@@ -132,7 +134,7 @@ SyncObject::SyncObject(uint32_t aNumPrerequisites)
132134

133135
SyncObject::~SyncObject()
134136
{
135-
MOZ_ASSERT(mWaitingJobs.size() == 0);
137+
MOZ_ASSERT(mFirstWaitingJob == nullptr);
136138
}
137139

138140
bool
@@ -184,28 +186,41 @@ SyncObject::Signal()
184186
void
185187
SyncObject::AddWaitingJob(Job* aJob)
186188
{
187-
CriticalSectionAutoEnter lock(&mWaitingJobsSection);
188-
mWaitingJobs.push_back(aJob);
189+
// Push (using atomics) the task into the list of waiting tasks.
190+
for (;;) {
191+
Job* first = mFirstWaitingJob;
192+
aJob->mNextWaitingJob = first;
193+
if (mFirstWaitingJob.compareExchange(first, aJob)) {
194+
break;
195+
}
196+
}
189197
}
190198

191199
void SyncObject::SubmitWaitingJobs()
192200
{
193-
std::vector<Job*> tasksToSubmit;
194-
{
195-
// Scheduling the tasks can cause code that modifies <this>'s reference
196-
// count to run concurrently, and cause the caller of this function to
197-
// be owned by another thread. We need to make sure the reference count
198-
// does not reach 0 on another thread before mWaitingJobs.clear(), so
199-
// hold a strong ref to prevent that!
200-
RefPtr<SyncObject> kungFuDeathGrip(this);
201-
202-
CriticalSectionAutoEnter lock(&mWaitingJobsSection);
203-
tasksToSubmit = Move(mWaitingJobs);
204-
mWaitingJobs.clear();
201+
// Scheduling the tasks can cause code that modifies <this>'s reference
202+
// count to run concurrently, and cause the caller of this function to
203+
// be owned by another thread. We need to make sure the reference count
204+
// does not reach 0 on another thread before the end of this method, so
205+
// hold a strong ref to prevent that!
206+
RefPtr<SyncObject> kungFuDeathGrip(this);
207+
208+
// First atomically swap mFirstWaitingJob and waitingJobs...
209+
Job* waitingJobs = nullptr;
210+
for (;;) {
211+
waitingJobs = mFirstWaitingJob;
212+
if (mFirstWaitingJob.compareExchange(waitingJobs, nullptr)) {
213+
break;
214+
}
205215
}
206216

207-
for (Job* task : tasksToSubmit) {
208-
JobScheduler::GetQueueForJob(task)->SubmitJob(task);
217+
// ... and submit all of the waiting tasks in waitingJob now that they belong
218+
// to this thread.
219+
while (waitingJobs) {
220+
Job* next = waitingJobs->mNextWaitingJob;
221+
waitingJobs->mNextWaitingJob = nullptr;
222+
JobScheduler::GetQueueForJob(waitingJobs)->SubmitJob(waitingJobs);
223+
waitingJobs = next;
209224
}
210225
}
211226

gfx/2d/JobScheduler.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,16 @@ class Job {
109109
WorkerThread* GetWorkerThread() { return mPinToThread; }
110110

111111
protected:
112+
// An intrusive linked list of tasks waiting for a sync object to enter the
113+
// signaled state. When the task is not waiting for a sync object, mNextWaitingJob
114+
// should be null. This is only accessed from the thread that owns the task.
115+
Job* mNextWaitingJob;
116+
112117
RefPtr<SyncObject> mStartSync;
113118
RefPtr<SyncObject> mCompletionSync;
114119
WorkerThread* mPinToThread;
120+
121+
friend class SyncObject;
115122
};
116123

117124
class EventObject;
@@ -205,9 +212,8 @@ class SyncObject final : public external::AtomicRefCounted<SyncObject> {
205212

206213
void SubmitWaitingJobs();
207214

208-
std::vector<Job*> mWaitingJobs;
209-
CriticalSection mWaitingJobsSection; // for concurrent access to mWaintingJobs
210215
Atomic<int32_t> mSignals;
216+
Atomic<Job*> mFirstWaitingJob;
211217

212218
#ifdef DEBUG
213219
uint32_t mNumPrerequisites;

0 commit comments

Comments
 (0)