Bug 1841660 - Fix potential deadlock waiting for StartupCache ThreadedPrefetch, r=jesup

In a previous change to this logic, we missed a hidden early return in
the `MMAP_FAULT_HANDLER_CATCH()` macro which can early return on both
Linux and Windows. If this is hit, we could end up blocking at various
points waiting for the StartupCache to be prefetched.

This patch changes the logic to notify to happen in a ScopeExit instead,
ensuring that it'll happen on all return paths.

In addition, there was another potential deadlock due to the
ThreadedPrefetch thread acquiring mTableLock. This was fixed by passing
in the pointers in the runnable instead. This should be OK as we are
making sure to block on ThreadedPrefetch before we clear the startup
data already.

Differential Revision: https://phabricator.services.mozilla.com/D182732
This commit is contained in:
Nika Layzell 2023-07-04 19:05:06 +00:00
Родитель acd9db7d96
Коммит a2f7e06013
2 изменённых файлов: 17 добавлений и 18 удалений

Просмотреть файл

@ -255,8 +255,9 @@ void StartupCache::StartPrefetchMemory() {
MonitorAutoLock lock(mPrefetchComplete); MonitorAutoLock lock(mPrefetchComplete);
mPrefetchInProgress = true; mPrefetchInProgress = true;
} }
NS_DispatchBackgroundTask(NewRunnableMethod( NS_DispatchBackgroundTask(NewRunnableMethod<uint8_t*, size_t>(
"StartupCache::ThreadedPrefetch", this, &StartupCache::ThreadedPrefetch)); "StartupCache::ThreadedPrefetch", this, &StartupCache::ThreadedPrefetch,
mCacheData.get<uint8_t>().get(), mCacheData.size()));
} }
/** /**
@ -706,22 +707,20 @@ void StartupCache::WaitOnPrefetch() {
} }
} }
void StartupCache::ThreadedPrefetch() { void StartupCache::ThreadedPrefetch(uint8_t* aStart, size_t aSize) {
uint8_t* buf; // Always notify of completion, even if MMAP_FAULT_HANDLER_CATCH()
size_t size; // early-returns.
{ auto notifyPrefetchComplete = MakeScopeExit([&] {
MutexAutoLock lock(mTableLock); MonitorAutoLock lock(mPrefetchComplete);
buf = mCacheData.get<uint8_t>().get(); mPrefetchInProgress = false;
size = mCacheData.size(); mPrefetchComplete.NotifyAll();
} });
// PrefetchMemory does madvise/equivalent, but doesn't access the memory // PrefetchMemory does madvise/equivalent, but doesn't access the memory
// pointed to by buf // pointed to by aStart
MMAP_FAULT_HANDLER_BEGIN_BUFFER(buf, size) MMAP_FAULT_HANDLER_BEGIN_BUFFER(aStart, aSize)
PrefetchMemory(buf, size); PrefetchMemory(aStart, aSize);
MMAP_FAULT_HANDLER_CATCH() MMAP_FAULT_HANDLER_CATCH()
MonitorAutoLock lock(mPrefetchComplete);
mPrefetchInProgress = false;
mPrefetchComplete.NotifyAll();
} }
// mTableLock must be held // mTableLock must be held

Просмотреть файл

@ -213,12 +213,12 @@ class StartupCache : public nsIMemoryReporter {
Result<Ok, nsresult> WriteToDisk() MOZ_REQUIRES(mTableLock); Result<Ok, nsresult> WriteToDisk() MOZ_REQUIRES(mTableLock);
void WaitOnPrefetch(); void WaitOnPrefetch();
void StartPrefetchMemory(); void StartPrefetchMemory() MOZ_REQUIRES(mTableLock);
static nsresult InitSingleton(); static nsresult InitSingleton();
static void WriteTimeout(nsITimer* aTimer, void* aClosure); static void WriteTimeout(nsITimer* aTimer, void* aClosure);
void MaybeWriteOffMainThread(); void MaybeWriteOffMainThread();
void ThreadedPrefetch(); void ThreadedPrefetch(uint8_t* aStart, size_t aSize);
Monitor mPrefetchComplete{"StartupCachePrefetch"}; Monitor mPrefetchComplete{"StartupCachePrefetch"};
bool mPrefetchInProgress MOZ_GUARDED_BY(mPrefetchComplete){false}; bool mPrefetchInProgress MOZ_GUARDED_BY(mPrefetchComplete){false};