The Mozilla
Organization
At A Glance
Feedback
Get Involved
Newsgroups
License Terms
Newsbot
Developer Docs
Roadmap
Projects
Ports
Module Owners
Hacking
Get the Source
Build It
Testing
Download
Bugzilla
Bug Writing
Tools
View Source
Tree Status
New Checkins
Submit A Bug
FAQ
Search
nsISupports Proxies
Doug Turner
14 July 1999

Introduction

First and foremost, this is a work in progress. It is only a reflection of nsISupports Proxies as of this writing. A "Proxy", in this context, is a stub object which enables a method of any class which is derived from nsISupports to be called on any in-process thread. "Proxy" itself is possibly not the correct word to use, but this I use it for historical reasons.

The main reason for nsISupports Proxies is that Javascript and UI are on a single thread. When one is busy, the other is blocked. A good example of this is XPInstall. Its installation scripts differ from the majority of Javascripts, which are small and can be quickly run. XPInstall installation scripts are sometimes very complex and can require long execution time due to unzipping or native file system actions. If XPInstall ran on the UI thread, the product would appear frozen until the script was complete. This is definitely bad. Because of this, XPInstall was moved to its own thread. Now XPInstall can do its installations while the product renders, but now XPInstall can not access UI elements such as a progress meter or a confirmation dialog. How can a separate non-UI thread act as if it was on the UI thread? Herein lays the utility of nsISupports Proxies.

I believe that other people working on Seamonkey need similar solutions. In this document, I will try to explain how to use nsISupports Proxies.

How do I use it?

From a users point of view, you need only to look at the nsIProxyObjectManager. It has two entry points:


    NS_IMETHOD GetProxyObject(nsIEventQueue *destQueue, 
                              REFNSIID aIID, 
                              nsISupports* aObj, 
                              ProxyType proxyType,
                              void** aProxyObject) = 0;
  
    NS_IMETHOD GetProxyObject(nsIEventQueue *destQueue, 
                              const nsCID &aClass, 
                              nsISupports *aDelegate, 
                              const nsIID &aIID,
                              ProxyType proxyType,
                              void** aProxyObject) = 0;

The two APIs are essentially the same. The only difference is that the first accepts a created object, and the latter will create an object for you. Note that this creation of the object will happen of the destination's event queue. For instance, if you need to not only use an object remotely, but also have it created remotely, the second API is what you need to use.

I should mention this here. The IID that you are requesting MUST be in the typelib. These means that you should have had to create and IDL for it and have generated a typelib. If you have not, or do not know what I am talking about see:

http://www.mozilla.org/scriptable/

Now, the ProxyType parameter can be either two flags: PROXY_SYNC or PROXY_ASYNC.

PROXY_SYNC acts just like a function call in that it blocks the calling thread until the the method is invoked on the destination thread. This is the normal and default case. PROXY_ASYNC, on the other hand, is a "fire and forget" method call. Calls on object created with this flag will return immediately and you will lose all return information. NS_OK will be returned to you.

WARNING about PROXY_ASYNC:

You must take very special care when using this flag. If the calling thread goes away, any function which accesses the calling stack will blow up. For example:



	myFoo->bar(&x)

		... thread goes away ...

	bar(PRInt32 *x)
    {
		*x = 0;   <-----  You will blow up here.

So, given an event queue to execute methods on, and either a nsISupports object that has been created or CID, and a flag, a new nsISupports proxy object will be returned to you. Once you have a proxy object, you may use it as if it is the "real" object. All the methods that are in the "real" object are stubbed into the proxy object. When you are finished with a proxy object, you should call NS_RELEASE on it. It will take care of freeing the "real" object as well as itself. If you have created the object yourself and then created the proxy, please note that you will have at least a refcount of 2 (one for the proxy and one for the created object which you passed into GetProxyObject, plus any other refcounts which you may have).

A point here to bring up is how do we supply the event queue to GetProxyObject? Well there are two possibilities. First, you may know which event queue that you are interested in. In this case, just simply use it. In most cases, you will want the main UI thread (aka the primordial thread). If this is the case, you can simply pass nsnull as the event queue. There is logic that will determine if the caller is on the destination thread. If this is true, I will not call via proxy, but rather invoke the method directly which will save cycles. This detection will only be used if you are using a proxy created with the PROXY_SYNC flag.

A pretty cool feature recently added is AutoProxification. When ever you call a method on a proxy object, each parameter is inspected. If it happens to be a descendent of a nsISupports, it will automatically be turned into a Proxy. This allows you to call a component on another thread and then interact with it. Any of the objects it may return will be called in a thread-safe manner.

This means that Interfaces should be designed for either public or private use. If they are public, then they probably really should have IDLs for them. If AutoProxification can not Proxy a nsISupport parameter, the method will return an error code.

Example Usage



	nsresult rv = NS_OK;

	NS_WITH_SERVICE( nsIProxyObjectManager, pIProxyObjectManager, kProxyObjectManagerCID, &rv);
	if(NS_FAILED(rv)) return rv;

	rv = pIProxyObjectManager->GetProxyObject(nsnull,                    // the primordial thread.
                                                  nsITestProxy::GetIID(),
                                                  createdTestObject,         // a created object
                                                  PROXY_SYNC,                // normal proxy object
                                                  (void**)proxyToTestObject);// our proxy

	NS_RELEASE(createdTestObject);  // we do not care about the real object anymore. ie. GetProxyObject
                                        // refcounts it.

	proxyToTestObject->Test1(x,y,z);

	NS_RELEASE(proxyToTestObject);

Copyright © 1998-2000 The Mozilla Organization.
Last modified July 16, 1999.