Bug 913653: Remove lock from IOInterposer and add IOInterposer thread registration; r=froydnj

This commit is contained in:
Aaron Klotz 2014-04-08 22:57:52 -06:00
parent 1950826d5c
commit 7bd0ac22b6
7 changed files with 398 additions and 229 deletions

View File

@ -20,6 +20,7 @@
#include "mozIStorageFunction.h" #include "mozIStorageFunction.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "nsIVariant.h" #include "nsIVariant.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Services.h" #include "mozilla/Services.h"
// How long we collect write oprerations // How long we collect write oprerations
@ -279,9 +280,11 @@ void
DOMStorageDBThread::ThreadFunc(void* aArg) DOMStorageDBThread::ThreadFunc(void* aArg)
{ {
PR_SetCurrentThreadName("localStorage DB"); PR_SetCurrentThreadName("localStorage DB");
mozilla::IOInterposer::RegisterCurrentThread();
DOMStorageDBThread* thread = static_cast<DOMStorageDBThread*>(aArg); DOMStorageDBThread* thread = static_cast<DOMStorageDBThread*>(aArg);
thread->ThreadFunc(); thread->ThreadFunc();
mozilla::IOInterposer::UnregisterCurrentThread();
} }
void void

View File

@ -9,6 +9,7 @@
#include "base/thread_local.h" #include "base/thread_local.h"
#include "base/waitable_event.h" #include "base/waitable_event.h"
#include "GeckoProfiler.h" #include "GeckoProfiler.h"
#include "mozilla/IOInterposer.h"
namespace base { namespace base {
@ -139,6 +140,7 @@ void Thread::StopSoon() {
void Thread::ThreadMain() { void Thread::ThreadMain() {
char aLocal; char aLocal;
profiler_register_thread(name_.c_str(), &aLocal); profiler_register_thread(name_.c_str(), &aLocal);
mozilla::IOInterposer::RegisterCurrentThread();
// The message loop for this thread. // The message loop for this thread.
MessageLoop message_loop(startup_data_->options.message_loop_type); MessageLoop message_loop(startup_data_->options.message_loop_type);
@ -167,6 +169,7 @@ void Thread::ThreadMain() {
// Assert that MessageLoop::Quit was called by ThreadQuitTask. // Assert that MessageLoop::Quit was called by ThreadQuitTask.
DCHECK(GetThreadWasQuitProperly()); DCHECK(GetThreadWasQuitProperly());
mozilla::IOInterposer::UnregisterCurrentThread();
profiler_unregister_thread(); profiler_unregister_thread();
// We can't receive messages anymore. // We can't receive messages anymore.

View File

@ -9,6 +9,7 @@
#include "nsISupportsImpl.h" #include "nsISupportsImpl.h"
#include "nsPrintfCString.h" #include "nsPrintfCString.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/VisualEventTracer.h" #include "mozilla/VisualEventTracer.h"
namespace mozilla { namespace mozilla {
@ -152,8 +153,10 @@ already_AddRefed<nsIEventTarget> CacheIOThread::Target()
void CacheIOThread::ThreadFunc(void* aClosure) void CacheIOThread::ThreadFunc(void* aClosure)
{ {
PR_SetCurrentThreadName("Cache2 I/O"); PR_SetCurrentThreadName("Cache2 I/O");
mozilla::IOInterposer::RegisterCurrentThread();
CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure); CacheIOThread* thread = static_cast<CacheIOThread*>(aClosure);
thread->ThreadFunc(); thread->ThreadFunc();
mozilla::IOInterposer::UnregisterCurrentThread();
} }
void CacheIOThread::ThreadFunc() void CacheIOThread::ThreadFunc()

View File

@ -7,6 +7,7 @@
#include "prio.h" #include "prio.h"
#include "pldhash.h" #include "pldhash.h"
#include "nsXPCOMStrings.h" #include "nsXPCOMStrings.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/scache/StartupCache.h" #include "mozilla/scache/StartupCache.h"
@ -536,6 +537,7 @@ void
StartupCache::ThreadedWrite(void *aClosure) StartupCache::ThreadedWrite(void *aClosure)
{ {
PR_SetCurrentThreadName("StartupCache"); PR_SetCurrentThreadName("StartupCache");
mozilla::IOInterposer::RegisterCurrentThread();
/* /*
* It is safe to use the pointer passed in aClosure to reference the * It is safe to use the pointer passed in aClosure to reference the
* StartupCache object because the thread's lifetime is tightly coupled to * StartupCache object because the thread's lifetime is tightly coupled to
@ -545,6 +547,7 @@ StartupCache::ThreadedWrite(void *aClosure)
*/ */
StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure); StartupCache* startupCacheObj = static_cast<StartupCache*>(aClosure);
startupCacheObj->WriteToDisk(); startupCacheObj->WriteToDisk();
mozilla::IOInterposer::UnregisterCurrentThread();
} }
/* /*

View File

@ -9,6 +9,15 @@
#include "mozilla/Atomics.h" #include "mozilla/Atomics.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#if defined(MOZILLA_INTERNAL_API)
// We need to undefine MOZILLA_INTERNAL_API for RefPtr.h because IOInterposer
// does not clean up its data before shutdown.
#undef MOZILLA_INTERNAL_API
#include "mozilla/RefPtr.h"
#define MOZILLA_INTERNAL_API
#else
#include "mozilla/RefPtr.h"
#endif // defined(MOZILLA_INTERNAL_API)
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "mozilla/ThreadLocal.h" #include "mozilla/ThreadLocal.h"
#if !defined(XP_WIN) #if !defined(XP_WIN)
@ -21,34 +30,38 @@ using namespace mozilla;
namespace { namespace {
/** Find if a vector contains a specific element */
template<class T>
bool VectorContains(const std::vector<T>& vector, const T& element)
{
return std::find(vector.begin(), vector.end(), element) != vector.end();
}
/** Remove element from a vector */
template<class T>
void VectorRemove(std::vector<T>& vector, const T& element)
{
typename std::vector<T>::iterator newEnd = std::remove(vector.begin(),
vector.end(), element);
vector.erase(newEnd, vector.end());
}
/** Lists of Observers */ /** Lists of Observers */
struct ObserverLists { struct ObserverLists : public AtomicRefCounted<ObserverLists>
{
ObserverLists() ObserverLists()
: mObserverListsLock(PR_NewLock())
, mIsEnabled(true)
{ {
// We don't do MOZ_COUNT_CTOR(ObserverLists) as we will need to leak the
// IO interposer when doing late-write checks, which uses IO interposing
// to check for writes while static destructors are invoked.
} }
// mObserverListsLock guards access to lists of observers ObserverLists(ObserverLists const & aOther)
// Note, we can use mozilla::Mutex here as the ObserverLists may be leaked, : mCreateObservers(aOther.mCreateObservers)
// as we want to monitor IO during shutdown. Furthermore, as we may have to , mReadObservers(aOther.mReadObservers)
// unregister observers during shutdown an OffTheBooksMutex is not an option , mWriteObservers(aOther.mWriteObservers)
// either, as it base calls into sDeadlockDetector which may be nullptr , mFSyncObservers(aOther.mFSyncObservers)
// during shutdown. , mStatObservers(aOther.mStatObservers)
PRLock* mObserverListsLock; , mCloseObservers(aOther.mCloseObservers)
// Used for quickly disabling everything by IOInterposer::Disable()
mozilla::Atomic<bool> mIsEnabled;
~ObserverLists()
{ {
PR_DestroyLock(mObserverListsLock);
mObserverListsLock = nullptr;
} }
// Lists of observers for read, write and fsync events respectively // Lists of observers for read, write and fsync events respectively
// These are implemented as vectors since they are allowed to survive gecko, // These are implemented as vectors since they are allowed to survive gecko,
// without reporting leaks. This is necessary for the IOInterposer to be used // without reporting leaks. This is necessary for the IOInterposer to be used
@ -79,26 +92,271 @@ public:
} }
}; };
class PerThreadData
{
public:
PerThreadData(bool aIsMainThread = false)
: mIsMainThread(aIsMainThread)
, mIsHandlingObservation(false)
, mCurrentGeneration(0)
{
}
void
CallObservers(IOInterposeObserver::Observation& aObservation)
{
// Prevent recursive reporting.
if (mIsHandlingObservation) {
return;
}
mIsHandlingObservation = true;
// Decide which list of observers to inform
std::vector<IOInterposeObserver*>* observers = nullptr;
switch (aObservation.ObservedOperation()) {
case IOInterposeObserver::OpCreateOrOpen:
{
observers = &mObserverLists->mCreateObservers;
}
break;
case IOInterposeObserver::OpRead:
{
observers = &mObserverLists->mReadObservers;
}
break;
case IOInterposeObserver::OpWrite:
{
observers = &mObserverLists->mWriteObservers;
}
break;
case IOInterposeObserver::OpFSync:
{
observers = &mObserverLists->mFSyncObservers;
}
break;
case IOInterposeObserver::OpStat:
{
observers = &mObserverLists->mStatObservers;
}
break;
case IOInterposeObserver::OpClose:
{
observers = &mObserverLists->mCloseObservers;
}
break;
default:
{
// Invalid IO operation, see documentation comment for
// IOInterposer::Report()
MOZ_ASSERT(false);
// Just ignore it in non-debug builds.
return;
}
}
MOZ_ASSERT(observers);
// Inform observers
for (std::vector<IOInterposeObserver*>::iterator i = observers->begin(),
e = observers->end(); i != e; ++i)
{
(*i)->Observe(aObservation);
}
mIsHandlingObservation = false;
}
inline uint32_t
GetCurrentGeneration() const
{
return mCurrentGeneration;
}
inline bool
IsMainThread() const
{
return mIsMainThread;
}
inline void
SetObserverLists(uint32_t aNewGeneration, RefPtr<ObserverLists>& aNewLists)
{
mCurrentGeneration = aNewGeneration;
mObserverLists = aNewLists;
}
private:
bool mIsMainThread;
bool mIsHandlingObservation;
uint32_t mCurrentGeneration;
RefPtr<ObserverLists> mObserverLists;
};
class MasterList
{
public:
MasterList()
: mLock(PR_NewLock())
, mObservedOperations(IOInterposeObserver::OpNone)
, mIsEnabled(true)
{
}
~MasterList()
{
PR_DestroyLock(mLock);
mLock = nullptr;
}
inline void
Disable()
{
mIsEnabled = false;
}
void
Register(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
{
AutoPRLock lock(mLock);
ObserverLists* newLists = nullptr;
if (mObserverLists) {
newLists = new ObserverLists(*mObserverLists);
} else {
newLists = new ObserverLists();
}
// You can register to observe multiple types of observations
// but you'll never be registered twice for the same observations.
if (aOp & IOInterposeObserver::OpCreateOrOpen &&
!VectorContains(newLists->mCreateObservers, aObserver)) {
newLists->mCreateObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpRead &&
!VectorContains(newLists->mReadObservers, aObserver)) {
newLists->mReadObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpWrite &&
!VectorContains(newLists->mWriteObservers, aObserver)) {
newLists->mWriteObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpFSync &&
!VectorContains(newLists->mFSyncObservers, aObserver)) {
newLists->mFSyncObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpStat &&
!VectorContains(newLists->mStatObservers, aObserver)) {
newLists->mStatObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpClose &&
!VectorContains(newLists->mCloseObservers, aObserver)) {
newLists->mCloseObservers.push_back(aObserver);
}
mObserverLists = newLists;
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations | aOp);
mCurrentGeneration++;
}
void
Unregister(IOInterposeObserver::Operation aOp, IOInterposeObserver* aObserver)
{
AutoPRLock lock(mLock);
ObserverLists* newLists = nullptr;
if (mObserverLists) {
newLists = new ObserverLists(*mObserverLists);
} else {
newLists = new ObserverLists();
}
if (aOp & IOInterposeObserver::OpCreateOrOpen) {
VectorRemove(newLists->mCreateObservers, aObserver);
if (newLists->mCreateObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations &
~IOInterposeObserver::OpCreateOrOpen);
}
}
if (aOp & IOInterposeObserver::OpRead) {
VectorRemove(newLists->mReadObservers, aObserver);
if (newLists->mReadObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpRead);
}
}
if (aOp & IOInterposeObserver::OpWrite) {
VectorRemove(newLists->mWriteObservers, aObserver);
if (newLists->mWriteObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpWrite);
}
}
if (aOp & IOInterposeObserver::OpFSync) {
VectorRemove(newLists->mFSyncObservers, aObserver);
if (newLists->mFSyncObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpFSync);
}
}
if (aOp & IOInterposeObserver::OpStat) {
VectorRemove(newLists->mStatObservers, aObserver);
if (newLists->mStatObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpStat);
}
}
if (aOp & IOInterposeObserver::OpClose) {
VectorRemove(newLists->mCloseObservers, aObserver);
if (newLists->mCloseObservers.empty()) {
mObservedOperations = (IOInterposeObserver::Operation)
(mObservedOperations & ~IOInterposeObserver::OpClose);
}
}
mObserverLists = newLists;
mCurrentGeneration++;
}
void
Update(PerThreadData &aPtd)
{
if (mCurrentGeneration == aPtd.GetCurrentGeneration()) {
return;
}
// If the generation counts don't match then we need to update the current
// thread's observer list with the new master list.
AutoPRLock lock(mLock);
aPtd.SetObserverLists(mCurrentGeneration, mObserverLists);
}
inline bool
IsObservedOperation(IOInterposeObserver::Operation aOp)
{
// The quick reader may observe that no locks are being employed here,
// hence the result of the operations is truly undefined. However, most
// computers will usually return either true or false, which is good enough.
// It is not a problem if we occasionally report more or less IO than is
// actually occurring.
return mIsEnabled && !!(mObservedOperations & aOp);
}
private:
RefPtr<ObserverLists> mObserverLists;
// Note, we cannot use mozilla::Mutex here as the ObserverLists may be leaked
// (We want to monitor IO during shutdown). Furthermore, as we may have to
// unregister observers during shutdown an OffTheBooksMutex is not an option
// either, as its base calls into sDeadlockDetector which may be nullptr
// during shutdown.
PRLock* mLock;
// Flags tracking which operations are being observed
IOInterposeObserver::Operation mObservedOperations;
// Used for quickly disabling everything by IOInterposer::Disable()
Atomic<bool> mIsEnabled;
// Used to inform threads that the master observer list has changed
Atomic<uint32_t> mCurrentGeneration;
};
// List of observers registered // List of observers registered
static StaticAutoPtr<ObserverLists> sObserverLists; static StaticAutoPtr<MasterList> sMasterList;
static ThreadLocal<bool> sIsMainThread; static ThreadLocal<PerThreadData*> sThreadLocalData;
/** Find if a vector contains a specific element */
template<class T>
bool VectorContains(const std::vector<T>& vector, const T& element)
{
return std::find(vector.begin(), vector.end(), element) != vector.end();
}
/** Remove element from a vector */
template<class T>
void VectorRemove(std::vector<T>& vector, const T& element)
{
typename std::vector<T>::iterator newEnd = std::remove(vector.begin(),
vector.end(), element);
vector.erase(newEnd, vector.end());
}
} // anonymous namespace } // anonymous namespace
IOInterposeObserver::Observation::Observation(Operation aOperation, IOInterposeObserver::Observation::Observation(Operation aOperation,
@ -135,27 +393,25 @@ IOInterposeObserver::Observation::Report()
} }
} }
// Flags tracking which operations are being observed /* static */ bool
IOInterposeObserver::Operation IOInterposer::sObservedOperations = IOInterposer::Init()
IOInterposeObserver::OpNone;
/* static */ void IOInterposer::Init()
{ {
// Don't initialize twice... // Don't initialize twice...
if (sObserverLists) { if (sMasterList) {
return; return true;
}
if (!sThreadLocalData.init()) {
return false;
} }
sObserverLists = new ObserverLists();
sObservedOperations = IOInterposeObserver::OpNone;
if (sIsMainThread.init()) {
#if defined(XP_WIN) #if defined(XP_WIN)
bool isMainThread = XRE_GetWindowsEnvironment() != bool isMainThread = XRE_GetWindowsEnvironment() !=
WindowsEnvironmentType_Metro; WindowsEnvironmentType_Metro;
#else #else
bool isMainThread = true; bool isMainThread = true;
#endif #endif
sIsMainThread.set(isMainThread); RegisterCurrentThread(isMainThread);
} sMasterList = new MasterList();
// Now we initialize the various interposers depending on platform // Now we initialize the various interposers depending on platform
InitPoisonIOInterposer(); InitPoisonIOInterposer();
// We don't hook NSPR on Windows because PoisonIOInterposer captures a // We don't hook NSPR on Windows because PoisonIOInterposer captures a
@ -163,225 +419,111 @@ IOInterposeObserver::Operation IOInterposer::sObservedOperations =
#if !defined(XP_WIN) #if !defined(XP_WIN)
InitNSPRIOInterposing(); InitNSPRIOInterposing();
#endif #endif
return true;
} }
/* static */ bool /* static */ bool
IOInterposeObserver::IsMainThread() IOInterposeObserver::IsMainThread()
{ {
return sIsMainThread.initialized() && sIsMainThread.get(); if (!sThreadLocalData.initialized()) {
return false;
}
PerThreadData *ptd = sThreadLocalData.get();
if (!ptd) {
return false;
}
return ptd->IsMainThread();
} }
/* static */ void IOInterposer::Clear() /* static */ void
IOInterposer::Clear()
{ {
// Clear() shouldn't be called if Init() wasn't called, sMasterList = nullptr;
MOZ_ASSERT(sObserverLists);
if (sObserverLists) {
// We require everybody unregister before clearing. If somebody didn't then
// this is probably a case where one consumer clears the IO interposer and
// another consumer still wants events.
MOZ_ASSERT(sObserverLists->mReadObservers.empty());
MOZ_ASSERT(sObserverLists->mWriteObservers.empty());
MOZ_ASSERT(sObserverLists->mFSyncObservers.empty());
sObserverLists = nullptr;
sObservedOperations = IOInterposeObserver::OpNone;
}
} }
/* static */ void /* static */ void
IOInterposer::Disable() IOInterposer::Disable()
{ {
if (!sObserverLists) { if (!sMasterList) {
return; return;
} }
sObserverLists->mIsEnabled = false; sMasterList->Disable();
} }
/* static */ void IOInterposer::Report( /* static */ void
IOInterposeObserver::Observation& aObservation) IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
{ {
// IOInterposer::Init most be called before this method MOZ_ASSERT(sMasterList);
MOZ_ASSERT(sObserverLists); if (!sMasterList) {
if (!sObserverLists) {
return; return;
} }
//TODO: We only need read access here, so we should investigate the PerThreadData* ptd = sThreadLocalData.get();
// performance overhead involved in using some kind of shared lock. if (!ptd) {
// Work towards this end is tracked in bug #913653 // In this case the current thread is not registered with IOInterposer.
AutoPRLock listLock(sObserverLists->mObserverListsLock); // Alternatively we could take the slow path and just lock everything if
// we're not registered. That could potentially perform poorly, though.
return;
}
sMasterList->Update(*ptd);
// Don't try to report if there's nobody listening // Don't try to report if there's nobody listening.
if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) { if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
return; return;
} }
// Decide which list of observers to inform ptd->CallObservers(aObservation);
std::vector<IOInterposeObserver*>* observers = nullptr;
switch (aObservation.ObservedOperation()) {
case IOInterposeObserver::OpCreateOrOpen:
{
observers = &sObserverLists->mCreateObservers;
}
break;
case IOInterposeObserver::OpRead:
{
observers = &sObserverLists->mReadObservers;
}
break;
case IOInterposeObserver::OpWrite:
{
observers = &sObserverLists->mWriteObservers;
}
break;
case IOInterposeObserver::OpFSync:
{
observers = &sObserverLists->mFSyncObservers;
}
break;
case IOInterposeObserver::OpStat:
{
observers = &sObserverLists->mStatObservers;
}
break;
case IOInterposeObserver::OpClose:
{
observers = &sObserverLists->mCloseObservers;
}
break;
default:
{
// Invalid IO operation, see documentation comment for Report()
MOZ_ASSERT(false);
// Just ignore is in non-debug builds.
return;
}
}
MOZ_ASSERT(observers);
// Inform observers
uint32_t nObservers = observers->size();
for (uint32_t i = 0; i < nObservers; ++i) {
(*observers)[i]->Observe(aObservation);
}
} }
/* static */ bool /* static */ bool
IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp) IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
{ {
return sObserverLists && sObserverLists->mIsEnabled && return sMasterList && sMasterList->IsObservedOperation(aOp);
!!(sObservedOperations & aOp);
} }
/* static */ void IOInterposer::Register(IOInterposeObserver::Operation aOp, /* static */ void
IOInterposeObserver* aObserver) IOInterposer::Register(IOInterposeObserver::Operation aOp,
IOInterposeObserver* aObserver)
{ {
// We should never register nullptr as observer
MOZ_ASSERT(aObserver); MOZ_ASSERT(aObserver);
if (!sObserverLists || !aObserver) { if (!sMasterList || !aObserver) {
return; return;
} }
AutoPRLock listLock(sObserverLists->mObserverListsLock); sMasterList->Register(aOp, aObserver);
// You can register to observe multiple types of observations
// but you'll never be registered twice for the same observations.
if (aOp & IOInterposeObserver::OpCreateOrOpen &&
!VectorContains(sObserverLists->mCreateObservers, aObserver)) {
sObserverLists->mCreateObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpRead &&
!VectorContains(sObserverLists->mReadObservers, aObserver)) {
sObserverLists->mReadObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpWrite &&
!VectorContains(sObserverLists->mWriteObservers, aObserver)) {
sObserverLists->mWriteObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpFSync &&
!VectorContains(sObserverLists->mFSyncObservers, aObserver)) {
sObserverLists->mFSyncObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpStat &&
!VectorContains(sObserverLists->mStatObservers, aObserver)) {
sObserverLists->mStatObservers.push_back(aObserver);
}
if (aOp & IOInterposeObserver::OpClose &&
!VectorContains(sObserverLists->mCloseObservers, aObserver)) {
sObserverLists->mCloseObservers.push_back(aObserver);
}
// Update field of observed operation with the operations that the new
// observer is observing.
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations | aOp);
} }
/* static */ void IOInterposer::Unregister(IOInterposeObserver::Operation aOp, /* static */ void
IOInterposeObserver* aObserver) IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
IOInterposeObserver* aObserver)
{ {
if (!sObserverLists) { if (!sMasterList) {
return; return;
} }
AutoPRLock listLock(sObserverLists->mObserverListsLock); sMasterList->Unregister(aOp, aObserver);
if (aOp & IOInterposeObserver::OpCreateOrOpen) {
VectorRemove(sObserverLists->mCreateObservers, aObserver);
if (sObserverLists->mCreateObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations &
~IOInterposeObserver::OpCreateOrOpen);
}
}
if (aOp & IOInterposeObserver::OpRead) {
VectorRemove(sObserverLists->mReadObservers, aObserver);
if (sObserverLists->mReadObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations & ~IOInterposeObserver::OpRead);
}
}
if (aOp & IOInterposeObserver::OpWrite) {
VectorRemove(sObserverLists->mWriteObservers, aObserver);
if (sObserverLists->mWriteObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations & ~IOInterposeObserver::OpWrite);
}
}
if (aOp & IOInterposeObserver::OpFSync) {
VectorRemove(sObserverLists->mFSyncObservers, aObserver);
if (sObserverLists->mFSyncObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations & ~IOInterposeObserver::OpFSync);
}
}
if (aOp & IOInterposeObserver::OpStat) {
VectorRemove(sObserverLists->mStatObservers, aObserver);
if (sObserverLists->mStatObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations & ~IOInterposeObserver::OpStat);
}
}
if (aOp & IOInterposeObserver::OpClose) {
VectorRemove(sObserverLists->mCloseObservers, aObserver);
if (sObserverLists->mCloseObservers.empty()) {
sObservedOperations = (IOInterposeObserver::Operation)
(sObservedOperations & ~IOInterposeObserver::OpClose);
}
}
} }
/* static */ void /* static */ void
IOInterposer::RegisterCurrentThread(bool aIsMainThread) IOInterposer::RegisterCurrentThread(bool aIsMainThread)
{ {
// Right now this is a no-op unless we're running on Metro. if (!sThreadLocalData.initialized()) {
// More cross-platform stuff will be added in the near future, stay tuned!
#if defined(XP_WIN)
if (XRE_GetWindowsEnvironment() != WindowsEnvironmentType_Metro ||
!sIsMainThread.initialized()) {
return; return;
} }
sIsMainThread.set(aIsMainThread); MOZ_ASSERT(!sThreadLocalData.get());
#endif PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
sThreadLocalData.set(curThreadData);
}
/* static */ void
IOInterposer::UnregisterCurrentThread()
{
if (!sThreadLocalData.initialized()) {
return;
}
PerThreadData* curThreadData = sThreadLocalData.get();
MOZ_ASSERT(curThreadData);
sThreadLocalData.set(nullptr);
delete curThreadData;
} }

