2013-06-14 22:01:02 +04:00
|
|
|
/* 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/. */
|
|
|
|
|
2013-09-12 18:47:37 +04:00
|
|
|
#include <algorithm>
|
|
|
|
#include <vector>
|
|
|
|
|
2013-06-14 22:01:02 +04:00
|
|
|
#include "IOInterposer.h"
|
2013-09-12 18:47:37 +04:00
|
|
|
|
2014-03-15 10:10:39 +04:00
|
|
|
#include "mozilla/Atomics.h"
|
2013-09-12 18:47:37 +04:00
|
|
|
#include "mozilla/Mutex.h"
|
2014-04-09 08:57:52 +04:00
|
|
|
#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)
|
2013-09-12 18:47:37 +04:00
|
|
|
#include "mozilla/StaticPtr.h"
|
2014-03-15 02:35:01 +04:00
|
|
|
#include "mozilla/ThreadLocal.h"
|
|
|
|
#if !defined(XP_WIN)
|
|
|
|
#include "NSPRInterposer.h"
|
|
|
|
#endif // !defined(XP_WIN)
|
|
|
|
#include "nsXULAppAPI.h"
|
|
|
|
#include "PoisonIOInterposer.h"
|
2013-06-14 22:01:02 +04:00
|
|
|
|
|
|
|
using namespace mozilla;
|
|
|
|
|
2013-09-12 18:47:37 +04:00
|
|
|
namespace {
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/** 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());
|
|
|
|
}
|
|
|
|
|
2013-09-12 18:47:37 +04:00
|
|
|
/** Lists of Observers */
|
2014-04-09 08:57:52 +04:00
|
|
|
struct ObserverLists : public AtomicRefCounted<ObserverLists>
|
|
|
|
{
|
2013-09-12 18:47:37 +04:00
|
|
|
ObserverLists()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
ObserverLists(ObserverLists const & aOther)
|
|
|
|
: mCreateObservers(aOther.mCreateObservers)
|
|
|
|
, mReadObservers(aOther.mReadObservers)
|
|
|
|
, mWriteObservers(aOther.mWriteObservers)
|
|
|
|
, mFSyncObservers(aOther.mFSyncObservers)
|
|
|
|
, mStatObservers(aOther.mStatObservers)
|
|
|
|
, mCloseObservers(aOther.mCloseObservers)
|
2013-09-12 18:47:37 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
// Lists of observers for read, write and fsync events respectively
|
|
|
|
// These are implemented as vectors since they are allowed to survive gecko,
|
|
|
|
// without reporting leaks. This is necessary for the IOInterposer to be used
|
|
|
|
// for late-write checks.
|
2013-10-01 23:53:19 +04:00
|
|
|
std::vector<IOInterposeObserver*> mCreateObservers;
|
2013-09-12 18:47:37 +04:00
|
|
|
std::vector<IOInterposeObserver*> mReadObservers;
|
|
|
|
std::vector<IOInterposeObserver*> mWriteObservers;
|
|
|
|
std::vector<IOInterposeObserver*> mFSyncObservers;
|
2013-10-01 23:53:19 +04:00
|
|
|
std::vector<IOInterposeObserver*> mStatObservers;
|
|
|
|
std::vector<IOInterposeObserver*> mCloseObservers;
|
2013-09-12 18:47:37 +04:00
|
|
|
};
|
2013-06-14 22:01:02 +04:00
|
|
|
|
2013-09-12 18:47:37 +04:00
|
|
|
/**
|
|
|
|
* A quick and dirty RAII class to automatically lock a PRLock
|
|
|
|
*/
|
|
|
|
class AutoPRLock
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2013-09-12 18:47:37 +04:00
|
|
|
PRLock* mLock;
|
|
|
|
public:
|
|
|
|
AutoPRLock(PRLock* aLock)
|
|
|
|
: mLock(aLock)
|
|
|
|
{
|
|
|
|
PR_Lock(aLock);
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
2013-09-12 18:47:37 +04:00
|
|
|
~AutoPRLock()
|
|
|
|
{
|
|
|
|
PR_Unlock(mLock);
|
|
|
|
}
|
|
|
|
};
|
2013-06-14 22:01:02 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
class PerThreadData
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
public:
|
|
|
|
PerThreadData(bool aIsMainThread = false)
|
|
|
|
: mIsMainThread(aIsMainThread)
|
|
|
|
, mIsHandlingObservation(false)
|
|
|
|
, mCurrentGeneration(0)
|
|
|
|
{
|
|
|
|
}
|
2013-06-14 22:01:02 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
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
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
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++;
|
|
|
|
}
|
2013-06-14 22:01:02 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
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
|
|
|
|
static StaticAutoPtr<MasterList> sMasterList;
|
|
|
|
static ThreadLocal<PerThreadData*> sThreadLocalData;
|
2013-09-12 18:47:37 +04:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2014-02-01 07:14:03 +04:00
|
|
|
IOInterposeObserver::Observation::Observation(Operation aOperation,
|
|
|
|
const char* aReference,
|
|
|
|
bool aShouldReport)
|
|
|
|
: mOperation(aOperation)
|
|
|
|
, mReference(aReference)
|
|
|
|
, mShouldReport(IOInterposer::IsObservedOperation(aOperation) &&
|
|
|
|
aShouldReport)
|
|
|
|
{
|
|
|
|
if (mShouldReport) {
|
|
|
|
mStart = TimeStamp::Now();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IOInterposeObserver::Observation::Observation(Operation aOperation,
|
|
|
|
const TimeStamp& aStart,
|
|
|
|
const TimeStamp& aEnd,
|
|
|
|
const char* aReference)
|
|
|
|
: mOperation(aOperation)
|
|
|
|
, mStart(aStart)
|
|
|
|
, mEnd(aEnd)
|
|
|
|
, mReference(aReference)
|
|
|
|
, mShouldReport(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
IOInterposeObserver::Observation::Report()
|
|
|
|
{
|
|
|
|
if (mShouldReport) {
|
|
|
|
mEnd = TimeStamp::Now();
|
|
|
|
IOInterposer::Report(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ bool
|
|
|
|
IOInterposer::Init()
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2013-09-12 18:47:37 +04:00
|
|
|
// Don't initialize twice...
|
2014-04-09 08:57:52 +04:00
|
|
|
if (sMasterList) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!sThreadLocalData.init()) {
|
|
|
|
return false;
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
2014-03-15 02:35:01 +04:00
|
|
|
#if defined(XP_WIN)
|
2014-04-09 08:57:52 +04:00
|
|
|
bool isMainThread = XRE_GetWindowsEnvironment() !=
|
|
|
|
WindowsEnvironmentType_Metro;
|
2014-03-15 02:35:01 +04:00
|
|
|
#else
|
2014-04-09 08:57:52 +04:00
|
|
|
bool isMainThread = true;
|
2014-03-15 02:35:01 +04:00
|
|
|
#endif
|
2014-04-09 08:57:52 +04:00
|
|
|
RegisterCurrentThread(isMainThread);
|
|
|
|
sMasterList = new MasterList();
|
|
|
|
|
2014-03-15 02:35:01 +04:00
|
|
|
// Now we initialize the various interposers depending on platform
|
|
|
|
InitPoisonIOInterposer();
|
|
|
|
// We don't hook NSPR on Windows because PoisonIOInterposer captures a
|
|
|
|
// superset of the former's events.
|
|
|
|
#if !defined(XP_WIN)
|
|
|
|
InitNSPRIOInterposing();
|
|
|
|
#endif
|
2014-04-09 08:57:52 +04:00
|
|
|
return true;
|
2014-03-15 02:35:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */ bool
|
|
|
|
IOInterposeObserver::IsMainThread()
|
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
if (!sThreadLocalData.initialized()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
PerThreadData *ptd = sThreadLocalData.get();
|
|
|
|
if (!ptd) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return ptd->IsMainThread();
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::Clear()
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
sMasterList = nullptr;
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
|
|
|
|
2014-03-15 10:10:39 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::Disable()
|
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
if (!sMasterList) {
|
2014-03-15 10:10:39 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-04-09 08:57:52 +04:00
|
|
|
sMasterList->Disable();
|
2014-03-15 10:10:39 +04:00
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::Report(IOInterposeObserver::Observation& aObservation)
|
2013-06-14 22:01:02 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
MOZ_ASSERT(sMasterList);
|
|
|
|
if (!sMasterList) {
|
2013-09-12 18:47:37 +04:00
|
|
|
return;
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
2013-09-12 18:47:37 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
PerThreadData* ptd = sThreadLocalData.get();
|
|
|
|
if (!ptd) {
|
|
|
|
// In this case the current thread is not registered with IOInterposer.
|
|
|
|
// Alternatively we could take the slow path and just lock everything if
|
|
|
|
// we're not registered. That could potentially perform poorly, though.
|
2013-09-12 18:47:37 +04:00
|
|
|
return;
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
2014-04-09 08:57:52 +04:00
|
|
|
sMasterList->Update(*ptd);
|
2013-06-14 22:01:02 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
// Don't try to report if there's nobody listening.
|
|
|
|
if (!IOInterposer::IsObservedOperation(aObservation.ObservedOperation())) {
|
|
|
|
return;
|
2013-09-12 18:47:37 +04:00
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
ptd->CallObservers(aObservation);
|
2013-09-12 18:47:37 +04:00
|
|
|
}
|
|
|
|
|
2014-03-15 10:10:39 +04:00
|
|
|
/* static */ bool
|
|
|
|
IOInterposer::IsObservedOperation(IOInterposeObserver::Operation aOp)
|
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
return sMasterList && sMasterList->IsObservedOperation(aOp);
|
2014-03-15 10:10:39 +04:00
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::Register(IOInterposeObserver::Operation aOp,
|
|
|
|
IOInterposeObserver* aObserver)
|
2013-09-12 18:47:37 +04:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aObserver);
|
2014-04-09 08:57:52 +04:00
|
|
|
if (!sMasterList || !aObserver) {
|
2013-09-12 18:47:37 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
sMasterList->Register(aOp, aObserver);
|
2013-06-14 22:01:02 +04:00
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::Unregister(IOInterposeObserver::Operation aOp,
|
|
|
|
IOInterposeObserver* aObserver)
|
2013-09-12 18:47:37 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
if (!sMasterList) {
|
2013-09-12 18:47:37 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
sMasterList->Unregister(aOp, aObserver);
|
|
|
|
}
|
2013-09-12 18:47:37 +04:00
|
|
|
|
2014-04-09 08:57:52 +04:00
|
|
|
/* static */ void
|
|
|
|
IOInterposer::RegisterCurrentThread(bool aIsMainThread)
|
|
|
|
{
|
|
|
|
if (!sThreadLocalData.initialized()) {
|
|
|
|
return;
|
2014-01-21 03:02:51 +04:00
|
|
|
}
|
2014-04-09 08:57:52 +04:00
|
|
|
MOZ_ASSERT(!sThreadLocalData.get());
|
|
|
|
PerThreadData* curThreadData = new PerThreadData(aIsMainThread);
|
|
|
|
sThreadLocalData.set(curThreadData);
|
2013-09-12 18:47:37 +04:00
|
|
|
}
|
2014-03-15 02:35:01 +04:00
|
|
|
|
|
|
|
/* static */ void
|
2014-04-09 08:57:52 +04:00
|
|
|
IOInterposer::UnregisterCurrentThread()
|
2014-03-15 02:35:01 +04:00
|
|
|
{
|
2014-04-09 08:57:52 +04:00
|
|
|
if (!sThreadLocalData.initialized()) {
|
2014-03-15 02:35:01 +04:00
|
|
|
return;
|
|
|
|
}
|
2014-04-09 08:57:52 +04:00
|
|
|
PerThreadData* curThreadData = sThreadLocalData.get();
|
|
|
|
MOZ_ASSERT(curThreadData);
|
|
|
|
sThreadLocalData.set(nullptr);
|
|
|
|
delete curThreadData;
|
2014-03-15 02:35:01 +04:00
|
|
|
}
|
|
|
|
|