User:Yoric/RunLevelQueue: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
mNo edit summary
Line 1: Line 1:
= Run-level queue<br>  =
= The context<br>  =
 
<br>
 
== The context<br>  ==


Roughly, start-up of Mozilla seems to follow the general pattern:<br>  
Roughly, start-up of Mozilla seems to follow the general pattern:<br>  
Line 17: Line 13:
<br>  
<br>  


== The problem<br>  ==
= The problem<br>  =


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&nbsp;can tell, there is currently no good mechanism for offloading work to step 4.<br>  
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&nbsp;can tell, there is currently no good mechanism for offloading work to step 4.<br>  
Line 31: Line 27:
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.<br>  
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.<br>  


Option 2 depends on magic duration numbers to ensure that tasks are executed at the right moment.<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 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>  
Line 37: Line 33:
<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 at a given stage of execution. An underlying thread (possibly more, depending on the platform) is spawned early and exited with the main process, and the sole task of this thread is to receive tasks and execute them as soon as the corresponding stage of execution has been reached.<br>  
Offer a simple API, accessible both by C++ and JS, to register tasks that need to happen at a given stage of execution. An underlying thread (possibly more, depending on the platform) is spawned early and exited with the main process, and the sole task of this thread is to receive tasks and execute them as soon as the corresponding stage of execution has been reached.<br>  
Line 43: Line 39:
<br>
<br>


== The API<br>  ==
= The API<br>  =


This is an early draft. Any suggestion is appreciated.<br>  
This is an early draft. Any suggestion is appreciated.<br>  


=== C++<br>  ===
== C++<br>  ==


  /**
  /**
Line 154: Line 150:
  };
  };


=== JavaScript<br> ===
== JavaScript<br> ==
 
(tbd)<br>
 
== XPCOM<br> ==


(tbd)<br>
(tbd)<br>


=== XPCOM<br> ===
= How to use it =


(tbd)<br>
(tbd)<br>

Revision as of 08:51, 20 September 2011

The context

Roughly, start-up of Mozilla seems to follow the general pattern:

  1. Initialize low-level, platform-specific stuff, launch main loop
  2. Perform initialization tasks that need to be completed before UI is displayed
  3. Display UI
    At this stage, as far as the user is concerned, start-up is complete, although that is not strictly true.
  4. Perform initialization tasks that have been postponed to speed-up critical path (possibly in a background thread).
  5. 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

  1. spawning new threads to execute tasks;
  2. registering tasks with timers;
  3. 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 at a given stage of execution. An underlying thread (possibly more, depending on the platform) is spawned early and exited with the main process, and the sole task of this thread is to receive tasks and execute them as soon as the corresponding stage of execution has been reached.


The API

This is an early draft. Any suggestion is appreciated.

C++

/**
* Accessing individual run-levels
*/
class nsIRunLevels {
public:
 /**
  * Return a representation of the run-level currently executed.
  */
 nsITaskQueue* getCurrentRunLevel();

 /**
  * Return a representation of a run-level, current or future.
  */
 nsITaskQueue* getRunLevel(int runLevel);

/**
* Definition of the various runlevels.
*/
 const int RUNLEVEL_CURRENT        = -1;
 const int RUNLEVEL_LOW_LEVEL_INIT = 0;
 const int RUNLEVEL_PREUI_INIT     = 1;
 const int RUNLEVEL_UI_INIT        = 2;
 const int RUNLEVEL_POSTUI_INIT    = 3;
};

/**
* Implementation of a run-level/task queue.
*
* Details regarding the number of threads used by this queue is platform-dependent.
*/
class nsITaskQueue {
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)