View File

@ -161,9 +161,6 @@ protected:
*/ */
class IOInterposer MOZ_FINAL class IOInterposer MOZ_FINAL
{ {
// Track whether or not a given type of observation is being observed
static IOInterposeObserver::Operation sObservedOperations;
// No instance of class should be created, they'd be empty anyway. // No instance of class should be created, they'd be empty anyway.
IOInterposer(); IOInterposer();
public: public:
@ -178,7 +175,7 @@ public:
* Remark, it's safe to call this method multiple times, so just call it when * Remark, it's safe to call this method multiple times, so just call it when
* you to utilize IO interposing. * you to utilize IO interposing.
*/ */
static void Init(); static bool Init();
/** /**
* This function must be called from the main thread, and furthermore * This function must be called from the main thread, and furthermore
@ -249,13 +246,26 @@ public:
IOInterposeObserver* aObserver); IOInterposeObserver* aObserver);
/** /**
* Registers the current thread with the IOInterposer. * Registers the current thread with the IOInterposer. This must be done to
* ensure that per-thread data is created in an orderly fashion.
* We could have written this to initialize that data lazily, however this
* could have unintended consequences if a thread that is not aware of
* IOInterposer was implicitly registered: its per-thread data would never
* be deleted because it would not know to unregister itself.
* *
* @param aIsMainThread true if IOInterposer should treat the current thread * @param aIsMainThread true if IOInterposer should treat the current thread
* as the main thread. * as the main thread.
*/ */
static void static void
RegisterCurrentThread(bool aIsMainThread = false); RegisterCurrentThread(bool aIsMainThread = false);
/**
* Unregisters the current thread with the IOInterposer. This is important
* to call when a thread is shutting down because it cleans up data that
* is stored in a TLS slot.
*/
static void
UnregisterCurrentThread();
}; };
class IOInterposerInit class IOInterposerInit

