User:Yoric/RunLevelQueue

From MozillaWiki
Jump to: navigation, search

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.


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:

Graphics-related

  • font enumeration under Mac or Windows;
  • d3d initialization (bug 692121)

Storage-related

  • synchronization by Places;
  • url classifier update;
  • database vacuuming;
  • history expiration.

Others

(tbd)