Bug 1173756 - Part 3: Merge File and ChildRunnable; r=luke

This commit is contained in:
Jan Varga 2015-08-29 07:45:24 +02:00
parent 414775f261
commit 3ca2c55387

View File

@ -237,9 +237,7 @@ EvictEntries(nsIFile* aDirectory, const nsACString& aGroup,
// FileDescriptorHolder owns a file descriptor and its memory mapping. // FileDescriptorHolder owns a file descriptor and its memory mapping.
// FileDescriptorHolder is derived by two runnable classes (that is, // FileDescriptorHolder is derived by two runnable classes (that is,
// (Parent|Child)Runnable. FileDescriptorHolder is derived by File and // (Parent|Child)Runnable.
// ParentRunnable. Since File and ParentRunnable both need to be runnables
// FileDescriptorHolder also derives nsRunnable.
class FileDescriptorHolder : public nsRunnable class FileDescriptorHolder : public nsRunnable
{ {
public: public:
@ -331,153 +329,6 @@ protected:
void* mMappedMemory; void* mMappedMemory;
}; };
// File is a base class derived by ChildRunnable that presents a single
// interface to the AsmJSCache ops which need to wait until the file is open.
class File : public FileDescriptorHolder
{
public:
class AutoClose
{
File* mFile;
public:
explicit AutoClose(File* aFile = nullptr)
: mFile(aFile)
{ }
void
Init(File* aFile)
{
MOZ_ASSERT(!mFile);
mFile = aFile;
}
File*
operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
MOZ_ASSERT(mFile);
return mFile;
}
void
Forget(File** aFile)
{
*aFile = mFile;
mFile = nullptr;
}
~AutoClose()
{
if (mFile) {
mFile->Close();
}
}
};
JS::AsmJSCacheResult
BlockUntilOpen(AutoClose* aCloser)
{
MOZ_ASSERT(!mWaiting, "Can only call BlockUntilOpen once");
MOZ_ASSERT(!mOpened, "Can only call BlockUntilOpen once");
mWaiting = true;
nsresult rv = NS_DispatchToMainThread(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return JS::AsmJSCache_InternalError;
}
{
MutexAutoLock lock(mMutex);
while (mWaiting) {
mCondVar.Wait();
}
}
if (!mOpened) {
return mResult;
}
// Now that we're open, we're guarnateed a Close() call. However, we are
// not guarnateed someone is holding an outstanding reference until the File
// is closed, so we do that ourselves and Release() in OnClose().
aCloser->Init(this);
AddRef();
return JS::AsmJSCache_Success;
}
// This method must be called if BlockUntilOpen returns 'true'. AutoClose
// mostly takes care of this. A derived class that implements Close() must
// guarnatee that OnClose() is called (eventually).
virtual void
Close() = 0;
protected:
File()
: mMutex("File::mMutex"),
mCondVar(mMutex, "File::mCondVar"),
mWaiting(false),
mOpened(false),
mResult(JS::AsmJSCache_InternalError)
{ }
~File()
{
MOZ_ASSERT(!mWaiting, "Shouldn't be destroyed while thread is waiting");
MOZ_ASSERT(!mOpened, "OnClose() should have been called");
}
void
OnOpen()
{
Notify(JS::AsmJSCache_Success);
}
void
OnFailure(JS::AsmJSCacheResult aResult)
{
MOZ_ASSERT(aResult != JS::AsmJSCache_Success);
FileDescriptorHolder::Finish();
Notify(aResult);
}
void
OnClose()
{
FileDescriptorHolder::Finish();
MOZ_ASSERT(mOpened);
mOpened = false;
// Match the AddRef in BlockUntilOpen(). The main thread event loop still
// holds an outstanding ref which will keep 'this' alive until returning to
// the event loop.
Release();
}
private:
void
Notify(JS::AsmJSCacheResult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mWaiting);
mWaiting = false;
mOpened = aResult == JS::AsmJSCache_Success;
mResult = aResult;
mCondVar.Notify();
}
Mutex mMutex;
CondVar mCondVar;
bool mWaiting;
bool mOpened;
JS::AsmJSCacheResult mResult;
};
class UnlockDirectoryRunnable final class UnlockDirectoryRunnable final
: public nsRunnable : public nsRunnable
{ {
@ -1313,14 +1164,54 @@ DeallocEntryParent(PAsmJSCacheEntryParent* aActor)
namespace { namespace {
// A runnable that presents a single interface to the AsmJSCache ops which need
// to wait until the file is open.
class ChildRunnable final class ChildRunnable final
: public File : public FileDescriptorHolder
, public PAsmJSCacheEntryChild , public PAsmJSCacheEntryChild
, public nsIIPCBackgroundChildCreateCallback , public nsIIPCBackgroundChildCreateCallback
{ {
typedef mozilla::ipc::PBackgroundChild PBackgroundChild; typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
public: public:
class AutoClose
{
ChildRunnable* mChildRunnable;
public:
explicit AutoClose(ChildRunnable* aChildRunnable = nullptr)
: mChildRunnable(aChildRunnable)
{ }
void
Init(ChildRunnable* aChildRunnable)
{
MOZ_ASSERT(!mChildRunnable);
mChildRunnable = aChildRunnable;
}
ChildRunnable*
operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN
{
MOZ_ASSERT(mChildRunnable);
return mChildRunnable;
}
void
Forget(ChildRunnable** aChildRunnable)
{
*aChildRunnable = mChildRunnable;
mChildRunnable = nullptr;
}
~AutoClose()
{
if (mChildRunnable) {
mChildRunnable->Close();
}
}
};
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE NS_DECL_NSIRUNNABLE
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
@ -1330,25 +1221,64 @@ public:
WriteParams aWriteParams, WriteParams aWriteParams,
ReadParams aReadParams) ReadParams aReadParams)
: mPrincipal(aPrincipal), : mPrincipal(aPrincipal),
mOpenMode(aOpenMode),
mWriteParams(aWriteParams), mWriteParams(aWriteParams),
mReadParams(aReadParams), mReadParams(aReadParams),
mMutex("ChildRunnable::mMutex"),
mCondVar(mMutex, "ChildRunnable::mCondVar"),
mOpenMode(aOpenMode),
mState(eInitial),
mResult(JS::AsmJSCache_InternalError),
mActorDestroyed(false), mActorDestroyed(false),
mState(eInitial) mWaiting(false),
mOpened(false)
{ {
MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!NS_IsMainThread());
MOZ_COUNT_CTOR(ChildRunnable); MOZ_COUNT_CTOR(ChildRunnable);
} }
protected: JS::AsmJSCacheResult
BlockUntilOpen(AutoClose* aCloser)
{
MOZ_ASSERT(!mWaiting, "Can only call BlockUntilOpen once");
MOZ_ASSERT(!mOpened, "Can only call BlockUntilOpen once");
mWaiting = true;
nsresult rv = NS_DispatchToMainThread(this);
if (NS_WARN_IF(NS_FAILED(rv))) {
return JS::AsmJSCache_InternalError;
}
{
MutexAutoLock lock(mMutex);
while (mWaiting) {
mCondVar.Wait();
}
}
if (!mOpened) {
return mResult;
}
// Now that we're open, we're guaranteed a Close() call. However, we are
// not guaranteed someone is holding an outstanding reference until the File
// is closed, so we do that ourselves and Release() in OnClose().
aCloser->Init(this);
AddRef();
return JS::AsmJSCache_Success;
}
private:
~ChildRunnable() ~ChildRunnable()
{ {
MOZ_ASSERT(!mWaiting, "Shouldn't be destroyed while thread is waiting");
MOZ_ASSERT(!mOpened);
MOZ_ASSERT(mState == eFinished); MOZ_ASSERT(mState == eFinished);
MOZ_ASSERT(mActorDestroyed); MOZ_ASSERT(mActorDestroyed);
MOZ_COUNT_DTOR(ChildRunnable); MOZ_COUNT_DTOR(ChildRunnable);
} }
private: // IPDL methods.
bool bool
RecvOnOpenMetadataForRead(const Metadata& aMetadata) override RecvOnOpenMetadataForRead(const Metadata& aMetadata) override
{ {
@ -1378,7 +1308,7 @@ private:
} }
mState = eOpened; mState = eOpened;
File::OnOpen(); Notify(JS::AsmJSCache_Success);
return true; return true;
} }
@ -1400,7 +1330,7 @@ private:
} }
void void
Close() override final Close()
{ {
MOZ_ASSERT(mState == eOpened); MOZ_ASSERT(mState == eOpened);
@ -1408,24 +1338,42 @@ private:
NS_DispatchToMainThread(this); NS_DispatchToMainThread(this);
} }
private:
void void
Fail(JS::AsmJSCacheResult aResult) Fail(JS::AsmJSCacheResult aResult)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == eInitial || mState == eOpening); MOZ_ASSERT(mState == eInitial || mState == eOpening);
MOZ_ASSERT(aResult != JS::AsmJSCache_Success);
mState = eFinished; mState = eFinished;
File::OnFailure(aResult);
FileDescriptorHolder::Finish();
Notify(aResult);
}
void
Notify(JS::AsmJSCacheResult aResult)
{
MOZ_ASSERT(NS_IsMainThread());
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mWaiting);
mWaiting = false;
mOpened = aResult == JS::AsmJSCache_Success;
mResult = aResult;
mCondVar.Notify();
} }
nsIPrincipal* const mPrincipal; nsIPrincipal* const mPrincipal;
nsAutoPtr<PrincipalInfo> mPrincipalInfo; nsAutoPtr<PrincipalInfo> mPrincipalInfo;
const OpenMode mOpenMode;
WriteParams mWriteParams; WriteParams mWriteParams;
ReadParams mReadParams; ReadParams mReadParams;
bool mActorDestroyed; Mutex mMutex;
CondVar mCondVar;
// Couple enums and bools together
const OpenMode mOpenMode;
enum State { enum State {
eInitial, // Just created, waiting to be dispatched to the main thread eInitial, // Just created, waiting to be dispatched to the main thread
eBackgroundChildPending, // Waiting for the background child to be created eBackgroundChildPending, // Waiting for the background child to be created
@ -1435,6 +1383,11 @@ private:
eFinished // Terminal state eFinished // Terminal state
}; };
State mState; State mState;
JS::AsmJSCacheResult mResult;
bool mActorDestroyed;
bool mWaiting;
bool mOpened;
}; };
NS_IMETHODIMP NS_IMETHODIMP
@ -1487,7 +1440,15 @@ ChildRunnable::Run()
// Per FileDescriptorHolder::Finish()'s comment, call before // Per FileDescriptorHolder::Finish()'s comment, call before
// releasing the directory lock (which happens in the parent upon receipt // releasing the directory lock (which happens in the parent upon receipt
// of the Send__delete__ message). // of the Send__delete__ message).
File::OnClose(); FileDescriptorHolder::Finish();
MOZ_ASSERT(mOpened);
mOpened = false;
// Match the AddRef in BlockUntilOpen(). The main thread event loop still
// holds an outstanding ref which will keep 'this' alive until returning to
// the event loop.
Release();
if (!mActorDestroyed) { if (!mActorDestroyed) {
unused << Send__delete__(this, JS::AsmJSCache_Success); unused << Send__delete__(this, JS::AsmJSCache_Success);
@ -1559,7 +1520,7 @@ OpenFile(nsIPrincipal* aPrincipal,
OpenMode aOpenMode, OpenMode aOpenMode,
WriteParams aWriteParams, WriteParams aWriteParams,
ReadParams aReadParams, ReadParams aReadParams,
File::AutoClose* aFile) ChildRunnable::AutoClose* aChildRunnable)
{ {
MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aWriteParams.mSize == 0); MOZ_ASSERT_IF(aOpenMode == eOpenForRead, aWriteParams.mSize == 0);
MOZ_ASSERT_IF(aOpenMode == eOpenForWrite, aReadParams.mBegin == nullptr); MOZ_ASSERT_IF(aOpenMode == eOpenForWrite, aReadParams.mBegin == nullptr);
@ -1583,15 +1544,16 @@ OpenFile(nsIPrincipal* aPrincipal,
// We need to synchronously call into the parent to open the file and // We need to synchronously call into the parent to open the file and
// interact with the QuotaManager. The child can then map the file into its // interact with the QuotaManager. The child can then map the file into its
// address space to perform I/O. // address space to perform I/O.
nsRefPtr<File> file = nsRefPtr<ChildRunnable> childRunnable =
new ChildRunnable(aPrincipal, aOpenMode, aWriteParams, aReadParams); new ChildRunnable(aPrincipal, aOpenMode, aWriteParams, aReadParams);
JS::AsmJSCacheResult openResult = file->BlockUntilOpen(aFile); JS::AsmJSCacheResult openResult =
childRunnable->BlockUntilOpen(aChildRunnable);
if (openResult != JS::AsmJSCache_Success) { if (openResult != JS::AsmJSCache_Success) {
return openResult; return openResult;
} }
if (!file->MapMemory(aOpenMode)) { if (!childRunnable->MapMemory(aOpenMode)) {
return JS::AsmJSCache_InternalError; return JS::AsmJSCache_InternalError;
} }
@ -1609,7 +1571,7 @@ OpenEntryForRead(nsIPrincipal* aPrincipal,
const char16_t* aLimit, const char16_t* aLimit,
size_t* aSize, size_t* aSize,
const uint8_t** aMemory, const uint8_t** aMemory,
intptr_t* aFile) intptr_t* aHandle)
{ {
if (size_t(aLimit - aBegin) < sMinCachedModuleLength) { if (size_t(aLimit - aBegin) < sMinCachedModuleLength) {
return false; return false;
@ -1619,10 +1581,10 @@ OpenEntryForRead(nsIPrincipal* aPrincipal,
readParams.mBegin = aBegin; readParams.mBegin = aBegin;
readParams.mLimit = aLimit; readParams.mLimit = aLimit;
File::AutoClose file; ChildRunnable::AutoClose childRunnable;
WriteParams notAWrite; WriteParams notAWrite;
JS::AsmJSCacheResult openResult = JS::AsmJSCacheResult openResult =
OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &file); OpenFile(aPrincipal, eOpenForRead, notAWrite, readParams, &childRunnable);
if (openResult != JS::AsmJSCache_Success) { if (openResult != JS::AsmJSCache_Success) {
return false; return false;
} }
@ -1638,29 +1600,31 @@ OpenEntryForRead(nsIPrincipal* aPrincipal,
// in the first word. // in the first word.
// - When attempting to read a cache file, check whether the first word is // - When attempting to read a cache file, check whether the first word is
// sAsmJSCookie. // sAsmJSCookie.
if (file->FileSize() < sizeof(AsmJSCookieType) || if (childRunnable->FileSize() < sizeof(AsmJSCookieType) ||
*(AsmJSCookieType*)file->MappedMemory() != sAsmJSCookie) { *(AsmJSCookieType*)childRunnable->MappedMemory() != sAsmJSCookie) {
return false; return false;
} }
*aSize = file->FileSize() - sizeof(AsmJSCookieType); *aSize = childRunnable->FileSize() - sizeof(AsmJSCookieType);
*aMemory = (uint8_t*) file->MappedMemory() + sizeof(AsmJSCookieType); *aMemory = (uint8_t*) childRunnable->MappedMemory() + sizeof(AsmJSCookieType);
// The caller guarnatees a call to CloseEntryForRead (on success or // The caller guarnatees a call to CloseEntryForRead (on success or
// failure) at which point the file will be closed. // failure) at which point the file will be closed.
file.Forget(reinterpret_cast<File**>(aFile)); childRunnable.Forget(reinterpret_cast<ChildRunnable**>(aHandle));
return true; return true;
} }
void void
CloseEntryForRead(size_t aSize, CloseEntryForRead(size_t aSize,
const uint8_t* aMemory, const uint8_t* aMemory,
intptr_t aFile) intptr_t aHandle)
{ {
File::AutoClose file(reinterpret_cast<File*>(aFile)); ChildRunnable::AutoClose childRunnable(
reinterpret_cast<ChildRunnable*>(aHandle));
MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize()); MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == childRunnable->FileSize());
MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory()); MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) ==
childRunnable->MappedMemory());
} }
JS::AsmJSCacheResult JS::AsmJSCacheResult
@ -1670,7 +1634,7 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
const char16_t* aEnd, const char16_t* aEnd,
size_t aSize, size_t aSize,
uint8_t** aMemory, uint8_t** aMemory,
intptr_t* aFile) intptr_t* aHandle)
{ {
if (size_t(aEnd - aBegin) < sMinCachedModuleLength) { if (size_t(aEnd - aBegin) < sMinCachedModuleLength) {
return JS::AsmJSCache_ModuleTooSmall; return JS::AsmJSCache_ModuleTooSmall;
@ -1688,10 +1652,10 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
writeParams.mNumChars = aEnd - aBegin; writeParams.mNumChars = aEnd - aBegin;
writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars); writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars);
File::AutoClose file; ChildRunnable::AutoClose childRunnable;
ReadParams notARead; ReadParams notARead;
JS::AsmJSCacheResult openResult = JS::AsmJSCacheResult openResult =
OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &file); OpenFile(aPrincipal, eOpenForWrite, writeParams, notARead, &childRunnable);
if (openResult != JS::AsmJSCache_Success) { if (openResult != JS::AsmJSCache_Success) {
return openResult; return openResult;
} }
@ -1699,29 +1663,31 @@ OpenEntryForWrite(nsIPrincipal* aPrincipal,
// Strip off the AsmJSCookieType from the buffer returned to the caller, // Strip off the AsmJSCookieType from the buffer returned to the caller,
// which expects a buffer of aSize, not a buffer of sizeWithCookie starting // which expects a buffer of aSize, not a buffer of sizeWithCookie starting
// with a cookie. // with a cookie.
*aMemory = (uint8_t*) file->MappedMemory() + sizeof(AsmJSCookieType); *aMemory = (uint8_t*) childRunnable->MappedMemory() + sizeof(AsmJSCookieType);
// The caller guarnatees a call to CloseEntryForWrite (on success or // The caller guarnatees a call to CloseEntryForWrite (on success or
// failure) at which point the file will be closed // failure) at which point the file will be closed
file.Forget(reinterpret_cast<File**>(aFile)); childRunnable.Forget(reinterpret_cast<ChildRunnable**>(aHandle));
return JS::AsmJSCache_Success; return JS::AsmJSCache_Success;
} }
void void
CloseEntryForWrite(size_t aSize, CloseEntryForWrite(size_t aSize,
uint8_t* aMemory, uint8_t* aMemory,
intptr_t aFile) intptr_t aHandle)
{ {
File::AutoClose file(reinterpret_cast<File*>(aFile)); ChildRunnable::AutoClose childRunnable(
reinterpret_cast<ChildRunnable*>(aHandle));
MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == file->FileSize()); MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == childRunnable->FileSize());
MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) == file->MappedMemory()); MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) ==
childRunnable->MappedMemory());
// Flush to disk before writing the cookie (see OpenEntryForRead). // Flush to disk before writing the cookie (see OpenEntryForRead).
if (PR_SyncMemMap(file->FileDesc(), if (PR_SyncMemMap(childRunnable->FileDesc(),
file->MappedMemory(), childRunnable->MappedMemory(),
file->FileSize()) == PR_SUCCESS) { childRunnable->FileSize()) == PR_SUCCESS) {
*(AsmJSCookieType*)file->MappedMemory() = sAsmJSCookie; *(AsmJSCookieType*)childRunnable->MappedMemory() = sAsmJSCookie;
} }
} }