mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
addendum of rev c554d461ab
This commit is contained in:
+116
-30
@@ -21,6 +21,7 @@
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "pratom.h"
|
||||
#include "mozilla/CycleCollectedJSRuntime.h"
|
||||
#include "mozilla/Logging.h"
|
||||
@@ -238,28 +239,43 @@ private:
|
||||
|
||||
struct nsThreadShutdownContext
|
||||
{
|
||||
// NB: This will be the last reference.
|
||||
nsRefPtr<nsThread> terminatingThread;
|
||||
nsThread* joiningThread;
|
||||
bool shutdownAck;
|
||||
bool awaitingShutdownAck;
|
||||
};
|
||||
|
||||
// This event is responsible for notifying nsThread::Shutdown that it is time
|
||||
// to call PR_JoinThread.
|
||||
class nsThreadShutdownAckEvent : public nsRunnable
|
||||
// to call PR_JoinThread. It implements nsICancelableRunnable so that it can
|
||||
// run on a DOM Worker thread (where all events must implement
|
||||
// nsICancelableRunnable.)
|
||||
class nsThreadShutdownAckEvent : public nsRunnable,
|
||||
public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit nsThreadShutdownAckEvent(nsThreadShutdownContext* aCtx)
|
||||
: mShutdownContext(aCtx)
|
||||
{
|
||||
}
|
||||
NS_IMETHOD Run()
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
mShutdownContext->shutdownAck = true;
|
||||
mShutdownContext->terminatingThread->ShutdownComplete(mShutdownContext);
|
||||
return NS_OK;
|
||||
}
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
return Run();
|
||||
}
|
||||
private:
|
||||
virtual ~nsThreadShutdownAckEvent() { }
|
||||
|
||||
nsThreadShutdownContext* mShutdownContext;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(nsThreadShutdownAckEvent, nsRunnable,
|
||||
nsICancelableRunnable)
|
||||
|
||||
// This event is responsible for setting mShutdownContext
|
||||
class nsThreadShutdownEvent : public nsRunnable
|
||||
{
|
||||
@@ -342,9 +358,12 @@ nsThread::ThreadFunc(void* aArg)
|
||||
|
||||
// Wait for and process startup event
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
if (!self->GetEvent(true, getter_AddRefs(event))) {
|
||||
NS_WARNING("failed waiting for thread startup event");
|
||||
return;
|
||||
{
|
||||
MutexAutoLock lock(self->mLock);
|
||||
if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
|
||||
NS_WARNING("failed waiting for thread startup event");
|
||||
return;
|
||||
}
|
||||
}
|
||||
event->Run(); // unblocks nsThread::Init
|
||||
event = nullptr;
|
||||
@@ -369,11 +388,18 @@ nsThread::ThreadFunc(void* aArg)
|
||||
// mEventsAreDoomed atomically with the removal of the last event. The key
|
||||
// invariant here is that we will never permit PutEvent to succeed if the
|
||||
// event would be left in the queue after our final call to
|
||||
// NS_ProcessPendingEvents.
|
||||
// NS_ProcessPendingEvents. We also have to keep processing events as long
|
||||
// as we have outstanding mRequestedShutdownContexts.
|
||||
while (true) {
|
||||
// Check and see if we're waiting on any threads.
|
||||
while (self->mRequestedShutdownContexts.Length()) {
|
||||
// We can't stop accepting events just yet. Block and check again.
|
||||
NS_ProcessNextEvent(self, true);
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(self->mLock);
|
||||
if (!self->mEvents->HasPendingEvent()) {
|
||||
if (!self->mEvents->HasPendingEvent(lock)) {
|
||||
// No events in the queue, so we will stop now. Don't let any more
|
||||
// events be added, since they won't be processed. It is critical
|
||||
// that no PutEvent can occur between testing that the event queue is
|
||||
@@ -394,7 +420,8 @@ nsThread::ThreadFunc(void* aArg)
|
||||
nsThreadManager::get()->UnregisterCurrentThread(self);
|
||||
|
||||
// Dispatch shutdown ACK
|
||||
event = new nsThreadShutdownAckEvent(self->mShutdownContext);
|
||||
MOZ_ASSERT(self->mShutdownContext->terminatingThread == self);
|
||||
event = do_QueryObject(new nsThreadShutdownAckEvent(self->mShutdownContext));
|
||||
self->mShutdownContext->joiningThread->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
|
||||
// Release any observer of the thread here.
|
||||
@@ -417,6 +444,7 @@ nsThread::nsThread(MainThreadFlag aMainThread, uint32_t aStackSize)
|
||||
: mLock("nsThread.mLock")
|
||||
, mScriptObserver(nullptr)
|
||||
, mEvents(&mEventsRoot)
|
||||
, mEventsRoot(mLock)
|
||||
, mPriority(PRIORITY_NORMAL)
|
||||
, mThread(nullptr)
|
||||
, mNestedEventLoopDepth(0)
|
||||
@@ -456,7 +484,7 @@ nsThread::Init()
|
||||
// that mThread is set properly.
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mEventsRoot.PutEvent(startup); // retain a reference
|
||||
mEventsRoot.PutEvent(startup, lock); // retain a reference
|
||||
}
|
||||
|
||||
// Wait for thread to call ThreadManager::SetupCurrentThread, which completes
|
||||
@@ -502,7 +530,7 @@ nsThread::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, nsNestedEventTarget*
|
||||
nsIRunnable* temp2 = temp.forget().take(); // can't use unused << aEvent here due to Windows (boo)
|
||||
return temp2 ? NS_ERROR_UNEXPECTED : NS_ERROR_UNEXPECTED; // to make compiler not bletch on us
|
||||
}
|
||||
queue->PutEvent(Move(aEvent));
|
||||
queue->PutEvent(Move(aEvent), lock);
|
||||
|
||||
// Make sure to grab the observer before dropping the lock, otherwise the
|
||||
// event that we just placed into the queue could run and eventually delete
|
||||
@@ -603,9 +631,9 @@ nsThread::GetPRThread(PRThread** aResult)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::Shutdown()
|
||||
nsThread::AsyncShutdown()
|
||||
{
|
||||
LOG(("THRD(%p) shutdown\n", this));
|
||||
LOG(("THRD(%p) async shutdown\n", this));
|
||||
|
||||
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
|
||||
// shutting down a thread in a thread pool. That happens b/c the thread
|
||||
@@ -614,37 +642,61 @@ nsThread::Shutdown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return !!ShutdownInternal(/* aSync = */ false) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsThreadShutdownContext*
|
||||
nsThread::ShutdownInternal(bool aSync)
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
|
||||
if (NS_WARN_IF(mThread == PR_GetCurrentThread())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Prevent multiple calls to this method
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
if (!mShutdownRequired) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return nullptr;
|
||||
}
|
||||
mShutdownRequired = false;
|
||||
}
|
||||
|
||||
nsThreadShutdownContext context;
|
||||
context.joiningThread = nsThreadManager::get()->GetCurrentThread();
|
||||
context.shutdownAck = false;
|
||||
nsThread* currentThread = nsThreadManager::get()->GetCurrentThread();
|
||||
MOZ_ASSERT(currentThread);
|
||||
|
||||
nsAutoPtr<nsThreadShutdownContext>& context =
|
||||
*currentThread->mRequestedShutdownContexts.AppendElement();
|
||||
context = new nsThreadShutdownContext();
|
||||
|
||||
context->terminatingThread = this;
|
||||
context->joiningThread = currentThread;
|
||||
context->awaitingShutdownAck = aSync;
|
||||
|
||||
// Set mShutdownContext and wake up the thread in case it is waiting for
|
||||
// events to process.
|
||||
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, &context);
|
||||
nsCOMPtr<nsIRunnable> event = new nsThreadShutdownEvent(this, context);
|
||||
// XXXroc What if posting the event fails due to OOM?
|
||||
PutEvent(event.forget(), nullptr);
|
||||
|
||||
// We could still end up with other events being added after the shutdown
|
||||
// task, but that's okay because we process pending events in ThreadFunc
|
||||
// after setting mShutdownContext just before exiting.
|
||||
return context;
|
||||
}
|
||||
|
||||
// Process events on the current thread until we receive a shutdown ACK.
|
||||
// Allows waiting; ensure no locks are held that would deadlock us!
|
||||
while (!context.shutdownAck) {
|
||||
NS_ProcessNextEvent(context.joiningThread, true);
|
||||
void
|
||||
nsThread::ShutdownComplete(nsThreadShutdownContext* aContext)
|
||||
{
|
||||
MOZ_ASSERT(mThread);
|
||||
MOZ_ASSERT(aContext->terminatingThread == this);
|
||||
|
||||
if (aContext->awaitingShutdownAck) {
|
||||
// We're in a synchronous shutdown, so tell whatever is up the stack that
|
||||
// we're done and unwind the stack so it can call us again.
|
||||
aContext->awaitingShutdownAck = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now, it should be safe to join without fear of dead-locking.
|
||||
@@ -664,6 +716,34 @@ nsThread::Shutdown()
|
||||
}
|
||||
#endif
|
||||
|
||||
// Delete aContext.
|
||||
MOZ_ALWAYS_TRUE(
|
||||
aContext->joiningThread->mRequestedShutdownContexts.RemoveElement(aContext));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsThread::Shutdown()
|
||||
{
|
||||
LOG(("THRD(%p) sync shutdown\n", this));
|
||||
|
||||
// XXX If we make this warn, then we hit that warning at xpcom shutdown while
|
||||
// shutting down a thread in a thread pool. That happens b/c the thread
|
||||
// in the thread pool is already shutdown by the thread manager.
|
||||
if (!mThread) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsThreadShutdownContext* context = ShutdownInternal(/* aSync = */ true);
|
||||
NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Process events on the current thread until we receive a shutdown ACK.
|
||||
// Allows waiting; ensure no locks are held that would deadlock us!
|
||||
while (context->awaitingShutdownAck) {
|
||||
NS_ProcessNextEvent(context->joiningThread, true);
|
||||
}
|
||||
|
||||
ShutdownComplete(context);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -674,7 +754,10 @@ nsThread::HasPendingEvents(bool* aResult)
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
*aResult = mEvents->GetEvent(false, nullptr);
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
*aResult = mEvents->HasPendingEvent(lock);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -833,7 +916,10 @@ nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
|
||||
|
||||
// If we are shutting down, then do not wait for new events.
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
mEvents->GetEvent(reallyWait, getter_AddRefs(event));
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
mEvents->GetEvent(reallyWait, getter_AddRefs(event), lock);
|
||||
}
|
||||
|
||||
*aResult = (event.get() != nullptr);
|
||||
|
||||
@@ -989,7 +1075,7 @@ nsThread::PushEventQueue(nsIEventTarget** aResult)
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
nsChainedEventQueue* queue = new nsChainedEventQueue();
|
||||
nsChainedEventQueue* queue = new nsChainedEventQueue(mLock);
|
||||
queue->mEventTarget = new nsNestedEventTarget(this, queue);
|
||||
|
||||
{
|
||||
@@ -1031,8 +1117,8 @@ nsThread::PopEventQueue(nsIEventTarget* aInnermostTarget)
|
||||
mEvents = mEvents->mNext;
|
||||
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
while (queue->GetEvent(false, getter_AddRefs(event))) {
|
||||
mEvents->PutEvent(event.forget());
|
||||
while (queue->GetEvent(false, getter_AddRefs(event), lock)) {
|
||||
mEvents->PutEvent(event.forget(), lock);
|
||||
}
|
||||
|
||||
// Don't let the event target post any more events.
|
||||
|
||||
Reference in New Issue
Block a user