Difference between revisions of "RFC/TaskDependencies"
(→API) |
m (→API: fix indentation) |
||
Line 39: | Line 39: | ||
*/ | */ | ||
nsIPromise and(in uint32 count, | nsIPromise and(in uint32 count, | ||
− | + | [array, size_is(count)] in nsIPromise dependencies); | |
/** | /** | ||
Line 46: | Line 46: | ||
*/ | */ | ||
nsIPromise or(in uint32 count, | nsIPromise or(in uint32 count, | ||
− | + | [array, size_is(count)] in nsIPromise dependencies); | |
/** | /** |
Revision as of 00:44, 13 January 2013
This page details a proposal for handling dependencies between tasks in Firefox.
Contents
Problems addressed by this proposal
Firefox is progressively being refactored and extended to make is asynchronous, so as to avoid jank. However, mozilla-central currently offers little support for this model of programming.
This proposal introduces a minimal API and guideline that may be used to simplify the task of developing asynchronous tasks linked together by dependencies.
General ideas
- Provide a nsIPromise interface and a few functions to manipulate it. An instance of nsIPromise is a state for which some component may wish to wait.
- Get used to exposing nsIPromise in components so that other components can explicitly wait for them.
- JavaScript components can expose regular promises (existing module promise/core.js) instead of instances of nsIPromise
API
nsIPromise
interface nsIDependencyService: nsISupports { /** * Return a fresh promise. */ nsIPromise makePromise();
/** * Return a fresh barrier. */ nsIBarrier makeBarrier();
/** * Return a timeout, i.e. a promise that will automatically * be rejected after some delay. */ nsIPromise timeout(long milliseconds, nsIPromise origin) /** * Return a promise that is satisfied when all the arguments are satisfied. */ nsIPromise and(in uint32 count, [array, size_is(count)] in nsIPromise dependencies); /** * Return a promise that is satisfied once at least one of the * arguments is satisfied. */ nsIPromise or(in uint32 count, [array, size_is(count)] in nsIPromise dependencies); /** * Interactions between XPCOM promises and JavaScript promises */ nsIPromise import(jsval jsPromise); jsval export(nsIPromise xpcomPromise); }; interface nsIPromise: nsISupports { /** * Run a callback once the dependency is resolved */ void then(in nsIPromiseCallback onResolve, [optional] in nsIPromiseCallback onReject); /** * Resolve this dependency */ void resolve(); /** * Reject this dependency */ void reject(); }; [function] interface nsIPromiseCallback: nsISupports { void run(nsresult status); };
/** * A synchronization barrier. * * The barrier is lifted once all the promises * obtained by addBlocker are resolved. */ interface nsIBarrier: nsISupports { nsIPromise addBlocker();
/** * Register callbacks to be called once all the * promises obtained by addBlocker are resolved. */ void then(in nsIPromiseCallback onResolve, [optional] in nsIPromiseCallback onReject); };
Informal examples
Simple dependency
Component Foo can only be initialized once component Bar has reached a state "ready".
Steps:
- component Bar publishes a promise ready;
- component Foo's initialization is triggered once ready is resolved.
Dependency with timeout
Component Foo can only be initialized once component Bar has reached a state "ready". However, if Bar does not reach that state after 1 second,
Steps:
- component Bar publishes a promise ready;
- component Foo's derives a new promise using nsIPromiseService::timeout and this new promise triggers initialization of Foo.
Shutdown dependency
Component FooService can only be shutdown once all the components that use it have reached a state in which they do not require FooService anymore.
Steps:
- component FooService offers a method addBlocker which registers and returns a new nsIPromise to each caller;
- each client of FooService calls addBlocker and resolves the promise only once it is ready to release FooService;
- once FooService is willing to shutdown, it waits for all the promises registered with addBlocker until shutdown can proceed.