User:Yoric/RunLevelQueue: Difference between revisions
(→C++: Replaced [getRunLevel()] by several methods.) |
No edit summary |
||
| (4 intermediate revisions by the same user not shown) | |||
| Line 25: | Line 25: | ||
<br> | <br> | ||
Measures indicate that option 1 is not too costly. However, whether it actually speeds up startup remains an open question.<br> | |||
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. <br> | 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. <br> | ||
| Line 31: | Line 31: | ||
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.<br> | 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.<br> | ||
<br> | <br> | ||
= The idea<br> = | = The idea<br> = | ||
Offer a simple API, accessible both by C++ and JS, to register tasks that need to happen either: | 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. | <br> | ||
Depending on the platform, management of these tasks can be implemented by a thread or more, or by the main loop itself. | |||
= The API<br> = | = The API<br> = | ||
| Line 54: | Line 55: | ||
class nsRunLevels { | class nsRunLevels { | ||
public: | 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(); | |||
}; | }; | ||
| Line 81: | Line 82: | ||
class nsTaskQueue { | class nsTaskQueue { | ||
public: | public: | ||
/** | |||
* Register a task to be executed at a given run-level. | |||
* | |||
* @param task A task to executed. | * @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: | 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 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 | * Abstraction of a task | ||
*/ | */ | ||
class nsIQueuedTask { | 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. | public: //Definition of the various status values. | ||
const int STATUS_STARTED = 1; | |||
const int STATUS_CANCELED = 2; | |||
const int STATUS_TERMINATED = 4; | |||
//... | |||
}; | }; | ||
== JavaScript<br> == | == JavaScript<br> == | ||
(tbd)<br> | |||
== XPCOM<br> == | |||
(tbd)<br> | |||
= Where/when to use it = | |||
This is a rough list based on in-progress benchmarking by the [irc://irc.mozilla.org/#perf #perf] team: | |||
== Graphics-related == | |||
( | *font enumeration under Mac or Windows; | ||
*d3d initialization ([https://bugzilla.mozilla.org/show_bug.cgi?id=692121|see bug 692121]) | |||
== | == Storage-related == | ||
*synchronization by Places; | |||
*url classifier update; | |||
*database vacuuming; | |||
*history expiration. | |||
= | == Others == | ||
(tbd) | (tbd) | ||
Latest revision as of 13:14, 6 October 2011
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)