gecko-dev/xpcom/threads/CooperativeThreadPool.h

151 строка
5.2 KiB
C++

/* -*- 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 mozilla_CooperativeThreadPool_h
#define mozilla_CooperativeThreadPool_h
#include "mozilla/Array.h"
#include "mozilla/CondVar.h"
#include "mozilla/Mutex.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "prthread.h"
// Windows silliness. winbase.h defines an empty no-argument Yield macro.
#undef Yield
class nsIEventTarget;
namespace mozilla {
// A CooperativeThreadPool is a pool of threads that process jobs, with at most
// one thread running at a given time. While processing a job, a thread can
// yield to another thread in the pool. Threads can be blocked on abstract
// Resources. When a thread yields, we iterate over all threads and look for
// one whose Resource has become available. It's possible that no thread is
// available to run, in which case all threads in the pool sleep. An outside
// thread can get things started again by calling RecheckBlockers (presumably
// after it has mutated some state that it expects would cause a Resource to
// become available).
class CooperativeThreadPool {
public:
// Every pool must have a controller object, on which callbacks are invoked.
class Controller {
public:
// Called when a new thread in the pool is started. aIndex is the index of
// the thread within the pool. aName is the thread name (e.g., Main#4), and
// aStackTop is the guess for the address of the top of the stack. The
// thread will begin processing events immediately after OnStartThread is
// called.
virtual void OnStartThread(size_t aIndex, const nsACString& aName,
void* aStackTop) = 0;
// Called when a thread in the pool is about to be shut down.
virtual void OnStopThread(size_t aIndex) = 0;
// Threads in the pool are either suspended or running. At most one thread
// will be running at any time. All other threads will be suspended.
// Called when a thread is resumed (un-suspended). Note that OnResumeThread
// will not be called if the thread is starting up. OnStartThread will be
// called instead.
virtual void OnResumeThread(size_t aIndex) = 0;
// Called when a thread in the pool is about to be suspended. Note that
// OnSuspendThread will not be called if the thread is shutting
// down. OnStopThread is called instead.
virtual void OnSuspendThread(size_t aIndex) = 0;
};
CooperativeThreadPool(size_t aNumThreads, Mutex& aMutex,
Controller& aController);
~CooperativeThreadPool();
void Shutdown();
// An abstract class representing something that can be blocked on. Examples
// are an event queue (where IsAvailable would check if the queue is
// non-empty) or an object that can be owned by only one thread at a time
// (where IsAvailable would check if the object is unowned).
class Resource {
public:
virtual bool IsAvailable(const MutexAutoLock& aProofOfLock) = 0;
};
// Typically called by a thread outside the pool, this will check if any
// thread is blocked on a resource that has become available. In this case,
// the thread will resume. Nothing is done if some thread in the pool was
// already running.
void RecheckBlockers(const MutexAutoLock& aProofOfLock);
// Must be called from within the thread pool. Causes the current thread to be
// blocked on aBlocker, which can be null if the thread is ready to run
// unconditionally.
static void Yield(Resource* aBlocker, const MutexAutoLock& aProofOfLock);
static bool IsCooperativeThread();
enum class AllThreadsBlocked { Blocked };
using SelectedThread = Variant<size_t, AllThreadsBlocked>;
SelectedThread CurrentThreadIndex(const MutexAutoLock& aProofOfLock) const;
static const size_t kMaxThreads = 16;
private:
class CooperativeThread {
friend class CooperativeThreadPool;
public:
CooperativeThread(CooperativeThreadPool* aPool, size_t aIndex);
void BeginShutdown();
void EndShutdown();
void Started();
bool IsBlocked(const MutexAutoLock& aProofOfLock);
void SetBlocker(Resource* aResource) { mBlocker = aResource; }
void Yield(const MutexAutoLock& aProofOfLock);
private:
static void ThreadFunc(void* aArg);
void ThreadMethod();
CooperativeThreadPool* mPool;
CondVar mCondVar;
Resource* mBlocker;
PRThread* mThread;
nsCOMPtr<nsIEventTarget> mEventTarget;
const size_t mIndex;
bool mRunning;
};
class StartRunnable;
Mutex& mMutex;
CondVar mShutdownCondition;
nsThreadPoolNaming mThreadNaming;
bool mRunning;
const size_t mNumThreads;
size_t mRunningThreads;
Controller& mController;
Array<UniquePtr<CooperativeThread>, kMaxThreads> mThreads;
SelectedThread mSelectedThread;
static MOZ_THREAD_LOCAL(CooperativeThread*) sTlsCurrentThread;
};
} // namespace mozilla
#endif // mozilla_CooperativeThreadPool_h