この文脈において、「プロキシ」とは、nsISupports
から派生していてタイプライブラリを持つ任意のクラスのメソッドが、任意のプロセス中のスレッドにおいて呼び出しを行えるようにするスタブオブジェクトのことです。
nsISupports プロキシが必要な主な理由は、JavaScript と UI が一つのスレッド上にあることです。ひとつのスレッドがビジーの場合、他のスレッドはブロックされます。このことの良い例が XPInstall です。そのインストールスクリプトは、小さくかつ速く走る、多くの JavaScript とは異なっています。XPInstall インストールスクリプトは、しばしばとても複雑であり、また、解凍のためやネイティブなファイルシステムの動作のために、長い実行時間を要求します。もし XPInstall が UI スレッドの上で動作するのであれば、製品はスクリプトが完了するまで動作できなくなるでしょう。これは、あきらかにまずいです。このため、XPInstall は、それ自身のスレッドに移動します。現在 XPInstall は、製品が動作している時に、インストールを実行できます。しかし、現在 XPInstall はプログレスメーターや確認ダイアログのようなUI要素にアクセスできません。分離した非 UI スレッドが UI スレッド上で動作しているかのように見せるにはどうしたらよいでしょう。ここで、nsISupports
プロキシのユーティリティを使います。
Seamonkey にかかわっている他の人々も同様の解決策を望んでいると思います。このドキュメントでは、nsISupports プロキシの使い方を説明しようと思います。また、 もご参照ください。
どうやって使うか?
Mozilla 1.9 で API に変更が行われましたが、以下のコードは更新されていません。更新後のインターフェースとドキュメントは xpcom/proxy/public/nsIProxyObjectManager.idl
をご覧ください。 もご参照ください。
ユーザの視点からいうと、nsIProxyObjectManager
を調べるだけです。これには、二つの入口点があります。
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);
2 つの API は基本的に同じです。違いは、最初の方はすでに作成した object
オブジェクトを受け入れるのに対し、後の方は中でオブジェクトを作成することです。この生成は、目的のイベントキューで起きます。例えば、もしオブジェクトをリモートで使うだけでなく、リモートで作成したい場合、2 番目の API を使う必要があります。
要求した IID はタイプライブラリの中に存在しなければ なりません 。このことは、それに対する IDL とタイプライブラリを作成しておかなければならないことを意味します。もしそうしない場合や私が何を言っているのか理解できない場合は、 https://www.mozilla-japan.org/scriptable/ を見てください
proxyType
パラメータは、2 つのフラグのどちらかをとります。PROXY_SYNC
または PROXY_ASYNC
です。これら 2 つのフラグは、どちらも PROXY_ALWAYS
と OR をとることができます。
PROXY_ALWAYS
は、現在のスレッドがなんであれ、プロキシオブジェクトがいつも作られることを保証します。もしこのフラグがセットされていなければ、プロキシオブジェクトマネージャは、現在のスレッド上のイベントキューと渡されてきたイベントキューを比較します。もしこれらが一致したら、
プロキシ化されていない
、そのままのオブジェクトを返します。ほとんどの場合は、あなたはこのフラグをセットしたいでしょう。
PROXY_SYNC
は、メソッドが目的のスレッド上で実行されるまで、呼び出しているスレッドをブロックするため、単なる関数呼び出しを行っているように振舞います。これが通常でかつデフォルトの場合です。
一方、PROXY_ASYNC
は、「飛んでいって (戻るのを) 忘れてしまう」メソッド呼び出しです。このフラグで作成されたオブジェクトでの呼び出しは、直ちに復帰し、復帰情報は失われます。NS_OK が (復帰値として) 返ってきます。
PROXY_ASYNC
についての警告:
このフラグを使う際には、よく気をつける必要があります。呼び出しているスレッドが終了した時、呼び出し側のスタックにアクセスしているすべてのメソッドが失敗します。例えば:
myFoo->bar(&x) ... スレッドが終了してしまう ... bar(PRInt32 *x) { ... *x = 0; <----- ここで失敗します }
そのため、メソッドを実行するためのイベントキュー、そして作成された nsISupports
オブジェクトや CID そしてフラグが渡されて、新しい nsISupports
プロキシオブジェクトが返ってきます。一旦プロキシオブジェクトを手にすると、それをあたかも「本当の」オブジェクトであるかのように扱うことができます。「本当の」オブジェクトでのすべてのメソッドに対して、プロキシオブジェクトがスタブの役目を果たします。プロキシオブジェクトの使用が終わったら NS_RELEASE
を呼び出すべきです。これにより、自分自身と同様に「本当の」オブジェクトの解放も行います。自分でオブジェクトを作ってから、プロキシを作ったのであれば、少なくとも参照カウントが 2 でなければならないことに注意してください。(ひとつがプロキシのため、もうひとつは GetProxyObject
の呼び出しで渡して作成したオブジェクトのためです。それに、その他の参照カウントが足されます。)
ここでは、GetProxyObject
に対してどのようにイベントキューを提供するかということを取り上げます。二つの可能性があります。ひとつは、興味を持っているイベントキューを知っている場合です。この場合、単にそれを使ってください。ほとんどの場合では、メイン UI スレッド (一種の基本的なスレッド) が必要でしょう。もしその場合、イベントキューとして、単に nsnull
を渡すことができます。xpcom/threads/nsIEventQueueService.h#44
にある定義済のフラグも使うことができます。
呼び出し元が目的のスレッドにいるかどうかを決定するロジックもあります。これが真であれば、プロキシ経由では呼び出されずに、最適化のためにメソッド (最適化) を直接呼び出します。この検知は、PROXY_SYNC
フラグで作成されたプロキシを使うときだけに使われます。
使用例
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); // もう本当のオブジェクトについては、気にしない。すなわち、GetProxyObjectが // 参照カウントを行っている。 NS_RELEASE(createdTestObject); proxyToTestObject->Test1(x,y,z); NS_RELEASE(proxyToTestObject);
原文書の情報
- 著者: Doug Turner
- 最終更新日: January 27, 2007
- 著作権: Portions of this content are © 1998–2007 by individual mozilla.org contributors; content available under a Creative Commons license | 詳細