nsISupports proxies

  • Revision slug: nsISupports_proxies
  • Revision title: nsISupports proxies
  • Revision id: 58598
  • Created:
  • Creator: Jorend
  • Is current revision? No
  • Comment [[nsISupports Proxies]] moved to [[nsISupports proxies]]: capitalization fix

Revision Content

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 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 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 of 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 currect 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. 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. You may also use the defined flags which are here.

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;

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

	rv = pIProxyObjectManager->GetProxyForObject( NS_UI_THREAD_EVENTQ,
                                                  nsITestProxy::GetIID(),
                                                  createdTestObject,
                                                  PROXY_SYNC | PROXY_ALWAYS,
                                                  (void**)proxyToTestObject);

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

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

	NS_RELEASE(proxyToTestObject);


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.

Revision Source

<p>A <b>proxy</b>, in this context, is a stub object which enables a method of any class which is derived from <code>nsISupports</code> and has a typelib to be called on any in-process thread.
</p><p>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.
</p><p>I believe that other people working on Seamonkey need similar solutions. In this document, I will try to explain how to use nsISupports Proxies.
</p>
<h3 name="How_do_I_use_it.3F"> How do I use it? </h3>
<p>From a users point of view, you need only to look at the nsIProxyObjectManager. It has two entry points:
</p><p><br> 
</p>
<pre class="eval">    NS_IMETHOD GetProxyForObject( nsIEventQueue *destQueue,
                                  const nsIID &amp; iid,
                                  nsISupports *object,
                                  PRInt32 proxyType,
                                  void * *result);

    NS_IMETHOD GetProxy( nsIEventQueue *destQueue,
                         const nsIID &amp; cid,
                         nsISupports *aOuter,
                         const nsIID &amp; iid,
                         PRInt32 proxyType,
                         void * *result);
</pre>
<p>The two APIs are essentially the same. The only difference is that the first accepts a created object <code>object</code>, and the latter will create an object for you. This creation 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, use the second API.
</p><p>The IID that you are requesting <i>must</i> 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/ .
</p><p>The ProxyType parameter can be either two flags: <code>PROXY_SYNC</code> or <code>PROXY_ASYNC</code>. These two flags can also be ORed with <code>PROXY_ALWAYS</code>.
</p><p><code>PROXY_ALWAYS</code> 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 currect thread. If they match, it will return the real <i>non-proxied</i> object. Most of the time you will want to set this flag.
</p><p><code>PROXY_SYNC</code> 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.
</p><p><code>PROXY_ASYNC</code>, 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.
</p>
<div class="warning"><b>Warning</b> about <code>PROXY_ASYNC</code>:
<p>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:
</p>
<pre class="eval"> myFoo-&gt;bar(&amp;x)
 
 ... thread goes away ...
 
 bar(PRInt32 *x)
 {
     ...
     *x = 0;   &lt;-----  You will blow up here.
 }
</pre>
</div>
<p>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).
</p><p>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 <a class="external" href="http://lxr.mozilla.org/seamonkey/source/xpcom/threads/nsIEventQueueService.h#44">here</a>.
</p><p>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 <b>PROXY_SYNC</b> flag.
</p><p><br>
</p>
<h4 name="Example_Usage"> Example Usage </h4>
<pre class="eval">	nsresult rv = NS_OK;

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

	rv = pIProxyObjectManager-&gt;GetProxyForObject( NS_UI_THREAD_EVENTQ,
                                                  nsITestProxy::GetIID(),
                                                  createdTestObject,
                                                  PROXY_SYNC | PROXY_ALWAYS,
                                                  (void**)proxyToTestObject);

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

	proxyToTestObject-&gt;Test1(x,y,z);

	NS_RELEASE(proxyToTestObject);
</pre>
<p><br>
</p>
<div class="originaldocinfo">
<h2 name="Original_Document_Information"> Original Document Information </h2>
<ul><li> Author: <a class="external" href="mailto:dougt@netscape.com">Doug Turner</a>
</li><li> Last Updated Date: January 27, 2007
</li><li> Copyright Information: Portions of this content are © 1998–2007 by individual mozilla.org contributors; content available under a Creative Commons license | <a class="external" href="http://www.mozilla.org/foundation/licensing/website-content.html">Details</a>.
</li></ul>
</div>
Revert to this revision