View File

@ -24,6 +24,7 @@
#include "prlog.h" #include "prlog.h"
#include "nsIObserverService.h" #include "nsIObserverService.h"
#include "mozilla/HangMonitor.h" #include "mozilla/HangMonitor.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Services.h" #include "mozilla/Services.h"
#include "nsXPCOMPrivate.h" #include "nsXPCOMPrivate.h"
#include "mozilla/ChaosMode.h" #include "mozilla/ChaosMode.h"
@ -291,6 +292,8 @@ nsThread::ThreadFunc(void *arg)
// Inform the ThreadManager // Inform the ThreadManager
nsThreadManager::get()->RegisterCurrentThread(self); nsThreadManager::get()->RegisterCurrentThread(self);
mozilla::IOInterposer::RegisterCurrentThread();
// Wait for and process startup event // Wait for and process startup event
nsCOMPtr<nsIRunnable> event; nsCOMPtr<nsIRunnable> event;
if (!self->GetEvent(true, getter_AddRefs(event))) { if (!self->GetEvent(true, getter_AddRefs(event))) {
@ -328,6 +331,8 @@ nsThread::ThreadFunc(void *arg)
} }
} }
mozilla::IOInterposer::UnregisterCurrentThread();
// Inform the threadmanager that this thread is going away // Inform the threadmanager that this thread is going away
nsThreadManager::get()->UnregisterCurrentThread(self); nsThreadManager::get()->UnregisterCurrentThread(self);