зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1799222 - Prevent accidental dispatches to threadpool and timer threads, r=xpcom-reviewers,necko-reviewers,geckoview-reviewers,media-playback-reviewers,jesup,m_kato,padenot,kmag
Differential Revision: https://phabricator.services.mozilla.com/D161349
This commit is contained in:
Родитель
391c21b2f8
Коммит
40a3a64aee
|
@ -17,7 +17,7 @@ namespace mozilla {
|
|||
nsCOMPtr<nsIThread> mThread;
|
||||
nsresult rv =
|
||||
NS_NewNamedThread("DDMediaLogs", getter_AddRefs(mThread), nullptr,
|
||||
nsIThreadManager::kThreadPoolStackSize);
|
||||
{.stackSize = nsIThreadManager::kThreadPoolStackSize});
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return {rv, nullptr};
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ class WorkerThread::Observer final : public nsIThreadObserver {
|
|||
WorkerThread::WorkerThread(ConstructorKey)
|
||||
: nsThread(
|
||||
MakeNotNull<ThreadEventQueue*>(MakeUnique<mozilla::EventQueue>()),
|
||||
nsThread::NOT_MAIN_THREAD, kWorkerStackSize),
|
||||
nsThread::NOT_MAIN_THREAD, {.stackSize = kWorkerStackSize}),
|
||||
mLock("WorkerThread::mLock"),
|
||||
mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar"),
|
||||
mWorkerPrivate(nullptr),
|
||||
|
|
|
@ -257,7 +257,7 @@ class WorkletThread::TerminateRunnable final : public Runnable {
|
|||
WorkletThread::WorkletThread(WorkletImpl* aWorkletImpl)
|
||||
: nsThread(
|
||||
MakeNotNull<ThreadEventQueue*>(MakeUnique<mozilla::EventQueue>()),
|
||||
nsThread::NOT_MAIN_THREAD, kWorkletStackSize),
|
||||
nsThread::NOT_MAIN_THREAD, {.stackSize = kWorkletStackSize}),
|
||||
mWorkletImpl(aWorkletImpl),
|
||||
mExitLoop(false),
|
||||
mIsTerminating(false) {
|
||||
|
|
|
@ -69,7 +69,7 @@ void CanvasRenderThread::Start() {
|
|||
nsthread->SetUseHangMonitor(true);
|
||||
nsthread->SetPriority(nsISupportsPriority::PRIORITY_HIGH);
|
||||
}),
|
||||
stackSize);
|
||||
{.stackSize = stackSize});
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
|
|
|
@ -83,7 +83,7 @@ CompositorThreadHolder::CreateCompositorThread() {
|
|||
nsCOMPtr<nsIThread> thread = NS_GetCurrentThread();
|
||||
static_cast<nsThread*>(thread.get())->SetUseHangMonitor(true);
|
||||
}),
|
||||
stackSize);
|
||||
{.stackSize = stackSize});
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
|
|
|
@ -788,7 +788,7 @@ nsSocketTransportService::Init() {
|
|||
if (!XRE_IsContentProcess() ||
|
||||
StaticPrefs::network_allow_raw_sockets_in_content_processes_AtStartup()) {
|
||||
nsresult rv = NS_NewNamedThread("Socket Thread", getter_AddRefs(thread),
|
||||
this, GetThreadStackSize());
|
||||
this, {.stackSize = GetThreadStackSize()});
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// In the child process, we just want a regular nsThread with no socket
|
||||
|
|
|
@ -93,7 +93,7 @@ NS_IMETHODIMP nsWifiMonitor::StartWatching(nsIWifiListener* aListener) {
|
|||
|
||||
if (!mThread) {
|
||||
rv = NS_NewNamedThread("Wifi Monitor", getter_AddRefs(mThread), this,
|
||||
GetMonitorThreadStackSize());
|
||||
{.stackSize = GetMonitorThreadStackSize()});
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ class AndroidUiThread : public nsThread {
|
|||
AndroidUiThread()
|
||||
: nsThread(
|
||||
MakeNotNull<ThreadEventQueue*>(MakeUnique<mozilla::EventQueue>()),
|
||||
nsThread::NOT_MAIN_THREAD, 0) {}
|
||||
nsThread::NOT_MAIN_THREAD, {.stackSize = 0}) {}
|
||||
|
||||
nsresult Dispatch(already_AddRefed<nsIRunnable> aEvent,
|
||||
uint32_t aFlags) override;
|
||||
|
|
|
@ -209,7 +209,7 @@ already_AddRefed<nsISerialEventTarget> ThreadEventQueue::PushEventQueue() {
|
|||
auto queue = MakeUnique<EventQueue>();
|
||||
RefPtr<NestedSink> sink = new NestedSink(queue.get(), this);
|
||||
RefPtr<ThreadEventTarget> eventTarget =
|
||||
new ThreadEventTarget(sink, NS_IsMainThread());
|
||||
new ThreadEventTarget(sink, NS_IsMainThread(), false);
|
||||
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
|
|
|
@ -31,13 +31,12 @@ static mozilla::Atomic<bool, mozilla::SequentiallyConsistent>
|
|||
#endif
|
||||
|
||||
ThreadEventTarget::ThreadEventTarget(ThreadTargetSink* aSink,
|
||||
bool aIsMainThread)
|
||||
: mSink(aSink)
|
||||
bool aIsMainThread, bool aBlockDispatch)
|
||||
: mSink(aSink),
|
||||
#ifdef DEBUG
|
||||
,
|
||||
mIsMainThread(aIsMainThread)
|
||||
mIsMainThread(aIsMainThread),
|
||||
#endif
|
||||
{
|
||||
mBlockDispatch(aBlockDispatch) {
|
||||
mThread = PR_GetCurrentThread();
|
||||
}
|
||||
|
||||
|
@ -77,6 +76,14 @@ ThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
|
|||
PR_GetCurrentThread() == mThread,
|
||||
"Dispatch to non-main thread after xpcom-shutdown-threads");
|
||||
|
||||
if (mBlockDispatch && !(aFlags & NS_DISPATCH_IGNORE_BLOCK_DISPATCH)) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(
|
||||
false,
|
||||
"Attempt to dispatch to thread which does not usually process "
|
||||
"dispatched runnables until shutdown");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
LogRunnable::LogDispatch(event.get());
|
||||
|
||||
if (aFlags & DISPATCH_SYNC) {
|
||||
|
@ -109,7 +116,8 @@ ThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL || aFlags == NS_DISPATCH_AT_END,
|
||||
NS_ASSERTION((aFlags & (NS_DISPATCH_AT_END |
|
||||
NS_DISPATCH_IGNORE_BLOCK_DISPATCH)) == aFlags,
|
||||
"unexpected dispatch flags");
|
||||
if (!mSink->PutEvent(event.take(), EventQueuePriority::Normal)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
|
|
@ -19,7 +19,8 @@ class DelayedRunnable;
|
|||
// be used with any ThreadTargetSink implementation.
|
||||
class ThreadEventTarget final : public nsISerialEventTarget {
|
||||
public:
|
||||
ThreadEventTarget(ThreadTargetSink* aSink, bool aIsMainThread);
|
||||
ThreadEventTarget(ThreadTargetSink* aSink, bool aIsMainThread,
|
||||
bool aBlockDispatch);
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIEVENTTARGET_FULL
|
||||
|
@ -52,8 +53,9 @@ class ThreadEventTarget final : public nsISerialEventTarget {
|
|||
|
||||
RefPtr<ThreadTargetSink> mSink;
|
||||
#ifdef DEBUG
|
||||
bool mIsMainThread;
|
||||
const bool mIsMainThread;
|
||||
#endif
|
||||
const bool mBlockDispatch;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
using namespace mozilla;
|
||||
|
||||
// Uncomment the following line to enable runtime stats during development.
|
||||
//#define TIMERS_RUNTIME_STATS
|
||||
// #define TIMERS_RUNTIME_STATS
|
||||
|
||||
#ifdef TIMERS_RUNTIME_STATS
|
||||
// This class gathers durations and displays some basic stats when destroyed.
|
||||
|
@ -376,7 +376,8 @@ nsresult TimerThread::Init() {
|
|||
nsTimerEvent::Init();
|
||||
|
||||
// We hold on to mThread to keep the thread alive.
|
||||
nsresult rv = NS_NewNamedThread("Timer", getter_AddRefs(mThread), this);
|
||||
nsresult rv = NS_NewNamedThread("Timer", getter_AddRefs(mThread), this,
|
||||
{.blockDispatch = true});
|
||||
if (NS_FAILED(rv)) {
|
||||
mThread = nullptr;
|
||||
} else {
|
||||
|
|
|
@ -65,6 +65,15 @@ interface nsIEventTarget : nsISupports
|
|||
*/
|
||||
const unsigned long DISPATCH_EVENT_MAY_BLOCK = 4;
|
||||
|
||||
/**
|
||||
* This flag specifies that the dispatched event should be delivered to the
|
||||
* target thread even if the thread has been configured to block dispatching
|
||||
* of runnables. This is generally done for threads which have their own
|
||||
* internal event loop, such as thread pools or the timer thread, and will not
|
||||
* service runnables dispatched to them until shutdown.
|
||||
*/
|
||||
const unsigned long DISPATCH_IGNORE_BLOCK_DISPATCH = 8;
|
||||
|
||||
/**
|
||||
* IsOnCurrentThread() should return true if events dispatched to this target
|
||||
* can possibly run on the current thread, and false otherwise. In the case
|
||||
|
@ -216,6 +225,7 @@ public:
|
|||
#define NS_DISPATCH_SYNC nsIEventTarget::DISPATCH_SYNC
|
||||
#define NS_DISPATCH_AT_END nsIEventTarget::DISPATCH_AT_END
|
||||
#define NS_DISPATCH_EVENT_MAY_BLOCK nsIEventTarget::DISPATCH_EVENT_MAY_BLOCK
|
||||
#define NS_DISPATCH_IGNORE_BLOCK_DISPATCH nsIEventTarget::DISPATCH_IGNORE_BLOCK_DISPATCH
|
||||
|
||||
// Convenient NS_DECL variant that includes some C++-only methods.
|
||||
#define NS_DECL_NSIEVENTTARGET_FULL \
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
[ptr] native PRThread(PRThread);
|
||||
native ThreadCreationOptions(nsIThreadManager::ThreadCreationOptions);
|
||||
|
||||
interface nsIEventTarget;
|
||||
interface nsIRunnable;
|
||||
|
@ -53,6 +54,21 @@ interface nsIThreadManager : nsISupports
|
|||
#endif
|
||||
|
||||
static const uint32_t kThreadPoolStackSize = DEFAULT_STACK_SIZE;
|
||||
|
||||
struct ThreadCreationOptions {
|
||||
// The size in bytes to reserve for the thread's stack. A value of `0` means
|
||||
// to use the platform default.
|
||||
uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE;
|
||||
|
||||
// If set to `true`, any attempts to dispatch runnables to this thread
|
||||
// without `DISPATCH_IGNORE_BLOCK_DISPATCH` will fail.
|
||||
//
|
||||
// This is intended to be used for threads which are expected to generally
|
||||
// only service a single runnable (other than thread lifecycle runnables),
|
||||
// and perform their own event dispatching internaly, such as thread pool
|
||||
// threads or the timer thread.
|
||||
bool blockDispatch = false;
|
||||
};
|
||||
%}
|
||||
|
||||
/**
|
||||
|
@ -60,14 +76,13 @@ interface nsIThreadManager : nsISupports
|
|||
*
|
||||
* @param name
|
||||
* The name of the thread. If it is empty the thread will not be named.
|
||||
* @param stackSize
|
||||
* Number of bytes to reserve for the thread's stack. 0 means use platform
|
||||
* default.
|
||||
* @param options
|
||||
* Configuration options for the newly created thread.
|
||||
*
|
||||
* @returns
|
||||
* The newly created nsIThread object.
|
||||
*/
|
||||
[noscript] nsIThread newNamedThread(in ACString name, [optional] in unsigned long stackSize);
|
||||
[noscript] nsIThread newNamedThread(in ACString name, in ThreadCreationOptions options);
|
||||
|
||||
/**
|
||||
* Get the main thread.
|
||||
|
|
|
@ -544,15 +544,16 @@ int sCanaryOutputFD = -1;
|
|||
#endif
|
||||
|
||||
nsThread::nsThread(NotNull<SynchronizedEventQueue*> aQueue,
|
||||
MainThreadFlag aMainThread, uint32_t aStackSize)
|
||||
MainThreadFlag aMainThread,
|
||||
nsIThreadManager::ThreadCreationOptions aOptions)
|
||||
: mEvents(aQueue.get()),
|
||||
mEventTarget(
|
||||
new ThreadEventTarget(mEvents.get(), aMainThread == MAIN_THREAD)),
|
||||
mEventTarget(new ThreadEventTarget(
|
||||
mEvents.get(), aMainThread == MAIN_THREAD, aOptions.blockDispatch)),
|
||||
mOutstandingShutdownContexts(0),
|
||||
mShutdownContext(nullptr),
|
||||
mScriptObserver(nullptr),
|
||||
mThreadName("<uninitialized>"),
|
||||
mStackSize(aStackSize),
|
||||
mStackSize(aOptions.stackSize),
|
||||
mNestedEventLoopDepth(0),
|
||||
mShutdownRequired(false),
|
||||
mPriority(PRIORITY_NORMAL),
|
||||
|
|
|
@ -171,7 +171,8 @@ class nsThread : public nsIThreadInternal,
|
|||
enum MainThreadFlag { MAIN_THREAD, NOT_MAIN_THREAD };
|
||||
|
||||
nsThread(NotNull<mozilla::SynchronizedEventQueue*> aQueue,
|
||||
MainThreadFlag aMainThread, uint32_t aStackSize);
|
||||
MainThreadFlag aMainThread,
|
||||
nsIThreadManager::ThreadCreationOptions aOptions);
|
||||
|
||||
private:
|
||||
nsThread();
|
||||
|
|
|
@ -298,8 +298,8 @@ nsresult nsThreadManager::Init() {
|
|||
RefPtr<ThreadEventQueue> synchronizedQueue =
|
||||
new ThreadEventQueue(std::move(queue), true);
|
||||
|
||||
mMainThread =
|
||||
new nsThread(WrapNotNull(synchronizedQueue), nsThread::MAIN_THREAD, 0);
|
||||
mMainThread = new nsThread(WrapNotNull(synchronizedQueue),
|
||||
nsThread::MAIN_THREAD, {.stackSize = 0});
|
||||
|
||||
nsresult rv = mMainThread->InitCurrentThread();
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -452,7 +452,8 @@ nsThread* nsThreadManager::CreateCurrentThread(
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<nsThread> thread = new nsThread(WrapNotNull(aQueue), aMainThread, 0);
|
||||
RefPtr<nsThread> thread =
|
||||
new nsThread(WrapNotNull(aQueue), aMainThread, {.stackSize = 0});
|
||||
if (!thread || NS_FAILED(thread->InitCurrentThread())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -514,8 +515,9 @@ bool nsThreadManager::IsNSThread() const {
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThreadManager::NewNamedThread(const nsACString& aName, uint32_t aStackSize,
|
||||
nsIThread** aResult) {
|
||||
nsThreadManager::NewNamedThread(
|
||||
const nsACString& aName, nsIThreadManager::ThreadCreationOptions aOptions,
|
||||
nsIThread** aResult) {
|
||||
// Note: can be called from arbitrary threads
|
||||
|
||||
// No new threads during Shutdown
|
||||
|
@ -528,7 +530,7 @@ nsThreadManager::NewNamedThread(const nsACString& aName, uint32_t aStackSize,
|
|||
RefPtr<ThreadEventQueue> queue =
|
||||
new ThreadEventQueue(MakeUnique<EventQueue>());
|
||||
RefPtr<nsThread> thr =
|
||||
new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, aStackSize);
|
||||
new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, aOptions);
|
||||
nsresult rv =
|
||||
thr->Init(aName); // Note: blocks until the new thread has been set up
|
||||
if (NS_FAILED(rv)) {
|
||||
|
|
|
@ -119,8 +119,9 @@ nsresult nsThreadPool::PutEvent(already_AddRefed<nsIRunnable> aEvent,
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = NS_NewNamedThread(mThreadNaming.GetNextThreadName(name),
|
||||
getter_AddRefs(thread), nullptr, stackSize);
|
||||
nsresult rv = NS_NewNamedThread(
|
||||
mThreadNaming.GetNextThreadName(name), getter_AddRefs(thread), nullptr,
|
||||
{.stackSize = stackSize, .blockDispatch = true});
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
@ -146,7 +147,7 @@ nsresult nsThreadPool::PutEvent(already_AddRefed<nsIRunnable> aEvent,
|
|||
// asynchronously without worrying about anything.
|
||||
ShutdownThread(thread);
|
||||
} else {
|
||||
thread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
thread->Dispatch(this, NS_DISPATCH_IGNORE_BLOCK_DISPATCH);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -43,7 +43,7 @@ class nsThreadSyncDispatch : public mozilla::Runnable {
|
|||
mIsPending = false;
|
||||
|
||||
// unblock the origin thread
|
||||
mOrigin->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
mOrigin->Dispatch(this, NS_DISPATCH_IGNORE_BLOCK_DISPATCH);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsTimerImpl.h"
|
||||
#include "prsystem.h"
|
||||
|
@ -157,24 +158,25 @@ PrioritizableCancelableRunnable::GetPriority(uint32_t* aPriority) {
|
|||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult NS_NewNamedThread(const nsACString& aName, nsIThread** aResult,
|
||||
nsIRunnable* aInitialEvent, uint32_t aStackSize) {
|
||||
nsIRunnable* aInitialEvent,
|
||||
nsIThreadManager::ThreadCreationOptions aOptions) {
|
||||
nsCOMPtr<nsIRunnable> event = aInitialEvent;
|
||||
return NS_NewNamedThread(aName, aResult, event.forget(), aStackSize);
|
||||
return NS_NewNamedThread(aName, aResult, event.forget(), aOptions);
|
||||
}
|
||||
|
||||
nsresult NS_NewNamedThread(const nsACString& aName, nsIThread** aResult,
|
||||
already_AddRefed<nsIRunnable> aInitialEvent,
|
||||
uint32_t aStackSize) {
|
||||
nsIThreadManager::ThreadCreationOptions aOptions) {
|
||||
nsCOMPtr<nsIRunnable> event = std::move(aInitialEvent);
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = nsThreadManager::get().nsThreadManager::NewNamedThread(
|
||||
aName, aStackSize, getter_AddRefs(thread));
|
||||
aName, aOptions, getter_AddRefs(thread));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
rv = thread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
|
||||
rv = thread->Dispatch(event.forget(), NS_DISPATCH_IGNORE_BLOCK_DISPATCH);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -58,32 +58,32 @@ class nsIThread;
|
|||
extern nsresult NS_NewNamedThread(
|
||||
const nsACString& aName, nsIThread** aResult,
|
||||
nsIRunnable* aInitialEvent = nullptr,
|
||||
uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
|
||||
nsIThreadManager::ThreadCreationOptions aOptions = {});
|
||||
|
||||
extern nsresult NS_NewNamedThread(
|
||||
const nsACString& aName, nsIThread** aResult,
|
||||
already_AddRefed<nsIRunnable> aInitialEvent,
|
||||
uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
|
||||
nsIThreadManager::ThreadCreationOptions aOptions = {});
|
||||
|
||||
template <size_t LEN>
|
||||
inline nsresult NS_NewNamedThread(
|
||||
const char (&aName)[LEN], nsIThread** aResult,
|
||||
already_AddRefed<nsIRunnable> aInitialEvent,
|
||||
uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE) {
|
||||
nsIThreadManager::ThreadCreationOptions aOptions = {}) {
|
||||
static_assert(LEN <= 16, "Thread name must be no more than 16 characters");
|
||||
return NS_NewNamedThread(nsDependentCString(aName, LEN - 1), aResult,
|
||||
std::move(aInitialEvent), aStackSize);
|
||||
std::move(aInitialEvent), aOptions);
|
||||
}
|
||||
|
||||
template <size_t LEN>
|
||||
inline nsresult NS_NewNamedThread(
|
||||
const char (&aName)[LEN], nsIThread** aResult,
|
||||
nsIRunnable* aInitialEvent = nullptr,
|
||||
uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE) {
|
||||
nsIThreadManager::ThreadCreationOptions aOptions = {}) {
|
||||
nsCOMPtr<nsIRunnable> event = aInitialEvent;
|
||||
static_assert(LEN <= 16, "Thread name must be no more than 16 characters");
|
||||
return NS_NewNamedThread(nsDependentCString(aName, LEN - 1), aResult,
|
||||
event.forget(), aStackSize);
|
||||
event.forget(), aOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче