Bug 1361900: Part 10 - Replace linked lists with a single hashtable. r?erahm
MozReview-Commit-ID: 3qXnswsP6Z0
--- a/js/xpconnect/loader/ScriptCacheActors.cpp
+++ b/js/xpconnect/loader/ScriptCacheActors.cpp
@@ -26,24 +26,26 @@ ScriptCacheChild::Init(const Maybe<FileD
// done.
Send__delete__(this, AutoTArray<ScriptData, 0>());
}
}
// Finalize the script cache for the content process, and send back data about
// any scripts executed up to this point.
void
-ScriptCacheChild::Finalize(LinkedList<ScriptPreloader::CachedScript>& scripts)
+ScriptCacheChild::SendScriptsAndFinalize(ScriptPreloader::ScriptHash& scripts)
{
MOZ_ASSERT(mWantCacheData);
AutoSafeJSAPI jsapi;
+ auto matcher = ScriptPreloader::Match<ScriptPreloader::ScriptStatus::Saved>();
+
nsTArray<ScriptData> dataArray;
- for (auto script : scripts) {
+ for (auto& script : IterHash(scripts, matcher)) {
if (!script->mSize && !script->XDREncode(jsapi.cx())) {
continue;
}
auto data = dataArray.AppendElement();
data->url() = script->mURL;
data->cachePath() = script->mCachePath;
--- a/js/xpconnect/loader/ScriptCacheActors.h
+++ b/js/xpconnect/loader/ScriptCacheActors.h
@@ -44,17 +44,17 @@ class ScriptCacheChild final : public PS
public:
ScriptCacheChild() = default;
void Init(const Maybe<FileDescriptor>& cacheFile, bool wantCacheData);
protected:
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
- void Finalize(LinkedList<ScriptPreloader::CachedScript>& scripts);
+ void SendScriptsAndFinalize(ScriptPreloader::ScriptHash& scripts);
private:
bool mWantCacheData = false;
};
} // namespace loader
} // namespace mozilla
--- a/js/xpconnect/loader/ScriptPreloader-inl.h
+++ b/js/xpconnect/loader/ScriptPreloader-inl.h
@@ -203,12 +203,111 @@ private:
bool error_ = false;
public:
const Range<uint8_t>& data;
size_t cursor_ = 0;
};
+
+template <typename T> struct Matcher;
+
+// Wraps the iterator for a nsTHashTable so that it may be used as a range
+// iterator. Each iterator result acts as a smart pointer to the hash element,
+// and has a Remove() method which will remove the element from the hash.
+//
+// It also accepts an optional Matcher instance against which to filter the
+// elements which should be iterated over.
+//
+// Example:
+//
+// for (auto& elem : HashElemIter<HashType>(hash)) {
+// if (elem->IsDead()) {
+// elem.Remove();
+// }
+// }
+template <typename T>
+class HashElemIter
+{
+ using Iterator = typename T::Iterator;
+ using ElemType = typename T::UserDataType;
+
+ T& hash_;
+ Matcher<ElemType>* matcher_;
+ Maybe<Iterator> iter_;
+
+public:
+ explicit HashElemIter(T& hash, Matcher<ElemType>* matcher = nullptr)
+ : hash_(hash), matcher_(matcher)
+ {
+ iter_.emplace(Move(hash.Iter()));
+ }
+
+ class Elem
+ {
+ friend class HashElemIter<T>;
+
+ HashElemIter<T>& iter_;
+ bool done_;
+
+ Elem(HashElemIter& iter, bool done)
+ : iter_(iter), done_(done)
+ {}
+
+ Iterator& iter() { return iter_.iter_.ref(); }
+
+ public:
+ Elem& operator*() { return *this; }
+
+ ElemType get() { return done_ ? nullptr : iter().Data(); }
+
+ ElemType operator->() { return get(); }
+
+ operator ElemType() { return get(); }
+
+ void Remove() { iter().Remove(); }
+
+ Elem& operator++()
+ {
+ MOZ_ASSERT(!done_);
+
+ do {
+ iter().Next();
+ done_ = iter().Done();
+ } while (!done_ && iter_.matcher_ && !iter_.matcher_->Matches(get()));
+
+ return *this;
+ }
+
+ bool operator!=(Elem& other)
+ {
+ return done_ != other.done_ || this->get() != other.get();
+ }
+ };
+
+ Elem begin() { return Elem(*this, iter_->Done()); }
+
+ Elem end() { return Elem(*this, true); }
+};
+
+template <typename T>
+HashElemIter<T> IterHash(T& hash, Matcher<typename T::UserDataType>* matcher = nullptr)
+{
+ return HashElemIter<T>(hash, matcher);
+}
+
+template <typename T, typename F>
+bool
+Find(T&& iter, F&& match)
+{
+ for (auto& elem : iter) {
+ if (match(elem)) {
+ return true;
+ }
+ }
+ return false;
+}
+
}; // namespace loader
}; // namespace mozilla
#endif // ScriptPreloader_inl_h
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -7,16 +7,17 @@
#include "mozilla/ScriptPreloader.h"
#include "ScriptPreloader-inl.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Logging.h"
+#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "MainThreadUtils.h"
#include "nsDebug.h"
#include "nsDirectoryServiceUtils.h"
@@ -50,23 +51,23 @@ ProcessType ScriptPreloader::sProcessTyp
nsresult
ScriptPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)
{
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/saved-scripts", KIND_HEAP, UNITS_BYTES,
- SizeOfLinkedList(mSavedScripts, MallocSizeOf),
+ SizeOfHashEntries<ScriptStatus::Saved>(mScripts, MallocSizeOf),
"Memory used to hold the scripts which have been executed in this "
"session, and will be written to the startup script cache file.");
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/restored-scripts", KIND_HEAP, UNITS_BYTES,
- SizeOfLinkedList(mRestoredScripts, MallocSizeOf),
+ SizeOfHashEntries<ScriptStatus::Restored>(mScripts, MallocSizeOf),
"Memory used to hold the scripts which have been restored from the "
"startup script cache file, but have not been executed in this session.");
MOZ_COLLECT_REPORT(
"explicit/script-preloader/heap/other", KIND_HEAP, UNITS_BYTES,
ShallowHeapSizeOfIncludingThis(MallocSizeOf),
"Memory used by the script cache service itself.");
@@ -189,21 +190,17 @@ TraceOp(JSTracer* trc, void* data)
preloader->Trace(trc);
}
} // anonymous namespace
void
ScriptPreloader::Trace(JSTracer* trc)
{
- for (auto script : mSavedScripts) {
- JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
- }
-
- for (auto script : mRestoredScripts) {
+ for (auto& script : IterHash(mScripts)) {
JS::TraceEdge(trc, &script->mScript, "ScriptPreloader::CachedScript.mScript");
}
}
ScriptPreloader::ScriptPreloader()
: mMonitor("[ScriptPreloader.mMonitor]")
, mSaveMonitor("[ScriptPreloader.mSaveMonitor]")
@@ -254,50 +251,38 @@ ScriptPreloader::Cleanup()
if (mSaveThread) {
MonitorAutoLock mal(mSaveMonitor);
while (!mSaveComplete && mSaveThread) {
mal.Wait();
}
}
- mSavedScripts.clear();
- mRestoredScripts.clear();
+ mScripts.Clear();
AutoSafeJSAPI jsapi;
JS_RemoveExtraGCRootsTracer(jsapi.cx(), TraceOp, this);
UnregisterWeakMemoryReporter(this);
}
void
-ScriptPreloader::FlushScripts(LinkedList<CachedScript>& scripts)
+ScriptPreloader::FlushCache()
{
- for (auto next = scripts.getFirst(); next; ) {
- auto script = next;
- next = script->getNext();
+ MonitorAutoLock mal(mMonitor);
+ for (auto& script : IterHash(mScripts)) {
// We can only purge finished scripts here. Async scripts that are
// still being parsed off-thread have a non-refcounted reference to
// this script, which needs to stay alive until they finish parsing.
if (script->mReadyToExecute) {
script->Cancel();
- script->remove();
- delete script;
+ script.Remove();
}
}
-}
-
-void
-ScriptPreloader::FlushCache()
-{
- MonitorAutoLock mal(mMonitor);
-
- FlushScripts(mSavedScripts);
- FlushScripts(mRestoredScripts);
// If we've already finished saving the cache at this point, start a new
// delayed save operation. This will write out an empty cache file in place
// of any cache file we've already written out this session, which will
// prevent us from falling back to the current session's cache file on the
// next startup.
if (mSaveComplete && mChildCache) {
mSaveComplete = false;
@@ -325,17 +310,17 @@ ScriptPreloader::Observe(nsISupports* su
} else if (!strcmp(topic, DOC_ELEM_INSERTED_TOPIC)) {
obs->RemoveObserver(this, DOC_ELEM_INSERTED_TOPIC);
MOZ_ASSERT(XRE_IsContentProcess());
mStartupFinished = true;
if (mChildActor) {
- mChildActor->Finalize(mSavedScripts);
+ mChildActor->SendScriptsAndFinalize(mScripts);
}
} else if (!strcmp(topic, SHUTDOWN_TOPIC)) {
ForceWriteCacheFile();
} else if (!strcmp(topic, CLEANUP_TOPIC)) {
Cleanup();
} else if (!strcmp(topic, CACHE_FLUSH_TOPIC)) {
FlushCache();
}
@@ -444,17 +429,19 @@ ScriptPreloader::InitCacheInternal()
headerSize = LittleEndian::readUint32(data.get());
data += sizeof(headerSize);
if (data + headerSize > end) {
return Err(NS_ERROR_UNEXPECTED);
}
{
- AutoCleanLinkedList<CachedScript> scripts;
+ auto cleanup = MakeScopeExit([&] () {
+ mScripts.Clear();
+ });
Range<uint8_t> header(data, data + headerSize);
data += headerSize;
InputBuffer buf(header);
size_t offset = 0;
while (!buf.finished()) {
@@ -469,38 +456,36 @@ ScriptPreloader::InitCacheInternal()
// size, as a basic sanity check.
if (script->mOffset != offset) {
return Err(NS_ERROR_UNEXPECTED);
}
offset += script->mSize;
script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
- scripts.insertBack(script.release());
+ mScripts.Put(script->mCachePath, script.release());
}
if (buf.error()) {
return Err(NS_ERROR_UNEXPECTED);
}
- for (auto script : scripts) {
- mScripts.Put(script->mCachePath, script);
- }
- mRestoredScripts = Move(scripts);
+ cleanup.release();
}
AutoJSAPI jsapi;
MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope()));
JSContext* cx = jsapi.cx();
auto start = TimeStamp::Now();
LOG(Info, "Off-thread decoding scripts...\n");
JS::CompileOptions options(cx, JSVERSION_LATEST);
- for (auto script : mRestoredScripts) {
+
+ for (auto& script : IterHash(mScripts, Match<ScriptStatus::Restored>())) {
// Only async decode scripts which have been used in this process type.
if (script->mProcessTypes.contains(CurrentProcessType()) &&
script->AsyncDecodable() &&
JS::CanCompileOffThread(cx, options, script->mSize)) {
DecodeScriptOffThread(cx, script);
} else {
script->mReadyToExecute = true;
}
@@ -531,55 +516,44 @@ ScriptPreloader::PrepareCacheWrite()
mChildCache->PrepareCacheWrite();
}
});
if (mDataPrepared) {
return;
}
- if (mRestoredScripts.isEmpty()) {
- // Check for any new scripts that we need to save. If there aren't
- // any, and there aren't any saved scripts that we need to remove,
- // don't bother writing out a new cache file.
- bool found = false;
- for (auto script : mSavedScripts) {
- if (!script->HasRange() || script->HasArray()) {
- found = true;
- break;
- }
- }
- if (!found) {
- mSaveComplete = true;
- return;
- }
+ bool found = Find(IterHash(mScripts), [] (CachedScript* script) {
+ return (script->mStatus == ScriptStatus::Restored ||
+ !script->HasRange() || script->HasArray());
+ });
+
+ if (!found) {
+ mSaveComplete = true;
+ return;
}
AutoSafeJSAPI jsapi;
- for (CachedScript* next = mSavedScripts.getFirst(); next; ) {
- CachedScript* script = next;
- next = script->getNext();
-
+ for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
// Don't write any scripts that are also in the child cache. They'll be
// loaded from the child cache in that case, so there's no need to write
// them twice.
CachedScript* childScript = mChildCache ? mChildCache->mScripts.Get(script->mCachePath) : nullptr;
if (childScript) {
- if (FindScript(mChildCache->mSavedScripts, script->mCachePath)) {
+ if (childScript->mStatus == ScriptStatus::Saved) {
childScript->UpdateLoadTime(script->mLoadTime);
childScript->mProcessTypes += script->mProcessTypes;
} else {
childScript = nullptr;
}
}
if (childScript || (!script->mSize && !script->XDREncode(jsapi.cx()))) {
- script->remove();
- delete script;
+ script.Remove();
} else {
script->mSize = script->Range().length();
}
}
mDataPrepared = true;
}
@@ -624,17 +598,17 @@ ScriptPreloader::WriteCache()
if (exists) {
NS_TRY(cacheFile->Remove(false));
}
AutoFDClose fd;
NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget()));
nsTArray<CachedScript*> scripts;
- for (auto script : mSavedScripts) {
+ for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) {
scripts.AppendElement(script);
}
// Sort scripts by load time, with async loaded scripts before sync scripts.
// Since async scripts are always loaded immediately at startup, it helps to
// have them stored contiguously.
scripts.Sort(CachedScript::Comparator());
@@ -685,85 +659,58 @@ ScriptPreloader::Run()
mSaveComplete = true;
NS_ReleaseOnMainThread(mSaveThread.forget());
mal.NotifyAll();
return NS_OK;
}
-/* static */ ScriptPreloader::CachedScript*
-ScriptPreloader::FindScript(LinkedList<CachedScript>& scripts, const nsCString& cachePath)
-{
- for (auto script : scripts) {
- if (script->mCachePath == cachePath) {
- return script;
- }
- }
- return nullptr;
-}
-
void
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
JS::HandleScript jsscript)
{
// Don't bother trying to cache any URLs with cache-busting query
// parameters.
if (mStartupFinished || !mCacheInitialized || cachePath.FindChar('?') >= 0) {
return;
}
// Don't bother caching files that belong to the mochitest harness.
NS_NAMED_LITERAL_CSTRING(mochikitPrefix, "chrome://mochikit/");
if (StringHead(url, mochikitPrefix.Length()) == mochikitPrefix) {
return;
}
- CachedScript* script = mScripts.Get(cachePath);
- bool restored = script && FindScript(mRestoredScripts, cachePath);
+ auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript);
- if (restored) {
- script->remove();
- mSavedScripts.insertBack(script);
+ if (script->mStatus == ScriptStatus::Restored) {
+ script->mStatus = ScriptStatus::Saved;
MOZ_ASSERT(jsscript);
script->mScript = jsscript;
script->mReadyToExecute = true;
- } else if (!script) {
- script = new CachedScript(*this, url, cachePath, jsscript);
- mSavedScripts.insertBack(script);
- mScripts.Put(cachePath, script);
- } else {
- return;
}
script->UpdateLoadTime(TimeStamp::Now());
script->mProcessTypes += CurrentProcessType();
}
void
ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath,
ProcessType processType, nsTArray<uint8_t>&& xdrData,
TimeStamp loadTime)
{
- CachedScript* script = mScripts.Get(cachePath);
- bool restored = script && FindScript(mRestoredScripts, cachePath);
+ auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, nullptr);
- if (restored) {
- script->remove();
- mSavedScripts.insertBack(script);
+ if (script->mStatus == ScriptStatus::Restored) {
+ script->mStatus = ScriptStatus::Saved;
script->mReadyToExecute = true;
} else {
- if (!script) {
- script = new CachedScript(this, url, cachePath, nullptr);
- mSavedScripts.insertBack(script);
- mScripts.Put(cachePath, script);
- }
-
if (!script->HasRange()) {
MOZ_ASSERT(!script->HasArray());
script->mSize = xdrData.Length();
script->mXDRData.construct<nsTArray<uint8_t>>(Forward<nsTArray<uint8_t>>(xdrData));
auto& data = script->Array();
script->mXDRRange.emplace(data.Elements(), data.Length());
@@ -864,16 +811,17 @@ ScriptPreloader::OffThreadDecodeCallback
script->mToken = token;
script->mReadyToExecute = true;
mal.NotifyAll();
}
ScriptPreloader::CachedScript::CachedScript(ScriptPreloader& cache, InputBuffer& buf)
: mCache(cache)
+ , mStatus(ScriptStatus::Restored)
{
Code(buf);
}
bool
ScriptPreloader::CachedScript::XDREncode(JSContext* cx)
{
JSAutoCompartment ac(cx, mScript);
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -12,17 +12,17 @@
#include "mozilla/MemoryReporting.h"
#include "mozilla/Maybe.h"
#include "mozilla/MaybeOneOf.h"
#include "mozilla/Monitor.h"
#include "mozilla/Range.h"
#include "mozilla/Vector.h"
#include "mozilla/Result.h"
#include "mozilla/loader/AutoMemMap.h"
-#include "nsDataHashtable.h"
+#include "nsClassHashtable.h"
#include "nsIFile.h"
#include "nsIMemoryReporter.h"
#include "nsIObserver.h"
#include "nsIThread.h"
#include "jsapi.h"
#include <prio.h>
@@ -38,16 +38,22 @@ namespace loader {
class InputBuffer;
class ScriptCacheChild;
enum class ProcessType : uint8_t {
Parent,
Web,
Extension,
};
+
+ template <typename T>
+ struct Matcher
+ {
+ virtual bool Matches(T) = 0;
+ };
}
using namespace mozilla::loader;
class ScriptPreloader : public nsIObserver
, public nsIMemoryReporter
, public nsIRunnable
{
@@ -96,16 +102,21 @@ public:
}
static void InitContentChild(dom::ContentParent& parent);
protected:
virtual ~ScriptPreloader() = default;
private:
+ enum class ScriptStatus {
+ Restored,
+ Saved,
+ };
+
// Represents a cached JS script, either initially read from the script
// cache file, to be added to the next session's script cache file, or
// both.
//
// A script which was read from the cache file may be in any of the
// following states:
//
// - Read from the cache, and being compiled off thread. In this case,
@@ -120,39 +131,33 @@ private:
// file. In this case, mReadyToExecute is true, and mScript is non-null.
//
// A script to be added to the next session's cache file always has a
// non-null mScript value. If it was read from the last session's cache
// file, it also has a non-empty mXDRRange range, which will be stored in
// the next session's cache file. If it was compiled in this session, its
// mXDRRange will initially be empty, and its mXDRData buffer will be
// populated just before it is written to the cache file.
- class CachedScript : public LinkedListElement<CachedScript>
+ class CachedScript
{
public:
CachedScript(CachedScript&&) = default;
CachedScript(ScriptPreloader& cache, const nsCString& url, const nsCString& cachePath, JSScript* script)
: mCache(cache)
, mURL(url)
, mCachePath(cachePath)
+ , mStatus(ScriptStatus::Saved)
, mScript(script)
, mReadyToExecute(true)
{}
inline CachedScript(ScriptPreloader& cache, InputBuffer& buf);
- ~CachedScript()
- {
-#ifdef DEBUG
- auto hashValue = mCache->mScripts.Get(mCachePath);
- MOZ_ASSERT_IF(hashValue, hashValue == this);
-#endif
- mCache->mScripts.Remove(mCachePath);
- }
+ ~CachedScript() = default;
// For use with nsTArray::Sort.
//
// Orders scripts by:
//
// 1) Async-decoded scripts before sync-decoded scripts, since the
// former are needed immediately at startup, and should be stored
// contiguously.
@@ -171,16 +176,28 @@ private:
{
if (a->AsyncDecodable() != b->AsyncDecodable()) {
return a->AsyncDecodable();
}
return a->mLoadTime < b->mLoadTime;
}
};
+ struct StatusMatcher final : public Matcher<CachedScript*>
+ {
+ StatusMatcher(ScriptStatus status) : mStatus(status) {}
+
+ virtual bool Matches(CachedScript* script)
+ {
+ return script->mStatus == mStatus;
+ }
+
+ const ScriptStatus mStatus;
+ };
+
void Cancel();
void FreeData()
{
// If the script data isn't mmapped, we need to release both it
// and the Range that points to it at the same time.
if (!mXDRData.empty()) {
mXDRRange.reset();
@@ -269,16 +286,18 @@ private:
nsCString mCachePath;
// The offset of this script in the cache file, from the start of the XDR
// data block.
uint32_t mOffset = 0;
// The size of this script's encoded XDR data.
uint32_t mSize = 0;
+ ScriptStatus mStatus;
+
TimeStamp mLoadTime{};
JS::Heap<JSScript*> mScript;
// True if this script is ready to be executed. This means that either the
// off-thread portion of an off-thread decode has finished, or the script
// is too small to be decoded off-thread, and may be immediately decoded
// whenever it is first executed.
@@ -296,16 +315,23 @@ private:
// compiled during this session.
Maybe<JS::TranscodeRange> mXDRRange;
// XDR data which was generated from a script compiled during this
// session, and will be written to the cache file.
MaybeOneOf<JS::TranscodeBuffer, nsTArray<uint8_t>> mXDRData;
};
+ template <ScriptStatus status>
+ static Matcher<CachedScript*>* Match()
+ {
+ static CachedScript::StatusMatcher matcher{status};
+ return &matcher;
+ }
+
// There's a trade-off between the time it takes to setup an off-thread
// decode and the time we save by doing the decode off-thread. At this
// point, the setup is quite expensive, and 20K is about where we start to
// see an improvement rather than a regression.
//
// This also means that we get much better performance loading one big
// script than several small scripts, since the setup is per-script, and the
// OMT compile is almost always complete by the time we need a given script.
@@ -319,71 +345,61 @@ private:
static constexpr int MAX_MAINTHREAD_DECODE_SIZE = 50 * 1024;
ScriptPreloader();
void ForceWriteCacheFile();
void Cleanup();
void FlushCache();
- void FlushScripts(LinkedList<CachedScript>& scripts);
// Opens the cache file for reading.
Result<Ok, nsresult> OpenCache();
// Writes a new cache file to disk. Must not be called on the main thread.
Result<Ok, nsresult> WriteCache();
// Prepares scripts for writing to the cache, serializing new scripts to
// XDR, and calculating their size-based offsets.
void PrepareCacheWrite();
// Returns a file pointer for the cache file with the given name in the
// current profile.
Result<nsCOMPtr<nsIFile>, nsresult>
GetCacheFile(const nsAString& suffix);
- static CachedScript* FindScript(LinkedList<CachedScript>& scripts, const nsCString& cachePath);
-
// Waits for the given cached script to finish compiling off-thread, or
// decodes it synchronously on the main thread, as appropriate.
JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script);
// Begins decoding the given script in a background thread.
void DecodeScriptOffThread(JSContext* cx, CachedScript* script);
static void OffThreadDecodeCallback(void* token, void* context);
void CancelOffThreadParse(void* token);
size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return (mallocSizeOf(this) + mScripts.ShallowSizeOfExcludingThis(mallocSizeOf) +
mallocSizeOf(mSaveThread.get()) + mallocSizeOf(mProfD.get()));
}
- template<typename T>
- static size_t SizeOfLinkedList(LinkedList<T>& list, mozilla::MallocSizeOf mallocSizeOf)
+ using ScriptHash = nsClassHashtable<nsCStringHashKey, CachedScript>;
+
+ template<ScriptStatus status>
+ static size_t SizeOfHashEntries(ScriptHash& scripts, mozilla::MallocSizeOf mallocSizeOf)
{
size_t size = 0;
- for (auto elem : list) {
+ for (auto elem : IterHash(scripts, Match<status>())) {
size += elem->HeapSizeOfIncludingThis(mallocSizeOf);
}
return size;
}
- // The list of scripts executed during this session, and being saved for
- // potential reuse, and to be written to the next session's cache file.
- AutoCleanLinkedList<CachedScript> mSavedScripts;
-
- // The list of scripts restored from the cache file at the start of this
- // session. Scripts are removed from this list and moved to mSavedScripts
- // the first time they're used during this session.
- AutoCleanLinkedList<CachedScript> mRestoredScripts;
-
- nsDataHashtable<nsCStringHashKey, CachedScript*> mScripts;
+ ScriptHash mScripts;
// True after we've shown the first window, and are no longer adding new
// scripts to the cache.
bool mStartupFinished = false;
bool mCacheInitialized = false;
bool mSaveComplete = false;
bool mDataPrepared = false;