nsISupports proxies

Obsolete since Gecko 12 (Firefox 12 / Thunderbird 12 / SeaMonkey 2.9)
This feature is obsolete. Although it may still work in some browsers, its use is discouraged since it could be removed at any time. Try to avoid using it.

XPCOM proxies were a technology which allowed code to make asynchronous or synchronous XPCOM calls to a different thread on arbitrary XPCOM interfaces. This technology has been removed in Firefox 12 because it was very complex and often lead to strange deadlock conditions. It is no longer needed because JavaScript code can no longer run on arbitrary threads, and compiled code can use compiled runnable to achieve the same effect in a much simpler manner. For more information about alternatives, see Making cross-thread calls using runnables.

The following interface is now obsolete: nsIProxyObjectManager.

About XPCOM proxies

A proxy, in this context, is a stub object which enables a method of any class which is derived from nsISupports and has a typelib to be called on any in-process thread.

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 the old install.js scripts that were used in XPInstall in older Mozilla versions. They from the majority of JavaScript code, which is 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 Mozilla need similar solutions. In this document, I will try to explain how to use nsISupports Proxies.

How do I use it?

The API has changed in Mozilla 1.9, but the prose below hasn't been updated yet. See xpcom/proxy/public/nsIProxyObjectManager.idl for the updated interface and its documentation. See also .

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

NS_IMETHOD GetProxyForObject(nsIEventQueue *destQueue,
                             const nsIID & iid,
                             nsISupports *object,
                             PRInt32 proxyType,
                             void * *result);

NS_IMETHOD GetProxy(nsIEventQueue *destQueue,
                    const nsIID & cid,
                    nsISupports *aOuter,
                    const nsIID & iid,
                    PRInt32 proxyType,
                    void * *result);

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

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 haven't, or you've never heard of these things, see http://www.mozilla.org/scriptable/ .

The proxyType parameter can be either two flags: PROXY_SYNC or PROXY_ASYNC. These two flags can also be ORed with PROXY_ALWAYS.

PROXY_ALWAYS will ensure that a proxy object is always created no matter what thread you currently are on. If this flag is not specified, the Proxy Object Manager will compare the eventQ which you pass to the eventQ which is on the current thread. If they match, it will return the real non-proxied object. Most of the time you will want to set this flag.

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. As soon as the calling function or method exits, there is no guarantee about the data at the location that used to be part of that stack frame. Accessing it might even cause a crash. For example:

 myFoo->bar(&x)

 ... stack frame goes away ...

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

So, given an event queue to execute methods on, and either an 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. You may also use the defined flags which are defined in xpcom/threads/nsIEventQueueService.h#44.

There is also 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 is an optimization. This detection will only be used if you are using a proxy created with the PROXY_SYNC flag.

Example usage

nsresult rv = NS_OK;

nsCOMPtr<nsIProxyObjectManager> pIProxyObjectManager(do_GetService("@mozilla.org/xpcomproxy;1", &rv));
if(NS_FAILED(rv)) return rv;

nsCOMPtr<IFoo> pTestObj(do_CreateInstance(FOO_CONTRACTID, &rv));
if(NS_FAILED(rv)) return rv;

nsCOMPtr<IFoo> pProxy;
rv = pIProxyObjectManager->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
	IFoo::GetIID(),
	pTestObj,
	NS_PROXY_SYNC | NS_PROXY_ALWAYS,
	getter_AddRefs(pProxy));
if(NS_FAILED(rv)) return rv;

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

pProxy->Bar();
NS_RELEASE(pProxy);

Original Document Information

  • Author: Doug Turner
  • Last Updated Date: January 27, 2007
  • Copyright Information: Portions of this content are © 1998–2007 by individual mozilla.org contributors; content available under a Creative Commons license | Details.