I am building an installer app using a XUL view and an XPCOM object
performing the installation tasks. The XUL View needs to be responsive and
display progress information while the XPCOM object performs the
installation task.
I tried a bunch of hacky javascript tricks to simulate multi-threading on
the javascript layer, none of them worked.
What I am trying to do now is implement an nsIObserver interface in the
javascript that registers as an observer for a "topic" with
nsIObserverService and the XPCOM object sends notification through
notifyObserver during the installation tasks from a worker thread.
To verify the observer framework works, I first tried this on a
single-threaded environment and it worked fine.
I next created the worker thread to perform the installation tasks in the
background. The thread creation code is as follows:
nsCOMPtr<nsIThread> installThread =
do_CreateInstance("@mozilla.org/thread;1");
nsCOMPtr<nsIRunnable> installRunnable =
do_QueryInterface(_installProcessor);
installThread->Init(installRunnable,
(PRUint32)0,
(PRThreadPriority)nsIThread::PRIORITY_NORMAL,
(PRThreadScope)nsIThread::SCOPE_GLOBAL,
(PRThreadState)nsIThread::STATE_JOINABLE);
installThread->Join();
I got an exception stack from the notifyObserver call from the worker
thread. I am assuming this is because I need to send the observer
notifications from the UI/Main thread.
Question1: How do I get the Main thread from the worker thread ?
Even after commenting out the notifyObserver calls, the XUL UI is still not
responsive. I see the following assertions on the console:
###!!! ASSERTION: nsSupportsCStringImpl not thread-safe:
'_mOwningThread.GetThread() == PR_GetCurrentThread()', file
/opt/spikesetup/src/mozilla/xpcom/ds/nsSupportsPrimitives.cpp, line 111
Break: at file
/opt/spikesetup/src/mozilla/xpcom/ds/nsSupportsPrimitives.cpp, line 111
Question 2: Is this assertion/break causing the UI not to be responsive ? I
would assume that the method call to the XPCOM object would have returned
control to the XUL view after creating the thread and the XUL view would
process redraw messages.
Question 3: Are there other ways that are recommended for performing long
running tasks in a XPCOM object and send notifications to the XUL/Javascript
?
Thanks in advance for any help.
-Sandip
I asked the same question one month ago. The response (not easy to
find) is here :
http://www.mozilla.org/projects/xpcom/Proxies.html
If you want to call a method which performs graphical works but you are
not in the graphical thread, you have to create a proxy from your
object and call the method on this proxy instead of your object.
Xavier.
For the second question, the PRThreadState needs to be set to
STATE_UNJOINABLE if you wish to have the main thread return control
after starting the worker thread.
So for reference here are some code snippets if you wish to perform
long running tasks in a background thread that sends progress
notifications to the UI thread.
// MyComp.cpp
// Code to start the worker thread
// Create a class that implements the nsIRunnable interface
// pass a pointer to my MyComp's Interface so that the worker thread
can invoke a
// notification method on it.
NS_IMETHODIMP MyComp::StartLongTask(....)
....
nsCOMPtr<IMyComp> myIComp = do_QueryInterface(this);
MyRunnable* myRunnable = new MyRunnable(myIComp);
nsCOMPtr<nsIRunnable> my_nsIRunnable = do_QueryInterface(myRunnable);
// create the worker thread:
nsCOMPtr<nsIThread> workerThread =
do_CreateInstance("@mozilla.org/thread;1");
workerThread->Init(my_nsIRunnable,
(PRUint32)0,
(PRThreadPriority)nsIThread::PRIORITY_NORMAL,
(PRThreadScope)nsIThread::SCOPE_GLOBAL,
(PRThreadState)nsIThread::STATE_UNJOINABLE);
workerThread->Join();
....
// notifyObserver method that will broadcast the registered topic to
all listeners
// This method must be a method defined in the IDL of IMyComp
NS_IMETHODIMP MyComp::NotifyObservers(const char *topic, const char
*data) {
PRUnichar* pruData = NULL;
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (data != NULL) {
// convert const char* to PRUnichar*
pruData = ....
}
observerService->NotifyObservers (NULL, topic, pruData);
return NS_OK;
}
// MyRunnable.cpp
NS_IMETHODIMP InstallProcessor::Run() {
// do something
notifyObservers(topic, data);
// do more things
notifyObservers(topic, data);
}
void MyRunnable::notifyObservers(const char* topic, const char* data) {
nsresult rv = NS_OK;
nsCOMPtr<nsIProxyObjectManager> pIProxyObjectManager =
do_GetService("@mozilla.org/xpcomproxy;1");
nsCOMPtr<IMyComp> proxyMyComp;
rv = pIProxyObjectManager->GetProxyForObject( NS_UI_THREAD_EVENTQ,
IMyComp::GetIID(),
_myComp, // the
MyComp pointer passed to constructor
PROXY_SYNC |
PROXY_ALWAYS,
getter_AddRefs(proxyMyComp));
if (proxyMyComp) {
proxyMyComp->NotifyObservers(topic, data);
}
}
Finally in the JS
// implement the observer interface. This is very easy in JS
var myObserver = { observe : function(subject, topic, data) {
if (topic == "myTopic") {
// update UI with progress
info
}
}
};
// register the observer
var observerService =
Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
observerService.addObserver(myObserver, "myTopic", false);
// start the long running task
myComp.startLongTask(...);
-Sandip
Err, that is quite wrong. It looks like you misunderstood the purpose of
the Join() function. The entire point of it is to block the current
thread until the other thread is finished. Therefore, it makes little
sense to call it immediately after thread creation.
The reason it works now is that you can't join an unjoinable thread, so
the call does nothing, and probably returns an error.
Note that you do have to call Join() at some point if you create a
joinable thread, otherwise you leak memory. But you should do it "later"
(when exactly depends on what the thread does...)
-christian
I'd file a bug on improving the documentation for nsIThread, but the
interface had major changes on trunk, so that'd be pointless. AFAIK most
nsIThread functions (in Gecko 1.8) map pretty directly to the NSPR
functions, so
http://www.mozilla.org/projects/nspr/reference/html/prthrd.html#15091
might be a useful reference.
> So what I want to do is create
> a background thread from the UI thread and start it and let it run to
> completion without joining or interrupting it. In that case do I just set
> the PRThreadState to STATE_UNJOINABLE and not call the Join ?
Yeah, that seems like the best solution.
-Sandip
On 6/7/06, Christian Biesinger <cbies...@web.de> wrote:
>
> sgh...@gmail.com wrote:
> > For the second question, the PRThreadState needs to be set to
> > STATE_UNJOINABLE if you wish to have the main thread return control
> > after starting the worker thread.
>