зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1083101 - Win32 implementation of the TaskScheduler. r=jrmuizel
This commit is contained in:
Родитель
8f49f122e9
Коммит
63fd1ccf4d
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче