User:Yoric/RunLevelQueue
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.
Option 1 is resource-consuming and quite possibly messy. A quick measurement on my Nightly under MacOS X shows that startup spawns 38 threads, 21 of which are still alive at the end of start-up.
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.
*/
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;
//...
public://Note: The following methods may need to move to a subclass
/**
* Start the queue. Execute immediately all tasks already registered.
*/
nsresult start();
/**
* Execute all remaining tasks, then mark task as terminated, then return.
*/
nsresult complete();
};
/**
* 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)
How to use it
(tbd)