Bug 1121676 - Use a lock to protect the list of top-level actors (r=bent)

This commit is contained in:
Bill McCloskey 2015-02-26 22:31:00 -08:00 committed by Bill McCloskey
parent 8fb0643e63
commit 5389443ac0
5 changed files with 120 additions and 74 deletions

View File

@ -551,19 +551,20 @@ InitOnContentProcessCreated()
#ifdef MOZ_NUWA_PROCESS #ifdef MOZ_NUWA_PROCESS
static void static void
ResetTransports(void* aUnused) { ResetTransports(void* aUnused)
{
ContentChild* child = ContentChild::GetSingleton(); ContentChild* child = ContentChild::GetSingleton();
mozilla::ipc::Transport* transport = child->GetTransport(); mozilla::ipc::Transport* transport = child->GetTransport();
int fd = transport->GetFileDescriptor(); int fd = transport->GetFileDescriptor();
transport->ResetFileDescriptor(fd); transport->ResetFileDescriptor(fd);
IToplevelProtocol* toplevel = child->GetFirstOpenedActors(); nsTArray<IToplevelProtocol*> actors;
while (toplevel != nullptr) { child->GetOpenedActors(actors);
for (size_t i = 0; i < actors.Length(); i++) {
IToplevelProtocol* toplevel = actors[i];
transport = toplevel->GetTransport(); transport = toplevel->GetTransport();
fd = transport->GetFileDescriptor(); fd = transport->GetFileDescriptor();
transport->ResetFileDescriptor(fd); transport->ResetFileDescriptor(fd);
toplevel = toplevel->getNext();
} }
} }
#endif #endif
@ -2677,9 +2678,10 @@ GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
content->GetTransport()->GetFileDescriptor(); content->GetTransport()->GetFileDescriptor();
i++; i++;
for (IToplevelProtocol* actor = content->GetFirstOpenedActors(); IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
actor != nullptr; size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
actor = actor->getNext()) { for (size_t j = 0; j < count; j++) {
IToplevelProtocol* actor = actors[j];
if (i >= aInfoListSize) { if (i >= aInfoListSize) {
NS_RUNTIMEABORT("Too many top level protocols!"); NS_RUNTIMEABORT("Too many top level protocols!");
} }

View File

@ -21,19 +21,56 @@ using base::ProcessId;
namespace mozilla { namespace mozilla {
namespace ipc { namespace ipc {
#ifdef MOZ_IPDL_TESTS static Atomic<size_t> gNumProtocols;
bool IToplevelProtocol::sAllowNonMainThreadUse; static StaticAutoPtr<Mutex> gProtocolMutex;
#endif
IToplevelProtocol::IToplevelProtocol(ProtocolId aProtoId)
: mOpener(nullptr)
, mProtocolId(aProtoId)
, mTrans(nullptr)
{
size_t old = gNumProtocols++;
if (!old) {
// We assume that two threads never race to create the first protocol. This
// assertion is sufficient to ensure that.
MOZ_ASSERT(NS_IsMainThread());
gProtocolMutex = new Mutex("ITopLevelProtocol::ProtocolMutex");
}
}
IToplevelProtocol::~IToplevelProtocol() IToplevelProtocol::~IToplevelProtocol()
{ {
MOZ_ASSERT(NS_IsMainThread() || AllowNonMainThreadUse()); bool last = false;
mOpenActors.clear();
{
MutexAutoLock al(*gProtocolMutex);
for (IToplevelProtocol* actor = mOpenActors.getFirst();
actor;
actor = actor->getNext()) {
actor->mOpener = nullptr;
}
mOpenActors.clear();
if (mOpener) {
removeFrom(mOpener->mOpenActors);
}
gNumProtocols--;
last = gNumProtocols == 0;
}
if (last) {
gProtocolMutex = nullptr;
}
} }
void IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor) void
IToplevelProtocol::AddOpenedActorLocked(IToplevelProtocol* aActor)
{ {
MOZ_ASSERT(NS_IsMainThread() || AllowNonMainThreadUse()); gProtocolMutex->AssertCurrentThreadOwns();
#ifdef DEBUG #ifdef DEBUG
for (const IToplevelProtocol* actor = mOpenActors.getFirst(); for (const IToplevelProtocol* actor = mOpenActors.getFirst();
@ -44,9 +81,49 @@ void IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor)
} }
#endif #endif
aActor->mOpener = this;
mOpenActors.insertBack(aActor); mOpenActors.insertBack(aActor);
} }
void
IToplevelProtocol::AddOpenedActor(IToplevelProtocol* aActor)
{
MutexAutoLock al(*gProtocolMutex);
AddOpenedActorLocked(aActor);
}
void
IToplevelProtocol::GetOpenedActorsLocked(nsTArray<IToplevelProtocol*>& aActors)
{
gProtocolMutex->AssertCurrentThreadOwns();
for (IToplevelProtocol* actor = mOpenActors.getFirst();
actor;
actor = actor->getNext()) {
aActors.AppendElement(actor);
}
}
void
IToplevelProtocol::GetOpenedActors(nsTArray<IToplevelProtocol*>& aActors)
{
MutexAutoLock al(*gProtocolMutex);
GetOpenedActorsLocked(aActors);
}
size_t
IToplevelProtocol::GetOpenedActorsUnsafe(IToplevelProtocol** aActors, size_t aActorsMax)
{
size_t count = 0;
for (IToplevelProtocol* actor = mOpenActors.getFirst();
actor;
actor = actor->getNext()) {
MOZ_RELEASE_ASSERT(count < aActorsMax);
aActors[count++] = actor;
}
return count;
}
IToplevelProtocol* IToplevelProtocol*
IToplevelProtocol::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds, IToplevelProtocol::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
base::ProcessHandle aPeerProcess, base::ProcessHandle aPeerProcess,
@ -62,22 +139,17 @@ IToplevelProtocol::CloneOpenedToplevels(IToplevelProtocol* aTemplate,
base::ProcessHandle aPeerProcess, base::ProcessHandle aPeerProcess,
ProtocolCloneContext* aCtx) ProtocolCloneContext* aCtx)
{ {
for (IToplevelProtocol* actor = aTemplate->GetFirstOpenedActors(); MutexAutoLock al(*gProtocolMutex);
actor;
actor = actor->getNext()) { nsTArray<IToplevelProtocol*> actors;
IToplevelProtocol* newactor = actor->CloneToplevel(aFds, aPeerProcess, aCtx); aTemplate->GetOpenedActorsLocked(actors);
AddOpenedActor(newactor);
for (size_t i = 0; i < actors.Length(); i++) {
IToplevelProtocol* newactor = actors[i]->CloneToplevel(aFds, aPeerProcess, aCtx);
AddOpenedActorLocked(newactor);
} }
} }
#ifdef MOZ_IPDL_TESTS
void
IToplevelProtocol::SetAllowNonMainThreadUse()
{
sAllowNonMainThreadUse = true;
}
#endif
class ChannelOpened : public IPC::Message class ChannelOpened : public IPC::Message
{ {
public: public:

View File

@ -21,6 +21,7 @@
#include "mozilla/ipc/Transport.h" #include "mozilla/ipc/Transport.h"
#include "mozilla/ipc/MessageLink.h" #include "mozilla/ipc/MessageLink.h"
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/Mutex.h"
#include "MainThreadUtils.h" #include "MainThreadUtils.h"
#if defined(ANDROID) && defined(DEBUG) #if defined(ANDROID) && defined(DEBUG)
@ -181,16 +182,13 @@ public:
* IToplevelProtocol tracks all top-level protocol actors created from * IToplevelProtocol tracks all top-level protocol actors created from
* this protocol actor. * this protocol actor.
*/ */
class IToplevelProtocol : public LinkedListElement<IToplevelProtocol> class IToplevelProtocol : private LinkedListElement<IToplevelProtocol>
{ {
protected: friend class LinkedList<IToplevelProtocol>;
explicit IToplevelProtocol(ProtocolId aProtoId) friend class LinkedListElement<IToplevelProtocol>;
: mProtocolId(aProtoId)
, mTrans(nullptr)
{
MOZ_ASSERT(NS_IsMainThread() || AllowNonMainThreadUse());
}
protected:
explicit IToplevelProtocol(ProtocolId aProtoId);
~IToplevelProtocol(); ~IToplevelProtocol();
/** /**
@ -209,19 +207,13 @@ public:
ProtocolId GetProtocolId() const { return mProtocolId; } ProtocolId GetProtocolId() const { return mProtocolId; }
/** void GetOpenedActors(nsTArray<IToplevelProtocol*>& aActors);
* Return first of actors of top level protocols opened by this one.
*/ // This Unsafe version should only be used when all other threads are
IToplevelProtocol* GetFirstOpenedActors() // frozen, since it performs no locking. It also takes a stack-allocated
{ // array and its size (number of elements) rather than an nsTArray. The Nuwa
MOZ_ASSERT(NS_IsMainThread() || AllowNonMainThreadUse()); // code that calls this function is not allowed to allocate memory.
return mOpenActors.getFirst(); size_t GetOpenedActorsUnsafe(IToplevelProtocol** aActors, size_t aActorsMax);
}
const IToplevelProtocol* GetFirstOpenedActors() const
{
MOZ_ASSERT(NS_IsMainThread() || AllowNonMainThreadUse());
return mOpenActors.getFirst();
}
virtual IToplevelProtocol* virtual IToplevelProtocol*
CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds, CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
@ -233,27 +225,15 @@ public:
base::ProcessHandle aPeerProcess, base::ProcessHandle aPeerProcess,
ProtocolCloneContext* aCtx); ProtocolCloneContext* aCtx);
#ifdef MOZ_IPDL_TESTS
static void SetAllowNonMainThreadUse();
#endif
static bool AllowNonMainThreadUse() {
#ifdef MOZ_IPDL_TESTS
return sAllowNonMainThreadUse;
#else
return false;
#endif
}
private: private:
void AddOpenedActorLocked(IToplevelProtocol* aActor);
void GetOpenedActorsLocked(nsTArray<IToplevelProtocol*>& aActors);
LinkedList<IToplevelProtocol> mOpenActors; // All protocol actors opened by this. LinkedList<IToplevelProtocol> mOpenActors; // All protocol actors opened by this.
IToplevelProtocol* mOpener;
ProtocolId mProtocolId; ProtocolId mProtocolId;
Transport* mTrans; Transport* mTrans;
#ifdef MOZ_IPDL_TESTS
static bool sAllowNonMainThreadUse;
#endif
}; };

View File

@ -139,10 +139,6 @@ IPDLUnitTestMain(void* aData)
{ {
char* testString = reinterpret_cast<char*>(aData); char* testString = reinterpret_cast<char*>(aData);
// Some tests require this, and we don't care what thread we're on if we're
// not using Nuwa.
mozilla::ipc::IToplevelProtocol::SetAllowNonMainThreadUse();
// Check if we are to run the test using threads instead: // Check if we are to run the test using threads instead:
const char *prefix = "thread:"; const char *prefix = "thread:";
const int prefixLen = strlen(prefix); const int prefixLen = strlen(prefix);
@ -378,10 +374,6 @@ IPDLUnitTestChildInit(IPC::Channel* transport,
base::ProcessHandle parent, base::ProcessHandle parent,
MessageLoop* worker) MessageLoop* worker)
{ {
// Some tests require this, and we don't care what thread we're on if we're
// not using Nuwa.
mozilla::ipc::IToplevelProtocol::SetAllowNonMainThreadUse();
switch (IPDLUnitTest()) { switch (IPDLUnitTest()) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
//===== TEMPLATED ===== //===== TEMPLATED =====

View File

@ -867,7 +867,7 @@ class B2GOptions(MochitestOptions):
defaults["testPath"] = "" defaults["testPath"] = ""
defaults["extensionsToExclude"] = ["specialpowers"] defaults["extensionsToExclude"] = ["specialpowers"]
# See dependencies of bug 1038943. # See dependencies of bug 1038943.
defaults["defaultLeakThreshold"] = 5404 defaults["defaultLeakThreshold"] = 5536
self.set_defaults(**defaults) self.set_defaults(**defaults)
def verifyRemoteOptions(self, options): def verifyRemoteOptions(self, options):