Pour créer un thread (ou "processus léger"), on peut utiliser le composant @mozilla.org/thread;1, qui permet d'exécuter dans un nouveau thread la méthode Run d'un composant implémentant l'interface nsIRunnable.
Voir ThreadJavascript pour un exemple en javascript.
Si P est un proxy créé dans/pour le thread T à partir de l'objet O, P permet d'invoquer toutes les méthodes de O, et garantit qu'elles seront exécutées dans le thread T.
Le premier usage habituel des proxies est de permettre d'appeler les methodes d'un objet O a partir du thread T (typiquement, le thread qui s'occupe de l'interface utilisateur) sans paralyser le thread T. Pour ce faire, la procedure consiste a
La technique fonctionne aussi bien en JavaScript qu'en C++. Dans l'exemple suivant, la constante speciale NS_CURRENT_EVENTQ sert a employer la file d'evenements du thread courant. Ce n'est generalement pas ce que vous desirez.
Nous décrirons ici comment appeler les méthodes d'un objet XPCOM à partir d'un thread différent du thread principal. On entend par thread principal d'une application mozilla, le thread lié à la gestion des évènements de l'interface utilisateur.
Pour effectuer sans problèmes des appels méthodes dans un environnement multithread, il faut "préparer" l'objet dont on veut appeler les méthodes, c'est ce que fait la classe "@mozilla.org/xpcomproxy;1" qui implémente la méthode GetProxyForObject via l'interface nsIProxyObjectManager :
nsresult NS_GetProxyForObject( nsIEventQueue *destQueue,
REFNSIID aIID,
nsISupprts* aObj,
PRInt32 proxyType,
void** aProxyObject)
{
// Récupère le proxy manager sous forme de service
nsresult rv;
nsComPtr<nsIPproxyObjectManager> proxyObjMgr =
do_GetService("@mozilla.org/xpcomproxy;1",&rv);
if(NS_FAILED(rv))
return rv;
return proxyObjMngr->GetProxyForObject(destQueue,aIID,aObj,proxyType,aProxyObject);
}
Remarque : Bien que possédant les mêmes méthodes, l'objet renvoyé est différent de l'objet initial passé en paramètre.
Les paramètres interessant sont:
Dans l'exemple suivant on prépare un objet "aFoobar" qui implémente l'interface nsIFoobar pour être appelé de manière asynchrone (non bloquante) dans un environnement multithread:
//aFoobar est un objet inaccessible dans un thread
//On va cloner cet objet pour que plus tard il soit accessible
//de n'import quel thread.
nsComPtr<nsIFoobar> foobarProxy;
nsresult rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
NS_GETIID(nsIFoobar),
aFoobar,
PROXY_ASYNC|PROXY_ALWAYS,
getter_AddRefs(foobarProxy));
//Donc dans un thread, si on veut manipuler aFoobar, on utilisera
//plutot foobarProxy. On pourra considérer foobarProxy comme
//la meme instance que aFoobar, mais accessible de n'importe
//quel thread.
Voir aussi http://www.mozilla.org/projects/xpcom/Pr(..)
Créer un composant nsIRunnable (on l'appelera par la suite runnable), en C++ si vous voulez communiquer entre thread, en particulier si vous voulez modifier le document de la fenêtre ou d'autres objets du thread principal.
Une chose qu'il est recommandé de faire : votre objet runnable devra implementer une autre interface, définissant des propriétés, ou une méthode init avec les arguments qu'il vous faut. Ces propriétés ou arguments seront en fait les objets du thread principal auquel vous voulez acceder à partir de votre nouveau thread. Ex :
interface nsIMyInterface : nsISupports
{
attribute nsIDOMDocument mainDoc;
}
ce qui donne en C++ :
class myRunnable : public nsIMyInterface, nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIMYINTERFACE
myRunnable();
private:
~myRunnable();
protected:
nsCOMPtr<nsIDOMDocument> mDocument;
nsCOMPtr<nsIDOMDocument> mDocumentProxy;
};
Vous aurez donc les methodes
NS_IMETHODIMP myRunnable::Run(){...}
NS_IMETHODIMP myRunnable::GetMainDoc(nsIDOMDocument ** aMainDoc){...}
NS_IMETHODIMP myRunnable::SetMainDoc(nsIDOMDocument * aMainDoc){...}
Dans le setter SetMainDoc, il vous faudra "proxyfier" l'objet aMaindoc si vous voulez l'utiliser dans la méthode Run (donc dans votre nouveau thread).
Ça donne à peu prés ça :
NS_IMETHODIMP myRunnable::SetMainDoc(nsIDOMDocument * aMainDoc){
nsresult rv;
mDocument = aMainDoc; // pour le getter
rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
nsIDOMDocument::GetIID(),
aMainDoc,
PROXY_SYNC|PROXY_ALWAYS,
getter_AddRefs(mDocumentProxy));
return rv;
}
Dans votre méthode run, vous pourrez ensuite utiliser mDocumentProxy pour accéder au document de votre fenêtre.
Une autre idée est aussi d'utiliser des observateurs (voir RessourcesLibs/Observers pour voir comment utiliser les observateurs), ou autre objet de type "service".
Dans ce cas, comme ce sont des services, il n'y a pas besoin de les passer en paramètre à votre composant runnable, donc pas besoin de créer une nouvelle interface comme précédement. Tout se fera dans le constructeur de votre objet myRunnable : vous récupérez le ou les objets service en questions, et vous les "proxifiez". Exemple avec le service d'observation :
class myRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
myRunnable();
private:
~myRunnable();
protected:
nsCOMPtr<nsIObserverService> mObserverServiceProxy;
};
myRunnable::myRunnable()
{
nsresult rv;
// récupération du service
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
// proxy sur le service dans mObserverServiceProxy
rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
nsIObserverService::GetIID(),
observerService,
PROXY_SYNC|PROXY_ALWAYS,
getter_AddRefs(mObserverServiceProxy));
}
Ensuite, dans la méthode run(), vous pouvez appeler l'observateur pour notifier de certains évènements :
mObserverServiceProxy->NotifyObservers(nsnull, "hello", nsnull);
Du coté de votre thread principal, il vous faut bien sûr avoir ajouter un observateur qui réponde à cet évènement "hello" (ledit observateur pouvant alors modifier la fenètre, faire d'autres traitement etc..)
L'avantage d'utiliser des observateurs est que vous pouvez ajouter la génération/gestion d'évènement sans avoir à modifier l'interface de votre composant, contrairement à la méthode précédente.
Bien sûr, on peut tout à fait combiner les deux méthodes.
La fonction suivante retourne un objet proxy pour l'objet xpcom aObject passé en paramètre:
function getProxyOnUIThread(aObject, aInterface, aSync )
{
if(aObject)
{
const nsIProxyObjectManager = Components.interfaces.nsIProxyObjectManager;
const nsIEventQueueService = Components.interfaces.nsIEventQueueService ;
var eventQSvc = Components.classes["@mozilla.org/event-queue-service;1"].
getService(nsIEventQueueService);
var uiQueue = eventQSvc.getSpecialEventQueue(
nsIEventQueueService.UI_THREAD_EVENT_QUEUE);
var proxyMgr = Components.
classes["@mozilla.org/xpcomproxy;1"].getService(nsIProxyObjectManager);
var proxyFlags = nsIProxyObjectManager.FORCE_PROXY_CREATION;
proxyFlags |= (aSync ? nsIProxyObjectManager.INVOKE_ASYNC :
nsIProxyObjectManager.INVOKE_SYNC);
return proxyMgr.getProxyForObject(uiQueue,aInterface, aObject, proxyFlags );
}
return null;
}
Copyright © 2003-2013 association xulfr, 2013-2016 Laurent Jouanneau - Informations légales.
Mozilla® est une marque déposée de la fondation Mozilla.
Mozilla.org™, Firefox™, Thunderbird™, Mozilla Suite™ et XUL™
sont des marques de la fondation Mozilla.