Bug 1665196 - Use the main-thread idle queue rather than a repeating timer to finalize font loading. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D92302
This commit is contained in:
Jonathan Kew 2020-10-03 08:42:43 +00:00
Родитель 0b7d12d042
Коммит 1101dcaf73
5 изменённых файлов: 74 добавлений и 87 удалений

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

@ -128,9 +128,7 @@ gfxFontInfoLoader::ShutdownObserver::Observe(nsISupports* aSubject,
return NS_OK; return NS_OK;
} }
void gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) { void gfxFontInfoLoader::StartLoader(uint32_t aDelay) {
mInterval = aInterval;
NS_ASSERTION(!mFontInfo, "fontinfo should be null when starting font loader"); NS_ASSERTION(!mFontInfo, "fontinfo should be null when starting font loader");
// sanity check // sanity check
@ -183,6 +181,12 @@ void gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) {
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return; return;
} }
PRThread* prThread;
if (NS_SUCCEEDED(mFontLoaderThread->GetPRThread(&prThread))) {
PR_SetThreadPriority(prThread, PR_PRIORITY_LOW);
}
mState = stateAsyncLoad; mState = stateAsyncLoad;
nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo); nsCOMPtr<nsIRunnable> loadEvent = new AsyncFontInfoLoader(mFontInfo);
@ -195,6 +199,32 @@ void gfxFontInfoLoader::StartLoader(uint32_t aDelay, uint32_t aInterval) {
} }
} }
class FinalizeLoaderRunnable : public Runnable {
virtual ~FinalizeLoaderRunnable() = default;
public:
NS_INLINE_DECL_REFCOUNTING_INHERITED(FinalizeLoaderRunnable, Runnable)
explicit FinalizeLoaderRunnable(gfxFontInfoLoader* aLoader)
: mozilla::Runnable("FinalizeLoaderRunnable"), mLoader(aLoader) {}
NS_IMETHOD Run() override {
nsresult rv;
if (mLoader->LoadFontInfo()) {
mLoader->CancelLoader();
rv = NS_OK;
} else {
nsCOMPtr<nsIRunnable> runnable = this;
rv = NS_DispatchToCurrentThreadQueue(
runnable.forget(), PR_INTERVAL_NO_TIMEOUT, EventQueuePriority::Idle);
}
return rv;
}
private:
gfxFontInfoLoader* mLoader;
};
void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) { void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) {
// Avoid loading data if loader has already been canceled. // Avoid loading data if loader has already been canceled.
// This should mean that CancelLoader() ran and the Load // This should mean that CancelLoader() ran and the Load
@ -207,24 +237,13 @@ void gfxFontInfoLoader::FinalizeLoader(FontInfoData* aFontInfo) {
mLoadTime = mFontInfo->mLoadTime; mLoadTime = mFontInfo->mLoadTime;
// try to load all font data immediately MOZ_ASSERT(NS_IsMainThread());
if (LoadFontInfo()) { nsCOMPtr<nsIRunnable> runnable = new FinalizeLoaderRunnable(this);
CancelLoader(); if (NS_FAILED(NS_DispatchToCurrentThreadQueue(runnable.forget(),
return; PR_INTERVAL_NO_TIMEOUT,
EventQueuePriority::Idle))) {
NS_WARNING("Failed to finalize async font info");
} }
NS_ASSERTION(!sFontLoaderShutdownObserved,
"Bug 1508626 - Finalize with interval timer for font loader "
"after shutdown observed");
NS_ASSERTION(!gXPCOMThreadsShutDown,
"Bug 1508626 - Finalize with interval timer for font loader "
"after shutdown but before observer");
// not all work completed ==> run load on interval
mState = stateTimerOnInterval;
mTimer->InitWithNamedFuncCallback(LoadFontInfoCallback, this, mInterval,
nsITimer::TYPE_REPEATING_SLACK,
"gfxFontInfoLoader::FinalizeLoader");
} }
void gfxFontInfoLoader::CancelLoader() { void gfxFontInfoLoader::CancelLoader() {
@ -246,25 +265,6 @@ void gfxFontInfoLoader::CancelLoader() {
CleanupLoader(); CleanupLoader();
} }
void gfxFontInfoLoader::LoadFontInfoTimerFire() {
if (mState == stateTimerOnDelay) {
NS_ASSERTION(!sFontLoaderShutdownObserved,
"Bug 1508626 - Setting interval timer for font loader after "
"shutdown observed");
NS_ASSERTION(!gXPCOMThreadsShutDown,
"Bug 1508626 - Setting interval timer for font loader after "
"shutdown but before observer");
mState = stateTimerOnInterval;
mTimer->SetDelay(mInterval);
}
bool done = LoadFontInfo();
if (done) {
CancelLoader();
}
}
gfxFontInfoLoader::~gfxFontInfoLoader() { gfxFontInfoLoader::~gfxFontInfoLoader() {
RemoveShutdownObserver(); RemoveShutdownObserver();
MOZ_COUNT_DTOR(gfxFontInfoLoader); MOZ_COUNT_DTOR(gfxFontInfoLoader);

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

@ -125,46 +125,43 @@ class FontInfoData {
// (e.g. localized names, face names, cmaps) are loaded async. // (e.g. localized names, face names, cmaps) are loaded async.
// helper class for loading in font info on a separate async thread // helper class for loading in font info on a separate async thread
// once async thread completes, completion process is run on regular // once async thread completes, completion process is run on the main
// intervals to prevent tying up the main thread // thread's idle queue in short slices
class gfxFontInfoLoader { class gfxFontInfoLoader {
public: public:
// state transitions: // state transitions:
// initial ---StartLoader with delay---> timer on delay // initial ---StartLoader with delay---> timer on delay
// initial ---StartLoader without delay---> timer on interval // initial ---StartLoader without delay---> timer off
// timer on delay ---LoaderTimerFire---> timer on interval // timer on delay ---LoaderTimerFire---> timer off
// timer on delay ---CancelLoader---> timer off // timer on delay ---CancelLoader---> timer off
// timer on interval ---CancelLoader---> timer off
// timer off ---StartLoader with delay---> timer on delay // timer off ---StartLoader with delay---> timer on delay
// timer off ---StartLoader without delay---> timer on interval // timer off ---StartLoader without delay---> timer off
typedef enum { typedef enum {
stateInitial, stateInitial,
stateTimerOnDelay, stateTimerOnDelay,
stateAsyncLoad, stateAsyncLoad,
stateTimerOnInterval,
stateTimerOff stateTimerOff
} TimerState; } TimerState;
gfxFontInfoLoader() : mInterval(0), mState(stateInitial) { gfxFontInfoLoader() : mState(stateInitial) {
MOZ_COUNT_CTOR(gfxFontInfoLoader); MOZ_COUNT_CTOR(gfxFontInfoLoader);
} }
virtual ~gfxFontInfoLoader(); virtual ~gfxFontInfoLoader();
// start timer with an initial delay, then call Run method at regular // start timer with an initial delay
// intervals void StartLoader(uint32_t aDelay);
void StartLoader(uint32_t aDelay, uint32_t aInterval);
// Finalize - async load complete, transfer data (on intervals if necessary) // Finalize - async load complete, transfer data (on idle)
virtual void FinalizeLoader(FontInfoData* aFontInfo); virtual void FinalizeLoader(FontInfoData* aFontInfo);
// cancel the timer and cleanup // cancel the timer and cleanup
void CancelLoader(); void CancelLoader();
uint32_t GetInterval() { return mInterval; }
protected: protected:
friend class FinalizeLoaderRunnable;
class ShutdownObserver : public nsIObserver { class ShutdownObserver : public nsIObserver {
public: public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
@ -194,15 +191,9 @@ class gfxFontInfoLoader {
// Cleanup - finish and cleanup after done, including possible reflows // Cleanup - finish and cleanup after done, including possible reflows
virtual void CleanupLoader() { mFontInfo = nullptr; } virtual void CleanupLoader() { mFontInfo = nullptr; }
// Timer interval callbacks
static void LoadFontInfoCallback(nsITimer* aTimer, void* aThis) {
gfxFontInfoLoader* loader = static_cast<gfxFontInfoLoader*>(aThis);
loader->LoadFontInfoTimerFire();
}
static void DelayedStartCallback(nsITimer* aTimer, void* aThis) { static void DelayedStartCallback(nsITimer* aTimer, void* aThis) {
gfxFontInfoLoader* loader = static_cast<gfxFontInfoLoader*>(aThis); gfxFontInfoLoader* loader = static_cast<gfxFontInfoLoader*>(aThis);
loader->StartLoader(0, loader->GetInterval()); loader->StartLoader(0);
} }
void LoadFontInfoTimerFire(); void LoadFontInfoTimerFire();
@ -213,7 +204,6 @@ class gfxFontInfoLoader {
nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsIObserver> mObserver; nsCOMPtr<nsIObserver> mObserver;
nsCOMPtr<nsIThread> mFontLoaderThread; nsCOMPtr<nsIThread> mFontLoaderThread;
uint32_t mInterval;
TimerState mState; TimerState mState;
// after async font loader completes, data is stored here // after async font loader completes, data is stored here

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

@ -127,10 +127,6 @@ const gfxFontEntry::ScriptRange gfxPlatformFontList::sComplexScriptRanges[] = {
{0, 0, 0, {0, 0, 0}} // terminator {0, 0, 0, {0, 0, 0}} // terminator
}; };
// prefs for the font info loader
#define FONT_LOADER_DELAY_PREF "gfx.font_loader.delay"
#define FONT_LOADER_INTERVAL_PREF "gfx.font_loader.interval"
static const char* kObservedPrefs[] = {"font.", static const char* kObservedPrefs[] = {"font.",
"font.name-list.", "font.name-list.",
"intl.accept_languages", // hmmmm... "intl.accept_languages", // hmmmm...
@ -638,7 +634,7 @@ void gfxPlatformFontList::InitOtherFamilyNames(
// (This is used so we can reliably run reftests that depend on localized // (This is used so we can reliably run reftests that depend on localized
// font-family names being available.) // font-family names being available.)
if (aDeferOtherFamilyNamesLoading && if (aDeferOtherFamilyNamesLoading &&
Preferences::GetUint(FONT_LOADER_DELAY_PREF) > 0) { StaticPrefs::gfx_font_loader_delay_AtStartup() > 0) {
if (!mPendingOtherFamilyNameTask) { if (!mPendingOtherFamilyNameTask) {
RefPtr<mozilla::CancelableRunnable> task = RefPtr<mozilla::CancelableRunnable> task =
new InitOtherFamilyNamesRunnable(); new InitOtherFamilyNamesRunnable();
@ -2104,7 +2100,7 @@ void gfxPlatformFontList::InitLoader() {
} }
#define FONT_LOADER_MAX_TIMESLICE \ #define FONT_LOADER_MAX_TIMESLICE \
100 // max time for one pass through RunLoader = 100ms 20 // max time for one pass through RunLoader = 20ms
bool gfxPlatformFontList::LoadFontInfo() { bool gfxPlatformFontList::LoadFontInfo() {
TimeStamp start = TimeStamp::Now(); TimeStamp start = TimeStamp::Now();
@ -2142,12 +2138,16 @@ bool gfxPlatformFontList::LoadFontInfo() {
} }
} }
// limit the time spent reading fonts in one pass // Limit the time spent reading fonts in one pass, unless the font-loader
TimeDuration elapsed = TimeStamp::Now() - start; // delay was set to zero, in which case we run to completion even if it
if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE && // causes some jank.
i + 1 != endIndex) { if (StaticPrefs::gfx_font_loader_delay_AtStartup() > 0) {
endIndex = i + 1; TimeDuration elapsed = TimeStamp::Now() - start;
break; if (elapsed.ToMilliseconds() > FONT_LOADER_MAX_TIMESLICE &&
i + 1 != endIndex) {
endIndex = i + 1;
break;
}
} }
} }
@ -2216,11 +2216,8 @@ void gfxPlatformFontList::CleanupLoader() {
} }
void gfxPlatformFontList::GetPrefsAndStartLoader() { void gfxPlatformFontList::GetPrefsAndStartLoader() {
uint32_t delay = std::max(1u, Preferences::GetUint(FONT_LOADER_DELAY_PREF)); uint32_t delay = std::max(1u, StaticPrefs::gfx_font_loader_delay_AtStartup());
uint32_t interval = StartLoader(delay);
std::max(1u, Preferences::GetUint(FONT_LOADER_INTERVAL_PREF));
StartLoader(delay, interval);
} }
void gfxPlatformFontList::ForceGlobalReflow() { void gfxPlatformFontList::ForceGlobalReflow() {

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

@ -4199,6 +4199,15 @@
value: @IS_NIGHTLY_BUILD@ value: @IS_NIGHTLY_BUILD@
mirror: once mirror: once
- name: gfx.font_loader.delay
type: uint32_t
#if defined(XP_WIN)
value: 60000
#else
value: 8000
#endif
mirror: once
# Disable antialiasing of Ahem, for use in tests. # Disable antialiasing of Ahem, for use in tests.
- name: gfx.font_rendering.ahem_antialias_none - name: gfx.font_rendering.ahem_antialias_none
type: RelaxedAtomicBool type: RelaxedAtomicBool

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

@ -612,15 +612,6 @@ pref("gfx.downloadable_fonts.disable_cache", false);
// whether to try and do something about it (e.g. download additional fonts)? // whether to try and do something about it (e.g. download additional fonts)?
pref("gfx.missing_fonts.notify", false); pref("gfx.missing_fonts.notify", false);
// prefs controlling the font (name/cmap) loader that runs shortly after startup
#ifdef XP_WIN
pref("gfx.font_loader.delay", 120000); // 2 minutes after startup
pref("gfx.font_loader.interval", 1000); // every 1 second until complete
#else
pref("gfx.font_loader.delay", 8000); // 8 secs after startup
pref("gfx.font_loader.interval", 50); // run every 50 ms
#endif
// whether to always search all font cmaps during system font fallback // whether to always search all font cmaps during system font fallback
pref("gfx.font_rendering.fallback.always_use_cmaps", false); pref("gfx.font_rendering.fallback.always_use_cmaps", false);