Back to index

lightning-sunbird  0.9+nobinonly
nsProxyEventObject.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "prprf.h"
00040 #include "prmem.h"
00041 
00042 #include "nscore.h"
00043 #include "nsProxyEvent.h"
00044 #include "nsIProxyObjectManager.h"
00045 #include "nsProxyEventPrivate.h"
00046 
00047 #include "nsIEventQueueService.h"
00048 #include "nsServiceManagerUtils.h"
00049 
00050 #include "nsHashtable.h"
00051 
00052 #include "nsIInterfaceInfoManager.h"
00053 #include "xptcall.h"
00054 
00055 #include "nsAutoLock.h"
00056 
00057 static NS_DEFINE_IID(kProxyObject_Identity_Class_IID, NS_PROXYEVENT_IDENTITY_CLASS_IID);
00058 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00059 
00061 
00062 class nsProxyEventKey : public nsHashKey
00063 {
00064 public:
00065     nsProxyEventKey(void* rootObjectKey, void* destQueueKey, PRInt32 proxyType)
00066         : mRootObjectKey(rootObjectKey), mDestQueueKey(destQueueKey), mProxyType(proxyType) {
00067     }
00068   
00069     PRUint32 HashCode(void) const {
00070         return NS_PTR_TO_INT32(mRootObjectKey) ^ 
00071             NS_PTR_TO_INT32(mDestQueueKey) ^ mProxyType;
00072     }
00073 
00074     PRBool Equals(const nsHashKey *aKey) const {
00075         const nsProxyEventKey* other = (const nsProxyEventKey*)aKey;
00076         return mRootObjectKey == other->mRootObjectKey
00077             && mDestQueueKey == other->mDestQueueKey
00078             && mProxyType == other->mProxyType;
00079     }
00080 
00081     nsHashKey *Clone() const {
00082         return new nsProxyEventKey(mRootObjectKey, mDestQueueKey, mProxyType);
00083     }
00084 
00085 protected:
00086     void*       mRootObjectKey;
00087     void*       mDestQueueKey;
00088     PRInt32     mProxyType;
00089 };
00090 
00092 
00093 #ifdef DEBUG_xpcom_proxy
00094 static PRMonitor* mon = nsnull;
00095 static PRUint32 totalProxyObjects = 0;
00096 static PRUint32 outstandingProxyObjects = 0;
00097 
00098 void
00099 nsProxyEventObject::DebugDump(const char * message, PRUint32 hashKey)
00100 {
00101 
00102     if (mon == nsnull)
00103     {
00104         mon = PR_NewMonitor();
00105     }
00106 
00107     PR_EnterMonitor(mon);
00108 
00109     if (message)
00110     {
00111         printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-\n");
00112         printf("%s\n", message);
00113 
00114         if(strcmp(message, "Create") == 0)
00115         {
00116             totalProxyObjects++;
00117             outstandingProxyObjects++;
00118         }
00119         else if(strcmp(message, "Delete") == 0)
00120         {
00121             outstandingProxyObjects--;
00122         }
00123     }
00124     printf("nsProxyEventObject @ %x with mRefCnt = %d\n", this, mRefCnt);
00125 
00126     PRBool isRoot = mRoot == nsnull;
00127     printf("%s wrapper around  @ %x\n", isRoot ? "ROOT":"non-root\n", GetRealObject());
00128 
00129     nsCOMPtr<nsISupports> rootObject = do_QueryInterface(mProxyObject->mRealObject);
00130     nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mDestQueue);
00131     nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType);
00132     printf("Hashkey: %d\n", key.HashCode());
00133         
00134     char* name;
00135     GetClass()->GetInterfaceInfo()->GetName(&name);
00136     printf("interface name is %s\n", name);
00137     if(name)
00138         nsMemory::Free(name);
00139     char * iid = GetClass()->GetProxiedIID().ToString();
00140     printf("IID number is %s\n", iid);
00141     delete iid;
00142     printf("nsProxyEventClass @ %x\n", mClass);
00143     
00144     if(mNext)
00145     {
00146         if(isRoot)
00147         {
00148             printf("Additional wrappers for this object...\n");
00149         }
00150         mNext->DebugDump(nsnull, 0);
00151     }
00152 
00153     printf("[proxyobjects] %d total used in system, %d outstading\n", totalProxyObjects, outstandingProxyObjects);
00154 
00155     if (message)
00156         printf("-=-=-=-=-=-=-=-=-=-=-=-=-\n");
00157 
00158     PR_ExitMonitor(mon);
00159 }
00160 #endif
00161 
00162 
00163 
00165 //
00166 //  nsProxyEventObject
00167 //
00169 nsProxyEventObject* 
00170 nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue,
00171                                       PRInt32 proxyType, 
00172                                       nsISupports *aObj,
00173                                       REFNSIID aIID)
00174 {
00175     nsresult rv;
00176 
00177     if (!aObj)
00178         return nsnull;
00179 
00180     nsISupports* rawObject = aObj;
00181 
00182     //
00183     // make sure that the object pass in is not a proxy...
00184     // if the object *is* a proxy, then be nice and build the proxy
00185     // for the real object...
00186     //
00187     nsCOMPtr<nsProxyEventObject> identificationObject;
00188 
00189     rv = rawObject->QueryInterface(kProxyObject_Identity_Class_IID,
00190                                    getter_AddRefs(identificationObject));
00191     if (NS_SUCCEEDED(rv)) {
00192         //
00193         // ATTENTION!!!!
00194         //
00195         // If you are hitting any of the assertions in this block of code, 
00196         // please contact dougt@netscape.com.
00197         //
00198 
00199         // if you hit this assertion, you might want to check out how 
00200         // you are using proxies.  You shouldn't need to be creating
00201         // a proxy from a proxy.  -- dougt@netscape.com
00202         NS_ASSERTION(0, "Someone is building a proxy from a proxy");
00203         
00204         NS_ASSERTION(identificationObject, "where did my identification object go!");
00205         if (!identificationObject) {
00206             return nsnull;
00207         }
00208 
00209         // someone is asking us to create a proxy for a proxy.  Lets get
00210         // the real object and build aproxy for that!
00211         rawObject = identificationObject->GetRealObject();
00212         
00213         NS_ASSERTION(rawObject, "where did my real object go!");
00214         if (!rawObject) {
00215             return nsnull;
00216         }
00217     }
00218 
00219     //
00220     // Get the root nsISupports of the |real| object.
00221     //
00222     nsCOMPtr<nsISupports> rootObject;
00223 
00224     rootObject = do_QueryInterface(rawObject, &rv);
00225     if (NS_FAILED(rv) || !rootObject) {
00226         NS_ASSERTION(NS_FAILED(rv), "where did my root object go!");
00227         return nsnull;
00228     }
00229 
00230     // Get the root nsISupports of the event queue...  This is used later,
00231     // as part of the hashtable key...
00232     nsCOMPtr<nsISupports> destQRoot = do_QueryInterface(destQueue, &rv);
00233     if (NS_FAILED(rv) || !destQRoot) {
00234         return nsnull;
00235     }
00236 
00237     //
00238     // Enter the proxy object creation lock.
00239     //
00240     // This lock protects thev linked list which chains proxies together 
00241     // (ie. mRoot and mNext) and ensures that there is no hashtable contention
00242     // between the time that a proxy is looked up (and not found) in the
00243     // hashtable and then added...
00244     //
00245     nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance();
00246     if (!manager) {
00247         return nsnull;
00248     }
00249 
00250     nsCOMPtr<nsIEventQueueService> eventQService(do_GetService(kEventQueueServiceCID, &rv));
00251     if (NS_FAILED(rv))
00252         return nsnull;
00253     
00254     nsAutoMonitor mon(manager->GetMonitor());
00255 
00256     // Get the hash table containing root proxy objects...
00257     nsHashtable *realToProxyMap = manager->GetRealObjectToProxyObjectMap();
00258     if (!realToProxyMap) {
00259         return nsnull;
00260     }
00261 
00262     // Now, lookup the root nsISupports of the raw object in the hashtable
00263     // The key consists of 3 pieces of information:
00264     //    - root nsISupports of the raw object
00265     //    - event queue of the current thread
00266     //    - type of proxy being constructed...
00267     //
00268     nsProxyEventKey rootkey(rootObject.get(), destQRoot.get(), proxyType);
00269 
00270     nsCOMPtr<nsProxyEventObject> rootProxy;
00271     nsCOMPtr<nsProxyEventObject> proxy;
00272     nsProxyEventObject* peo;
00273 
00274     // find in our hash table 
00275     rootProxy = (nsProxyEventObject*) realToProxyMap->Get(&rootkey);
00276 
00277     if(rootProxy) {
00278         //
00279         // At least one proxy has already been created for this raw object...
00280         //
00281         // Look for the specific interface proxy in the list off of
00282         // the root proxy...
00283         //
00284         peo = rootProxy->LockedFind(aIID);
00285 
00286         if(peo) {
00287             // An existing proxy is available... So use it.
00288             NS_ADDREF(peo);
00289             return peo;  
00290         }
00291     }
00292     else {
00293         // build the root proxy
00294         nsCOMPtr<nsProxyEventClass> rootClazz;
00295 
00296         rootClazz = dont_AddRef(nsProxyEventClass::GetNewOrUsedClass(
00297                                 NS_GET_IID(nsISupports)));
00298         if (!rootClazz) {
00299             return nsnull;
00300         }
00301 
00302         peo = new nsProxyEventObject(destQueue, 
00303                                      proxyType, 
00304                                      rootObject, 
00305                                      rootClazz, 
00306                                      nsnull,
00307                                      eventQService);
00308         if(!peo) {
00309             // Ouch... Out of memory!
00310             return nsnull;
00311         }
00312 
00313         // Add this root proxy into the hash table...
00314         realToProxyMap->Put(&rootkey, peo);
00315 
00316         if(aIID.Equals(NS_GET_IID(nsISupports))) {
00317             //
00318             // Since the requested proxy is for the nsISupports interface of
00319             // the raw object, use the new root proxy as the specific proxy
00320             // too...
00321             //
00322             NS_ADDREF(peo);
00323             return peo;
00324         }
00325 
00326         // This assignment is an owning reference to the new ProxyEventObject.
00327         // So, it will automatically get deleted if any subsequent early 
00328         // returns are taken...
00329         rootProxy = do_QueryInterface(peo);
00330     }
00331 
00332     //
00333     // at this point we have a proxy for the root nsISupports (ie. rootProxy)
00334     // but we need to build the specific proxy for this interface...
00335     //
00336     NS_ASSERTION(rootProxy, "What happened to the root proxy!");
00337 
00338     // Get a class for this IID.
00339     nsCOMPtr<nsProxyEventClass> proxyClazz;
00340         
00341     proxyClazz = dont_AddRef(nsProxyEventClass::GetNewOrUsedClass(aIID));
00342     if(!proxyClazz) {
00343         return nsnull;
00344     }
00345 
00346     // Get the raw interface for this IID
00347     nsCOMPtr<nsISupports> rawInterface;
00348 
00349     rv = rawObject->QueryInterface(aIID, getter_AddRefs(rawInterface));
00350     if (NS_FAILED(rv) || !rawInterface) {
00351         NS_ASSERTION(NS_FAILED(rv), "where did my rawInterface object go!");
00352         return nsnull;
00353     }
00354 
00355     peo = new nsProxyEventObject(destQueue, 
00356                                  proxyType, 
00357                                  rawInterface, 
00358                                  proxyClazz, 
00359                                  rootProxy,
00360                                  eventQService);
00361     if (!peo) {
00362         // Ouch... Out of memory!
00363         return nsnull;
00364     }
00365 
00366     //
00367     // Add the new specific proxy to the head of the list of proxies hanging
00368     // off of the rootProxy...
00369     //
00370     peo->mNext       = rootProxy->mNext;
00371     rootProxy->mNext = peo;
00372 
00373     NS_ADDREF(peo);
00374     return peo;  
00375 
00376 }
00377 
00378 nsProxyEventObject* nsProxyEventObject::LockedFind(REFNSIID aIID)
00379 {
00380     if(aIID.Equals(mClass->GetProxiedIID())) {
00381         return this;
00382     }
00383 
00384     if(aIID.Equals(NS_GET_IID(nsISupports))) {
00385         return this;
00386     }
00387 
00388     nsProxyEventObject* cur = (mRoot ? mRoot : mNext);
00389     while(cur) {
00390         if(aIID.Equals(cur->GetClass()->GetProxiedIID())) {
00391             return cur;
00392         }
00393         cur = cur->mNext;
00394     }
00395 
00396     return nsnull;
00397 }
00398 
00399 nsProxyEventObject::nsProxyEventObject()
00400 : mNext(nsnull)
00401 {
00402      NS_WARNING("This constructor should never be called");
00403 }
00404 
00405 nsProxyEventObject::nsProxyEventObject(nsIEventQueue *destQueue,
00406                                        PRInt32 proxyType,
00407                                        nsISupports* aObj,
00408                                        nsProxyEventClass* aClass,
00409                                        nsProxyEventObject* root,
00410                                        nsIEventQueueService* eventQService)
00411     : mClass(aClass),
00412       mRoot(root),
00413       mNext(nsnull)
00414 {
00415     NS_IF_ADDREF(mRoot);
00416 
00417     mProxyObject = new nsProxyObject(destQueue, proxyType, aObj, eventQService);
00418 
00419 #ifdef DEBUG_xpcom_proxy
00420     DebugDump("Create", 0);
00421 #endif
00422 }
00423 
00424 nsProxyEventObject::~nsProxyEventObject()
00425 {
00426 #ifdef DEBUG_xpcom_proxy
00427     DebugDump("Delete", 0);
00428 #endif
00429     if (mRoot) {
00430         //
00431         // This proxy is not the root interface so it must be removed
00432         // from the chain of proxies...
00433         //
00434         nsProxyEventObject* cur = mRoot;
00435         while(cur) {
00436             if(cur->mNext == this) {
00437                 cur->mNext = mNext;
00438                 mNext = nsnull;
00439                 break;
00440             }
00441             cur = cur->mNext;
00442         }
00443         NS_ASSERTION(cur, "failed to find wrapper in its own chain");
00444     }
00445     else {
00446         //
00447         // This proxy is for the root interface.  Each proxy in the chain
00448         // has a strong reference to the root... So, when its refcount goes
00449         // to zero, it safe to remove it because no proxies are in its chain.
00450         //
00451         if (! nsProxyObjectManager::IsManagerShutdown()) {
00452             nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance();
00453             nsHashtable *realToProxyMap = manager->GetRealObjectToProxyObjectMap();
00454 
00455             NS_ASSERTION(!mNext, "There are still proxies in the chain!");
00456 
00457             if (realToProxyMap != nsnull) {
00458                 nsCOMPtr<nsISupports> rootObject = do_QueryInterface(mProxyObject->mRealObject);
00459                 nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mDestQueue);
00460                 nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType);
00461 #ifdef DEBUG_dougt
00462                 void* value =
00463 #endif
00464                     realToProxyMap->Remove(&key);
00465 #ifdef DEBUG_dougt
00466                 NS_ASSERTION(value, "failed to remove from realToProxyMap");
00467 #endif
00468             }
00469         }
00470     }
00471 
00472     // I am worried about ordering.
00473     // do not remove assignments.
00474     mProxyObject = nsnull;
00475     mClass       = nsnull;
00476     NS_IF_RELEASE(mRoot);
00477 }
00478 
00479 //
00480 // nsISupports implementation...
00481 //
00482 
00483 NS_IMPL_THREADSAFE_ADDREF(nsProxyEventObject)
00484 
00485 NS_IMETHODIMP_(nsrefcnt)
00486 nsProxyEventObject::Release(void)
00487 {
00488     //
00489     // Be pessimistic about whether the manager or even the monitor exist...
00490     // This is to protect against shutdown issues where a proxy object could
00491     // be destroyed after (or while) the Proxy Manager is being destroyed...
00492     //
00493     nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance();
00494     nsAutoMonitor mon(manager ? manager->GetMonitor() : nsnull);
00495 
00496     nsrefcnt count;
00497     NS_PRECONDITION(0 != mRefCnt, "dup release");
00498     // Decrement atomically - in case the Proxy Object Manager has already
00499     // been deleted and the monitor is unavailable...
00500     count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
00501     NS_LOG_RELEASE(this, count, "nsProxyEventObject");
00502     if (0 == count) {
00503         mRefCnt = 1; /* stabilize */
00504         //
00505         // Remove the proxy from the hashtable (if necessary) or its
00506         // proxy chain.  This must be done inside of the proxy lock to
00507         // prevent GetNewOrUsedProxy(...) from ressurecting it...
00508         //
00509         NS_DELETEXPCOM(this);
00510         return 0;
00511     }
00512     return count;
00513 }
00514 
00515 NS_IMETHODIMP
00516 nsProxyEventObject::QueryInterface(REFNSIID aIID, void** aInstancePtr)
00517 {
00518     if( aIID.Equals(GetIID()) )
00519     {
00520         *aInstancePtr = NS_STATIC_CAST(nsISupports*, this);
00521         NS_ADDREF_THIS();
00522         return NS_OK;
00523     }
00524         
00525     return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
00526 }
00527 
00528 //
00529 // nsXPTCStubBase implementation...
00530 //
00531 
00532 NS_IMETHODIMP
00533 nsProxyEventObject::GetInterfaceInfo(nsIInterfaceInfo** info)
00534 {
00535     NS_ENSURE_ARG_POINTER(info);
00536 
00537     *info = mClass->GetInterfaceInfo();
00538 
00539     NS_ASSERTION(*info, "proxy class without interface");
00540     if (!*info) {
00541         return NS_ERROR_UNEXPECTED;
00542     }
00543 
00544     NS_ADDREF(*info);
00545     return NS_OK;
00546 }
00547 
00548 NS_IMETHODIMP
00549 nsProxyEventObject::CallMethod(PRUint16 methodIndex,
00550                                const nsXPTMethodInfo* info,
00551                                nsXPTCMiniVariant * params)
00552 {
00553     nsresult rv;
00554 
00555     if (mProxyObject) {
00556         rv = mProxyObject->Post(methodIndex,
00557                                 (nsXPTMethodInfo*)info,
00558                                 params,
00559                                 mClass->GetInterfaceInfo());
00560     } else {
00561         rv = NS_ERROR_NULL_POINTER;
00562     }
00563 
00564     return rv;
00565 }