User:Yoric/RunLevelQueue
Contents
The context
Roughly, start-up of Mozilla seems to follow the general pattern:
- Initialize low-level, platform-specific stuff, launch main loop
- Perform initialization tasks that need to be completed before UI is displayed
- Display UI
At this stage, as far as the user is concerned, start-up is complete, although that is not strictly true.
- Perform initialization tasks that have been postponed to speed-up critical path (possibly in a background thread).
- Proceed with application
The problem
We want to reduce as much as possible the duration of steps 1, 2 and 3, and to offload the work to step 4. Unfortunately, as far as I can tell, there is currently no good mechanism for offloading work to step 4.
Currently, we have a combination of
- spawning new threads to execute tasks;
- registering tasks with timers;
- registering tasks with observers.
Measures indicate that option 1 is not too costly. However, whether it actually speeds up startup remains an open question.
Option 2 depends on magic duration numbers to ensure that tasks are executed at the right moment. On slow platforms, it may actually slow down startup instead of speeding it up.
Option 3 is certainly the nicest but it increases the chances of breakage if registration takes place from either non-main thread or timer-executed callback, and makes the code more fragile wrt refactors: if the task is registered too late, it will never execute.
The idea
Offer a simple API, accessible both by C++ and JS, to register tasks that need to happen either:
- any time during a given stage of execution;
- after some other task has been completed.
Depending on the platform, management of these tasks can be implemented by a thread or more, or by the main loop itself.
The API
This is an early draft. Any suggestion is appreciated.
C++
/** * Accessing individual run-levels */ class nsRunLevels { public: /** * Return a representation of the run-level currently executed. */ nsITaskQueue* getCurrentRunLevel(); /** * The various standard runlevels, presented by chronological order. * Note that, in future versions, additional runlevels may appear. * However, order of runlevels is guaranteed to never change. */ nsITaskQueue* getRunLevelLowLevelInit(); nsITaskQueue* getRunLevelPreUIInit(); nsITaskQueue* getRunLevelUIInit(); nsITaskQueue* getRunLevelPostUIInit(); //... nsITaskQueue* getRunLevelShutdown(); nsITaskQueue* getRunLevelReboot(); }; /** * Implementation of a run-level/task queue. * * Details regarding the number of threads used by this queue is platform-dependent. */ class nsTaskQueue { public: /** * Register a task to be executed at a given run-level. * * @param task A task to executed. * @param flags * @param result An abstract value representing the task. Can be used for debugging purposes or to register another task to be executed after this one. If [nsnull], ignored. */ nsresult registerTask(nsIRunnable* task, int flags = 0, nsIQueuedTask** result = nsnull); public: /** * Definition of the various flags */ /** * If set, attempting to register a task with a queue after that queue has completed raises an error. * Otherwise, the system manages to execute the task. */ const int FLAG_DO_NOT_REGISTER_IF_QUEUE_TERMINATED = 1; /** * If set, if a task is registered after a queue has completed, this task is silently dropped. */ const int FLAG_DO_NOT_EXECUTE_IF_QUEUE_TERMINATED = 2; //... }; class nsTaskQueueControl: nsTaskQueue { public: /** * Start the queue. Execute immediately all tasks already registered. */ nsresult start(); /** * Execute all remaining tasks, then mark task as terminated, then return. */ nsresult waitForCompletion(); };
/** * Abstraction of a task */ class nsIQueuedTask { /** * Add a task that will be started once [this] is finished. * If several tasks are added with [pushTask] to [this], the order of execution * the tasks added is not specified. */ nsresult pushTask(nsIRunnable* task, nsIQueuedTask** result = nsnull); /** * Attempt to cancel a queued task and all the tasks queued with [pushTask]. * This method has no effect if execution of the queued task has started already. */ nsresult cancel(); /** * Attempt to stop a queued task and execute the tasks queued [pushTask]. * This method has no effect if execution of the queued task has started already. */ nsresult complete(); /** * Return the status of [this] task. */ int getStatus(); public: //Definition of the various status values. const int STATUS_STARTED = 1; const int STATUS_CANCELED = 2; const int STATUS_TERMINATED = 4; //... };
JavaScript
(tbd)
XPCOM
(tbd)
Where/when to use it
This is a rough list based on in-progress benchmarking by the #perf team:
- font enumeration under Mac or Windows;
- d3d initialization (bug 692121)
- synchronization by Places;
- url classifier update;
- database vacuuming;
- history expiration.
Others
(tbd)