Bug 1083101 - Win32 implementation of the TaskScheduler. r=jrmuizel

This commit is contained in:
Nicolas Silva 2015-09-04 14:27:52 +02:00
Родитель 8f49f122e9
Коммит 63fd1ccf4d
8 изменённых файлов: 308 добавлений и 85 удалений

Просмотреть файл

@ -23,7 +23,7 @@ bool TaskScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
}
for (uint32_t i = 0; i < aNumThreads; ++i) {
sSingleton->mWorkerThreads.push_back(new WorkerThread(sSingleton->mDrawingQueues[i%aNumQueues]));
sSingleton->mWorkerThreads.push_back(WorkerThread::Create(sSingleton->mDrawingQueues[i%aNumQueues]));
}
return true;
}
@ -230,5 +230,31 @@ SyncObject::AddSubsequent(Task* aTask)
#endif
}
WorkerThread::WorkerThread(MultiThreadedTaskQueue* aTaskQueue)
: mQueue(aTaskQueue)
{
aTaskQueue->RegisterThread();
}
void
WorkerThread::Run()
{
for (;;) {
Task* commands = nullptr;
if (!mQueue->WaitForTask(commands)) {
mQueue->UnregisterThread();
return;
}
TaskStatus status = TaskScheduler::ProcessTask(commands);
if (status == TaskStatus::Error) {
// Don't try to handle errors for now, but that's open to discussions.
// I expect errors to be mostly OOM issues.
MOZ_CRASH();
}
}
}
} //namespace
} //namespace

Просмотреть файл

@ -15,11 +15,14 @@
#include "mozilla/gfx/TaskScheduler_posix.h"
#endif
#include <vector>
namespace mozilla {
namespace gfx {
class MultiThreadedTaskQueue;
class SyncObject;
class WorkerThread;
class TaskScheduler {
public:
@ -209,6 +212,20 @@ protected:
Mutex* mMutex;
};
/// Base class for worker threads.
class WorkerThread
{
public:
static WorkerThread* Create(MultiThreadedTaskQueue* aTaskQueue);
virtual ~WorkerThread() {}
void Run();
protected:
WorkerThread(MultiThreadedTaskQueue* aTaskQueue);
MultiThreadedTaskQueue* mQueue;
};
} // namespace
} // namespace

Просмотреть файл

@ -11,6 +11,38 @@ using namespace std;
namespace mozilla {
namespace gfx {
void* ThreadCallback(void* threadData);
class WorkerThreadPosix : public WorkerThread {
public:
WorkerThreadPosix(MultiThreadedTaskQueue* aTaskQueue)
: WorkerThread(aTaskQueue)
{
pthread_create(&mThread, nullptr, ThreadCallback, static_cast<WorkerThread*>(this));
}
~WorkerThreadPosix()
{
pthread_join(mThread, nullptr);
}
protected:
pthread_t mThread;
};
void* ThreadCallback(void* threadData)
{
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
thread->Run();
return nullptr;
}
WorkerThread*
WorkerThread::Create(MultiThreadedTaskQueue* aTaskQueue)
{
return new WorkerThreadPosix(aTaskQueue);
}
MultiThreadedTaskQueue::MultiThreadedTaskQueue()
: mThreadsCount(0)
, mShuttingDown(false)
@ -106,45 +138,6 @@ MultiThreadedTaskQueue::UnregisterThread()
}
}
void* ThreadCallback(void* threadData)
{
WorkerThread* thread = (WorkerThread*)threadData;
thread->Run();
return nullptr;
}
WorkerThread::WorkerThread(MultiThreadedTaskQueue* aTaskQueue)
: mQueue(aTaskQueue)
{
aTaskQueue->RegisterThread();
pthread_create(&mThread, nullptr, ThreadCallback, this);
}
WorkerThread::~WorkerThread()
{
pthread_join(mThread, nullptr);
}
void
WorkerThread::Run()
{
for (;;) {
Task* commands = nullptr;
if (!mQueue->WaitForTask(commands)) {
mQueue->UnregisterThread();
return;
}
TaskStatus status = TaskScheduler::ProcessTask(commands);
if (status == TaskStatus::Error) {
// Don't try to handle errors for now, but that's open to discussions.
// I expect errors to be mostly OOM issues.
MOZ_CRASH();
}
}
}
EventObject::EventObject()
: mIsSet(false)
{}

Просмотреть файл

@ -22,6 +22,7 @@ namespace gfx {
class Task;
class PosixCondVar;
class WorkerThread;
class Mutex {
public:
@ -131,23 +132,6 @@ protected:
friend class WorkerThread;
};
/// Worker thread that continuously dequeues Tasks from a MultiThreadedTaskQueue
/// and process them.
///
/// The public interface of this class must remain identical to its equivalent
/// in TaskScheduler_win32.h
class WorkerThread {
public:
WorkerThread(MultiThreadedTaskQueue* aTaskQueue);
~WorkerThread();
void Run();
protected:
MultiThreadedTaskQueue* mQueue;
pthread_t mThread;
};
/// An object that a thread can synchronously wait on.
/// Usually set by a SetEventTask.
class EventObject : public external::AtomicRefCounted<EventObject>

Просмотреть файл

@ -0,0 +1,144 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#include "TaskScheduler.h"
#include "mozilla/gfx/Logging.h"
using namespace std;
namespace mozilla {
namespace gfx {
DWORD __stdcall ThreadCallback(void* threadData);
class WorkerThreadWin32 : public WorkerThread {
public:
WorkerThreadWin32(MultiThreadedTaskQueue* aTaskQueue)
: WorkerThread(aTaskQueue)
{
mThread = ::CreateThread(nullptr, 0, ThreadCallback, static_cast<WorkerThread*>(this), 0, nullptr);
}
~WorkerThreadWin32()
{
::WaitForSingleObject(mThread, INFINITE);
::CloseHandle(mThread);
}
protected:
HANDLE mThread;
};
DWORD __stdcall ThreadCallback(void* threadData)
{
WorkerThread* thread = static_cast<WorkerThread*>(threadData);
thread->Run();
return 0;
}
WorkerThread*
WorkerThread::Create(MultiThreadedTaskQueue* aTaskQueue)
{
return new WorkerThreadWin32(aTaskQueue);
}
bool
MultiThreadedTaskQueue::PopTask(Task*& aOutTask, AccessType aAccess)
{
for (;;) {
while (aAccess == BLOCKING && mTasks.empty()) {
{
MutexAutoLock lock(&mMutex);
if (mShuttingDown) {
return false;
}
}
HANDLE handles[] = { mAvailableEvent, mShutdownEvent };
::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
}
MutexAutoLock lock(&mMutex);
if (mShuttingDown) {
return false;
}
if (mTasks.empty()) {
if (aAccess == NON_BLOCKING) {
return false;
}
continue;
}
Task* task = mTasks.front();
MOZ_ASSERT(task);
mTasks.pop_front();
if (mTasks.empty()) {
::ResetEvent(mAvailableEvent);
}
aOutTask = task;
return true;
}
}
void
MultiThreadedTaskQueue::SubmitTask(Task* aTask)
{
MOZ_ASSERT(aTask);
MOZ_ASSERT(aTask->GetTaskQueue() == this);
MutexAutoLock lock(&mMutex);
mTasks.push_back(aTask);
::SetEvent(mAvailableEvent);
}
void
MultiThreadedTaskQueue::ShutDown()
{
{
MutexAutoLock lock(&mMutex);
mShuttingDown = true;
}
while (mThreadsCount) {
::SetEvent(mAvailableEvent);
::WaitForSingleObject(mShutdownEvent, INFINITE);
}
}
size_t
MultiThreadedTaskQueue::NumTasks()
{
MutexAutoLock lock(&mMutex);
return mTasks.size();
}
bool
MultiThreadedTaskQueue::IsEmpty()
{
MutexAutoLock lock(&mMutex);
return mTasks.empty();
}
void
MultiThreadedTaskQueue::RegisterThread()
{
mThreadsCount += 1;
}
void
MultiThreadedTaskQueue::UnregisterThread()
{
MutexAutoLock lock(&mMutex);
mThreadsCount -= 1;
if (mThreadsCount == 0) {
::SetEvent(mShutdownEvent);
}
}
} // namespace
} // namespace

Просмотреть файл

@ -10,6 +10,8 @@
#define NOT_IMPLEMENTED MOZ_CRASH("Not implemented")
#include "mozilla/RefPtr.h"
#include <windows.h>
#include <list>
namespace mozilla {
namespace gfx {
@ -19,10 +21,37 @@ class Task;
class Mutex {
public:
Mutex() { NOT_IMPLEMENTED; }
~Mutex() { NOT_IMPLEMENTED; }
void Lock() { NOT_IMPLEMENTED; }
void Unlock() { NOT_IMPLEMENTED; }
Mutex() {
::InitializeCriticalSection(&mMutex);
#ifdef DEBUG
mOwner = 0;
#endif
}
~Mutex() { ::DeleteCriticalSection(&mMutex); }
void Lock() {
::EnterCriticalSection(&mMutex);
#ifdef DEBUG
MOZ_ASSERT(mOwner != GetCurrentThreadId(), "recursive locking");
mOwner = GetCurrentThreadId();
#endif
}
void Unlock() {
#ifdef DEBUG
// GetCurrentThreadId cannot return 0: it is not a valid thread id
MOZ_ASSERT(mOwner == GetCurrentThreadId(), "mismatched lock/unlock");
mOwner = 0;
#endif
::LeaveCriticalSection(&mMutex);
}
protected:
CRITICAL_SECTION mMutex;
#ifdef DEBUG
DWORD mOwner;
#endif
};
// The public interface of this class must remain identical to its equivalent
@ -34,14 +63,43 @@ public:
NON_BLOCKING
};
bool WaitForTask(Task*& aOutCommands) { NOT_IMPLEMENTED; }
bool PopTask(Task*& aOutCommands, AccessType aAccess) { NOT_IMPLEMENTED; }
void SubmitTask(Task* aCommands) { NOT_IMPLEMENTED; }
void ShutDown() { NOT_IMPLEMENTED; }
size_t NumTasks() { NOT_IMPLEMENTED; }
bool IsEmpty() { NOT_IMPLEMENTED; }
void RegisterThread() { NOT_IMPLEMENTED; }
void UnregisterThread() { NOT_IMPLEMENTED; }
MultiThreadedTaskQueue()
: mThreadsCount(0)
, mShuttingDown(false)
{
mAvailableEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
mShutdownEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
}
~MultiThreadedTaskQueue()
{
::CloseHandle(mAvailableEvent);
::CloseHandle(mShutdownEvent);
}
bool WaitForTask(Task*& aOutTask) { return PopTask(aOutTask, BLOCKING); }
bool PopTask(Task*& aOutTask, AccessType aAccess);
void SubmitTask(Task* aTask);
void ShutDown();
size_t NumTasks();
bool IsEmpty();
void RegisterThread();
void UnregisterThread();
protected:
std::list<Task*> mTasks;
Mutex mMutex;
HANDLE mAvailableEvent;
HANDLE mShutdownEvent;
int32_t mThreadsCount;
bool mShuttingDown;
friend class WorkerThread;
};
@ -54,19 +112,18 @@ class EventObject : public external::AtomicRefCounted<EventObject>
public:
MOZ_DECLARE_REFCOUNTED_TYPENAME(EventObject)
EventObject() { NOT_IMPLEMENTED; }
~EventObject() { NOT_IMPLEMENTED; }
void Wait() { NOT_IMPLEMENTED; }
bool Peak() { NOT_IMPLEMENTED; }
void Set() { NOT_IMPLEMENTED; }
};
EventObject() { mEvent = ::CreateEvent(nullptr, TRUE, FALSE, nullptr); }
// The public interface of this class must remain identical to its equivalent
// in TaskScheduler_posix.h
class WorkerThread {
public:
WorkerThread(MultiThreadedTaskQueue* aTaskQueue) { NOT_IMPLEMENTED; }
void Run();
~EventObject() { ::CloseHandle(mEvent); }
void Wait() { ::WaitForSingleObject(mEvent, INFINITE); }
bool Peak() { return ::WaitForSingleObject(mEvent, 0) == WAIT_OBJECT_0; }
void Set() { ::SetEvent(mEvent); }
protected:
// TODO: it's expensive to create events so we should try to reuse them
HANDLE mEvent;
};
} // namespace

Просмотреть файл

@ -71,6 +71,7 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'SourceSurfaceD2D.cpp',
'SourceSurfaceD2D1.cpp',
'SourceSurfaceD2DTarget.cpp',
'TaskScheduler_win32.cpp',
]
DEFINES['WIN32'] = True

Просмотреть файл

@ -3,15 +3,16 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
#ifndef WIN32
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "mozilla/gfx/TaskScheduler.h"
#ifndef WIN32
#include <pthread.h>
#include <sched.h>
#endif
#include <stdlib.h>
#include <time.h>
@ -24,9 +25,11 @@ using namespace mozilla;
// things more apparent (if any).
void MaybeYieldThread()
{
#ifndef WIN32
if (rand() % 5 == 0) {
sched_yield();
}
#endif
}
/// Used by the TestCommand to check that tasks are processed in the right order.
@ -242,5 +245,3 @@ TEST(Moz2D, TaskScheduler_Chain) {
}
}
}
#endif