Bug 342311 - xpcom/proxy refactoring, in preparation for xptcall rework, r=darin

git-svn-id: svn://10.0.0.236/trunk@202029 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
benjamin%smedbergs.us
2006-07-13 15:51:03 +00:00
parent c373ac03dd
commit 5e98b75364
6 changed files with 591 additions and 1341 deletions

View File

@@ -50,7 +50,6 @@
* 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
*/
#include "nsProxyEvent.h"
#include "nsProxyEventPrivate.h"
#include "nsProxyRelease.h"
#include "nsIProxyObjectManager.h"
@@ -60,20 +59,14 @@
#include "prmem.h"
#include "xptcall.h"
#include "nsAutoLock.h"
#include "nsXPCOMCID.h"
#include "nsServiceManagerUtils.h"
#include "nsIComponentManager.h"
#include "nsIThreadInternal.h"
#include "nsThreadUtils.h"
#include "nsEventQueue.h"
#include "nsMemory.h"
#include "prlog.h"
#ifdef PR_LOGGING
static PRLogModuleInfo *sLog = PR_NewLogModule("xpcomproxy");
#endif
#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
/**
* Map the nsAUTF8String, nsUTF8String classes to the nsACString and
* nsCString classes respectively for now. These defines need to be removed
@@ -82,177 +75,78 @@ static PRLogModuleInfo *sLog = PR_NewLogModule("xpcomproxy");
#define nsAUTF8String nsACString
#define nsUTF8String nsCString
//-----------------------------------------------------------------------------
#define NS_PROXYEVENT_IID \
{ /* 9a24dc5e-2b42-4a5a-aeca-37b8c8fd8ccd */ \
0x9a24dc5e, \
0x2b42, \
0x4a5a, \
{0xae, 0xca, 0x37, 0xb8, 0xc8, 0xfd, 0x8c, 0xcd} \
}
class nsProxyEvent : public nsIRunnable
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_IID)
NS_DECL_ISUPPORTS
nsProxyEvent(nsProxyObjectCallInfo *info = nsnull)
: mInfo(info)
{}
void SetInfo(nsProxyObjectCallInfo *info)
{
NS_ASSERTION(info, "null info");
NS_ASSERTION(!mInfo, "unexpected change to mInfo");
mInfo = info;
}
PRBool IsSyncProxyEvent()
{
return mInfo->GetProxyObject()->GetProxyType() & NS_PROXY_SYNC;
}
protected:
nsProxyObjectCallInfo *mInfo;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyEvent, NS_PROXYEVENT_IID)
NS_IMPL_THREADSAFE_ISUPPORTS2(nsProxyEvent, nsProxyEvent, nsIRunnable)
//-----------------------------------------------------------------------------
class nsProxyThreadFilter : public nsIThreadEventFilter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITHREADEVENTFILTER
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsProxyThreadFilter, nsIThreadEventFilter)
NS_IMETHODIMP_(PRBool)
nsProxyThreadFilter::AcceptEvent(nsIRunnable *event)
{
LOG(("PROXY(%p): filter event [%p]\n", this, event));
// If we encounter one of our proxy events that is for a synchronous method
// call, then we want to put it in our event queue for processing. Else,
// we want to allow the event to be dispatched to the thread's event queue
// for processing later once we complete the current sync method call.
nsCOMPtr<nsProxyEvent> pe = do_QueryInterface(event);
return pe && pe->IsSyncProxyEvent();
}
//-----------------------------------------------------------------------------
class nsProxyCallEvent : public nsProxyEvent
{
public:
nsProxyCallEvent()
: nsProxyEvent()
{}
NS_IMETHOD Run()
{
NS_ASSERTION(mInfo, "no info");
Invoke();
if (IsSyncProxyEvent()) {
mInfo->PostCompleted();
} else {
delete mInfo;
mInfo = nsnull;
}
return NS_OK;
}
void Invoke()
{
LOG(("PROXY(%p): Invoke\n", this));
nsProxyObject *proxyObject = mInfo->GetProxyObject();
if (proxyObject)
{
// invoke the magic of xptc...
nsresult rv = XPTC_InvokeByIndex(proxyObject->GetRealObject(),
mInfo->GetMethodIndex(),
mInfo->GetParameterCount(),
mInfo->GetParameterList());
mInfo->SetResult(rv);
}
else
{
mInfo->SetResult(NS_ERROR_OUT_OF_MEMORY);
}
}
};
//-----------------------------------------------------------------------------
class nsProxyCallCompletedEvent : public nsProxyEvent
class nsProxyCallCompletedEvent : public nsRunnable
{
public:
nsProxyCallCompletedEvent(nsProxyObjectCallInfo *info)
: nsProxyEvent(info)
: mInfo(info)
{}
NS_IMETHOD Run()
{
NS_ASSERTION(mInfo, "no info");
mInfo->SetCompleted();
return NS_OK;
}
};
NS_DECL_NSIRUNNABLE
//-----------------------------------------------------------------------------
class nsProxyDestructorEvent : public nsRunnable
{
public:
nsProxyDestructorEvent(nsProxyObject *doomed)
: mDoomed(doomed)
{}
NS_IMETHOD Run()
{
delete mDoomed;
return NS_OK;
}
NS_IMETHOD QueryInterface(REFNSIID aIID, void **aResult);
private:
nsProxyObject *mDoomed;
nsProxyObjectCallInfo *mInfo;
};
NS_IMETHODIMP
nsProxyCallCompletedEvent::Run()
{
NS_ASSERTION(mInfo, "no info");
mInfo->SetCompleted();
return NS_OK;
}
NS_DEFINE_IID(kFilterIID, NS_PROXYEVENT_FILTER_IID);
NS_IMETHODIMP
nsProxyCallCompletedEvent::QueryInterface(REFNSIID aIID, void **aResult)
{
// We are explicitly breaking XPCOM rules here by returning a different
// object from QueryInterface. We do this so that
// nsProxyThreadFilter::AcceptEvent can know whether we are an event that
// needs to be allowed through during a synchronous proxy call.
if (aIID.Equals(kFilterIID)) {
*aResult = mInfo;
mInfo->AddRef();
return NS_OK;
}
return nsRunnable::QueryInterface(aIID, aResult);
}
//-----------------------------------------------------------------------------
nsProxyObjectCallInfo::nsProxyObjectCallInfo(nsProxyObject* owner,
nsXPTMethodInfo *methodInfo,
NS_IMETHODIMP
nsProxyObject::nsProxyObjectDestructorEvent::Run()
{
delete mDoomed;
return NS_OK;
}
//-----------------------------------------------------------------------------
nsProxyObjectCallInfo::nsProxyObjectCallInfo(nsProxyEventObject* owner,
const nsXPTMethodInfo *methodInfo,
PRUint32 methodIndex,
nsXPTCVariant* parameterList,
PRUint32 parameterCount,
nsIRunnable *event)
PRUint32 parameterCount) :
mResult(NS_ERROR_FAILURE),
mMethodInfo(methodInfo),
mMethodIndex(methodIndex),
mParameterList(parameterList),
mParameterCount(parameterCount),
mCompleted(0),
mOwner(owner)
{
NS_ASSERTION(owner, "No nsProxyObject!");
NS_ASSERTION(methodInfo, "No nsXPTMethodInfo!");
NS_ASSERTION(event, "No PLEvent!");
mCompleted = 0;
mMethodIndex = methodIndex;
mParameterList = parameterList;
mParameterCount = parameterCount;
mEvent = event; // XXX strong or weak ref?
mMethodInfo = methodInfo;
mCallersTarget = nsnull;
mOwner = owner;
RefCountInInterfacePointers(PR_TRUE);
if (mOwner->GetProxyType() & NS_PROXY_ASYNC)
CopyStrings(PR_TRUE);
}
nsProxyObjectCallInfo::~nsProxyObjectCallInfo()
{
RefCountInInterfacePointers(PR_FALSE);
@@ -262,7 +156,35 @@ nsProxyObjectCallInfo::~nsProxyObjectCallInfo()
mOwner = nsnull;
if (mParameterList)
free((void*) mParameterList);
free(mParameterList);
}
NS_IMETHODIMP
nsProxyObjectCallInfo::QueryInterface(REFNSIID aIID, void **aResult)
{
if (aIID.Equals(kFilterIID)) {
*aResult = this;
AddRef();
return NS_OK;
}
return nsRunnable::QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
nsProxyObjectCallInfo::Run()
{
PROXY_LOG(("PROXY(%p): Run\n", this));
mResult = XPTC_InvokeByIndex(mOwner->GetProxiedInterface(),
mMethodIndex,
mParameterCount,
mParameterList);
if (IsSync()) {
PostCompleted();
}
return NS_OK;
}
void
@@ -375,14 +297,14 @@ nsProxyObjectCallInfo::GetCompleted()
void
nsProxyObjectCallInfo::SetCompleted()
{
LOG(("PROXY(%p): SetCompleted\n", this));
PROXY_LOG(("PROXY(%p): SetCompleted\n", this));
PR_AtomicSet(&mCompleted, 1);
}
void
nsProxyObjectCallInfo::PostCompleted()
{
LOG(("PROXY(%p): PostCompleted\n", this));
PROXY_LOG(("PROXY(%p): PostCompleted\n", this));
if (mCallersTarget) {
nsCOMPtr<nsIRunnable> event =
@@ -410,13 +332,15 @@ nsProxyObjectCallInfo::SetCallersTarget(nsIEventTarget* target)
}
nsProxyObject::nsProxyObject(nsIEventTarget *target, PRInt32 proxyType,
nsISupports *realObject,
nsISupports *rootObject)
nsISupports *realObject) :
mProxyType(proxyType),
mTarget(target),
mRealObject(realObject),
mFirst(nsnull)
{
mRealObject = realObject;
mRootObject = rootObject;
mTarget = target;
mProxyType = proxyType;
nsProxyObjectManager *pom = nsProxyObjectManager::GetInstance();
NS_ASSERTION(pom, "Creating a proxy without a global proxy-object-manager.");
pom->AddRef();
}
nsProxyObject::~nsProxyObject()
@@ -428,208 +352,98 @@ nsProxyObject::~nsProxyObject()
NS_ProxyRelease(mTarget, doomed);
}
void
nsProxyObject::AddRef()
NS_IMPL_THREADSAFE_ADDREF(nsProxyObject)
NS_IMETHODIMP_(nsrefcnt)
nsProxyObject::Release()
{
PR_AtomicIncrement((PRInt32 *)&mRefCnt);
NS_LOG_ADDREF(this, mRefCnt, "nsProxyObject", sizeof(*this));
nsrefcnt count;
NS_PRECONDITION(0 != mRefCnt, "dup release");
count = PR_AtomicDecrement((PRInt32*) &mRefCnt);
NS_LOG_RELEASE(this, count, "nsProxyRelease");
if (count)
return count;
nsProxyObjectManager *pom = nsProxyObjectManager::GetInstance();
NS_ASSERTION(pom, "Deleting a proxy without a global proxy-object-manager.");
pom->Remove(this);
pom->Release();
return 0;
}
void
nsProxyObject::Release(void)
NS_IMETHODIMP
nsProxyObject::QueryInterface(REFNSIID aIID, void **aResult)
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
NS_LOG_RELEASE(this, count, "nsProxyObject");
if (count == 0)
{
mRefCnt = 1; /* stabilize */
PRBool callDirectly;
mTarget->IsOnCurrentThread(&callDirectly);
if (callDirectly)
{
delete this;
return;
}
// need to do something special here so that
// the real object will always be deleted on
// the correct thread..
nsCOMPtr<nsIRunnable> event = new nsProxyDestructorEvent(this);
if (event == nsnull)
{
NS_NOTREACHED("Leaking nsProxyObject!");
return; // if this happens we are going to leak.
}
if (NS_FAILED(mTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
NS_WARNING("Failed to dispatch nsProxyDestructorEvent");
}
}
nsresult
nsProxyObject::PostAndWait(nsProxyObjectCallInfo *proxyInfo)
{
LOG(("PROXY(%p): PostAndWait enter [%p]\n", this, proxyInfo));
NS_ENSURE_ARG_POINTER(proxyInfo);
nsIRunnable* event = proxyInfo->GetEvent();
if (!event)
return NS_ERROR_NULL_POINTER;
nsIThread *thread = NS_GetCurrentThread();
nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
NS_ENSURE_STATE(threadInt);
// Install thread filter to limit event processing only to subclasses of
// nsProxyEvent. XXX Add support for sequencing?
nsRefPtr<nsProxyThreadFilter> filter = new nsProxyThreadFilter();
if (!filter)
return NS_ERROR_OUT_OF_MEMORY;
threadInt->PushEventQueue(filter);
proxyInfo->SetCallersTarget(thread);
// Dispatch can fail if the thread is shutting down
nsresult rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_SUCCEEDED(rv)) {
while (!proxyInfo->GetCompleted()) {
if (!NS_ProcessNextEvent(thread)) {
rv = NS_ERROR_UNEXPECTED;
break;
}
}
} else {
NS_WARNING("Failed to dispatch nsProxyCallEvent");
if (aIID.Equals(GetIID())) {
*aResult = this;
AddRef();
return NS_OK;
}
threadInt->PopEventQueue();
LOG(("PROXY(%p): PostAndWait exit [%p %x]\n", this, proxyInfo, rv));
return rv;
}
nsresult
nsProxyObject::convertMiniVariantToVariant(nsXPTMethodInfo *methodInfo,
nsXPTCMiniVariant * params,
nsXPTCVariant **fullParam,
uint8 *outParamCount)
{
uint8 paramCount = methodInfo->GetParamCount();
*outParamCount = paramCount;
*fullParam = nsnull;
if (!paramCount) return NS_OK;
*fullParam = (nsXPTCVariant*)malloc(sizeof(nsXPTCVariant) * paramCount);
if (*fullParam == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
for (int i = 0; i < paramCount; i++)
{
const nsXPTParamInfo& paramInfo = methodInfo->GetParam(i);
if ((mProxyType & NS_PROXY_ASYNC) && paramInfo.IsDipper())
{
NS_WARNING("Async proxying of out parameters is not supported");
free(*fullParam);
return NS_ERROR_PROXY_INVALID_OUT_PARAMETER;
}
uint8 flags = paramInfo.IsOut() ? nsXPTCVariant::PTR_IS_DATA : 0;
(*fullParam)[i].Init(params[i], paramInfo.GetType(), flags);
if (aIID.Equals(NS_GET_IID(nsISupports))) {
*aResult = NS_STATIC_CAST(nsISupports*, this);
AddRef();
return NS_OK;
}
return NS_OK;
nsProxyObjectManager *pom = nsProxyObjectManager::GetInstance();
NS_ASSERTION(pom, "Deleting a proxy without a global proxy-object-manager.");
nsAutoMonitor mon(pom->GetMonitor());
return LockedFind(aIID, aResult);
}
nsresult
nsProxyObject::Post( PRUint32 methodIndex,
nsXPTMethodInfo *methodInfo,
nsXPTCMiniVariant * params,
nsIInterfaceInfo *interfaceInfo)
nsProxyObject::LockedFind(REFNSIID aIID, void **aResult)
{
nsresult rv = NS_OK;
// This method is only called when the global monitor is held.
// XXX assert this
if (! mTarget || ! mRealObject)
return NS_ERROR_OUT_OF_MEMORY;
nsProxyEventObject *peo;
if (methodInfo->IsNotXPCOM())
return NS_ERROR_PROXY_INVALID_IN_PARAMETER;
nsXPTCVariant *fullParam;
uint8 paramCount;
rv = convertMiniVariantToVariant(methodInfo, params, &fullParam, &paramCount);
for (peo = mFirst; peo; peo = peo->mNext) {
if (peo->GetClass()->GetProxiedIID().Equals(aIID)) {
*aResult = NS_STATIC_CAST(nsISupports*, peo);
peo->AddRef();
return NS_OK;
}
}
nsProxyEventClass *pec;
nsresult rv = nsProxyObjectManager::GetInstance()->GetClass(aIID, &pec);
if (NS_FAILED(rv))
return rv;
PRBool callDirectly;
// see if we should call into the method directly. Either it is a QI function call
// (methodIndex == 0), or it is a sync proxy and this code is running on the same thread
// as the destination dispatch target.
if ( (methodIndex == 0) ||
(mProxyType & NS_PROXY_SYNC &&
NS_SUCCEEDED(mTarget->IsOnCurrentThread(&callDirectly)) &&
callDirectly))
{
// invoke the magic of xptc...
nsresult rv = XPTC_InvokeByIndex( mRealObject,
methodIndex,
paramCount,
fullParam);
if (fullParam)
free(fullParam);
nsISomeInterface* newInterface;
rv = mRealObject->QueryInterface(aIID, (void**) &newInterface);
if (NS_FAILED(rv))
return rv;
}
nsRefPtr<nsProxyCallEvent> event = new nsProxyCallEvent();
if (!event) {
if (fullParam)
free(fullParam);
return NS_ERROR_OUT_OF_MEMORY;
}
nsProxyObjectCallInfo *proxyInfo =
new nsProxyObjectCallInfo(this,
methodInfo,
methodIndex,
fullParam, // will be deleted by ~()
paramCount,
event); // will be deleted by ~()
if (!proxyInfo) {
if (fullParam)
free(fullParam);
return NS_ERROR_OUT_OF_MEMORY;
}
peo = new nsProxyEventObject(this, pec, newInterface);
if (!peo)
return NS_ERROR_OUT_OF_MEMORY;
event->SetInfo(proxyInfo);
peo->mNext = mFirst;
mFirst = peo;
if (mProxyType & NS_PROXY_SYNC)
{
rv = PostAndWait(proxyInfo);
if (NS_SUCCEEDED(rv))
rv = proxyInfo->GetResult();
delete proxyInfo;
return rv;
}
if (mProxyType & NS_PROXY_ASYNC)
{
mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
return NS_OK;
}
return NS_ERROR_UNEXPECTED;
NS_ADDREF(peo);
*aResult = (nsISupports*) peo;
return NS_OK;
}
void
nsProxyObject::LockedRemove(nsProxyEventObject *peo)
{
nsProxyEventObject **i;
for (i = &mFirst; *i; i = &((*i)->mNext)) {
if (*i == peo) {
*i = peo->mNext;
break;
}
}
}

View File

@@ -1,172 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsProxyEvent_h__
#define nsProxyEvent_h__
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nscore.h"
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIEventTarget.h"
#include "prtypes.h"
#include "xptcall.h"
#include "xptinfo.h"
class nsProxyObjectCallInfo;
class nsIRunnable;
//#define AUTOPROXIFICATION
// WARNING about NS_PROXY_ASYNC:
//
// If the calling thread goes away, any function which accesses the calling stack
// will blow up.
//
// example:
//
// myFoo->bar(&x)
//
// ... thread goes away ...
//
// bar(PRInt32 *x)
// {
// *x = 0; <----- You will blow up here.
//
//
// So what gets saved?
//
// You can safely pass base types by value. You can also pass interface pointers.
// I will make sure that the interface pointers are addrefed while they are being
// proxied. You can also pass string and wstring. These I will copy and free.
//
// I do **NOT** copy arrays or strings with size. If you are using these either
// change your interface, or contact me about this feature request.
class nsProxyObject
{
public:
nsProxyObject(nsIEventTarget *destQueue, PRInt32 proxyType,
nsISupports *realObject, nsISupports *rootObject);
void AddRef();
void Release();
~nsProxyObject();
nsresult Post( PRUint32 methodIndex,
nsXPTMethodInfo * info,
nsXPTCMiniVariant * params,
nsIInterfaceInfo * interfaceInfo);
nsresult PostAndWait(nsProxyObjectCallInfo *proxyInfo);
nsISupports* GetRealObject() const { return mRealObject; }
nsIEventTarget* GetTarget() const { return mTarget; }
PRInt32 GetProxyType() const { return mProxyType; }
friend class nsProxyEventObject;
private:
nsAutoRefCnt mRefCnt;
PRInt32 mProxyType;
nsCOMPtr<nsIEventTarget> mTarget; /* event target */
nsCOMPtr<nsISupports> mRealObject; /* the non-proxy object that this event is referring to.
This is a strong ref. */
nsISupports *mRootObject; /* weak pointer to identity object */
nsresult convertMiniVariantToVariant(nsXPTMethodInfo * methodInfo,
nsXPTCMiniVariant * params,
nsXPTCVariant **fullParam,
uint8 *paramCount);
};
class nsProxyObjectCallInfo
{
public:
nsProxyObjectCallInfo(nsProxyObject* owner,
nsXPTMethodInfo *methodInfo,
PRUint32 methodIndex,
nsXPTCVariant* parameterList,
PRUint32 parameterCount,
nsIRunnable *event);
~nsProxyObjectCallInfo();
PRUint32 GetMethodIndex() const { return mMethodIndex; }
nsXPTCVariant* GetParameterList() const { return mParameterList; }
PRUint32 GetParameterCount() const { return mParameterCount; }
nsIRunnable* GetEvent() const { return mEvent; }
nsresult GetResult() const { return mResult; }
nsProxyObject* GetProxyObject() const { return mOwner; }
PRBool GetCompleted();
void SetCompleted();
void PostCompleted();
void SetResult(nsresult rv) { mResult = rv; }
nsIEventTarget* GetCallersTarget();
void SetCallersTarget(nsIEventTarget* target);
private:
nsresult mResult; /* this is the return result of the called function */
nsXPTMethodInfo *mMethodInfo;
PRUint32 mMethodIndex; /* which method to be called? */
nsXPTCVariant *mParameterList; /* marshalled in parameter buffer */
PRUint32 mParameterCount; /* number of params */
nsIRunnable *mEvent; /* the current runnable */
PRInt32 mCompleted; /* is true when the method has been called. */
nsCOMPtr<nsIEventTarget> mCallersTarget; /* this is the dispatch target that we must post a message back to
when we are done invoking the method (only NS_PROXY_SYNC). */
nsRefPtr<nsProxyObject> mOwner; /* this is the strong referenced nsProxyObject */
void RefCountInInterfacePointers(PRBool addRef);
void CopyStrings(PRBool copy);
};
#endif // nsProxyEvent_h__

View File

@@ -37,9 +37,6 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsProxyEvent.h"
#include "nsIProxyObjectManager.h"
#include "nsProxyEventPrivate.h"
#include "nsIComponentManager.h"
@@ -50,7 +47,6 @@
#include "nsHashtable.h"
#include "nsAutoLock.h"
#include "xptiprivate.h"
#include "xptcall.h"
// LIFETIME_CACHE will cache class for the entire cyle of the application.
@@ -59,138 +55,15 @@
static uint32 zero_methods_descriptor;
/* ssc@netscape.com wishes he could get rid of this instance of
* |NS_DEFINE_IID|, but |ProxyEventClassIdentity| is not visible from
* here.
*/
static NS_DEFINE_IID(kProxyObject_Identity_Class_IID, NS_PROXYEVENT_IDENTITY_CLASS_IID);
//////////////////////////////////////////////////////////////////////////////////////////////////
// nsProxyEventClass
//////////////////////////////////////////////////////////////////////////////////////////////////
NS_IMPL_THREADSAFE_ISUPPORTS1(nsProxyEventClass, nsProxyEventClass)
// static
nsProxyEventClass*
nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID)
{
/* find in our hash table */
nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance();
if (manager == nsnull)
return nsnull;
// don't need to lock the map, because this is only called by
// nsProxyEventClass::GetNewOrUsed which locks it for us.
nsHashtable *iidToClassMap = manager->GetIIDToProxyClassMap();
if (iidToClassMap == nsnull)
{
return nsnull;
}
nsProxyEventClass* clazz = nsnull;
nsIDKey key(aIID);
#ifdef PROXYEVENTCLASS_DEBUG
char* iidStr = aIID.ToString();
printf("GetNewOrUsedClass %s\n", iidStr);
PR_Free(iidStr);
#endif
clazz = (nsProxyEventClass*) iidToClassMap->Get(&key);
if(clazz)
{
NS_ADDREF(clazz);
#ifdef PROXYEVENTCLASS_DEBUG
char* iidStr = aIID.ToString();
printf("GetNewOrUsedClass %s hit\n", iidStr);
PR_Free(iidStr);
#endif
}
else
{
nsIInterfaceInfoManager* iimgr =
xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef();
if(iimgr)
{
nsCOMPtr<nsIInterfaceInfo> info;
if(NS_SUCCEEDED(iimgr->GetInfoForIID(&aIID, getter_AddRefs(info))))
{
/*
Check to see if isISupportsDescendent
*/
nsCOMPtr<nsIInterfaceInfo> oldest = info;
nsCOMPtr<nsIInterfaceInfo> parent;
while(NS_SUCCEEDED(oldest->GetParent(getter_AddRefs(parent)))&&
parent)
{
oldest = parent;
}
PRBool isISupportsDescendent = PR_FALSE;
nsID* iid;
if(NS_SUCCEEDED(oldest->GetInterfaceIID(&iid)))
{
isISupportsDescendent = iid->Equals(NS_GET_IID(nsISupports));
nsMemory::Free(iid);
}
NS_ASSERTION(isISupportsDescendent, "!isISupportsDescendent");
if (isISupportsDescendent)
{
clazz = new nsProxyEventClass(aIID, info);
if (!clazz->mDescriptors)
NS_RELEASE(clazz); // sets clazz to NULL
}
}
}
}
return clazz;
}
nsProxyEventClass::nsProxyEventClass()
{
NS_NOTREACHED("This constructor should never be called");
}
nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo)
: mIID(aIID),
mInfo(aInfo),
mDescriptors(NULL)
{
NS_ADDREF_THIS();
mInfo = aInfo;
/* add use to the used classes */
nsIDKey key(aIID);
nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance();
if (manager == nsnull)
return;
// don't need to lock the map, because this is only called
// by GetNewOrUsed which locks it for us.
nsHashtable *iidToClassMap = manager->GetIIDToProxyClassMap();
if (iidToClassMap != nsnull)
{
iidToClassMap->Put(&key, this);
#ifdef LIFETIME_CACHE
// extra addref to hold it in the cache
NS_ADDREF_THIS();
#endif
#ifdef PROXYEVENTCLASS_DEBUG
char* iidStr = aIID.ToString();
printf("GetNewOrUsedClass %s put\n", iidStr);
PR_Free(iidStr);
#endif
}
uint16 methodCount;
if(NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount)))
{
@@ -213,163 +86,4 @@ nsProxyEventClass::~nsProxyEventClass()
{
if (mDescriptors && mDescriptors != &zero_methods_descriptor)
delete [] mDescriptors;
if (nsProxyObjectManager::IsManagerShutdown())
return;
#ifndef LIFETIME_CACHE
nsIDKey key(mIID);
nsCOMPtr<nsProxyObjectManager> manager = getter_AddRefs(nsProxyObjectManager::GetInstance());
if (manager == nsnull) return;
nsHashtable *iidToClassMap = manager->GetIIDToProxyClassMap();
if (iidToClassMap != nsnull)
{
iidToClassMap->Remove(&key);
#ifdef PROXYEVENTCLASS_DEBUG
char* iidStr = mIID.ToString();
printf("GetNewOrUsedClass %s remove\n", iidStr);
PR_Free(iidStr);
#endif
}
#endif
}
nsresult
nsProxyEventClass::CallQueryInterfaceOnProxy(nsProxyEventObject* self, REFNSIID aIID, nsProxyEventObject** aInstancePtr)
{
NS_PRECONDITION(aInstancePtr, "Requires non-null result");
nsresult rv;
*aInstancePtr = nsnull; // in case of error.
// The functions we will call: QueryInterface(REFNSIID aIID, void** aInstancePtr)
nsXPTCMiniVariant var[2];
var[0].val.p = (void*)&aIID;
var[1].val.p = (void*)aInstancePtr;
nsCOMPtr<nsIInterfaceInfo> interfaceInfo;
const nsXPTMethodInfo *mi;
nsIInterfaceInfoManager* iim =
xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef();
if (!iim) return NS_NOINTERFACE;
iim->GetInfoForName("nsISupports", getter_AddRefs(interfaceInfo));
interfaceInfo->GetMethodInfo(0, &mi); // 0 is QueryInterface
rv = self->CallMethod(0, mi, var);
if (NS_SUCCEEDED(rv))
{
nsISupports *aIdentificationObject;
rv = (*aInstancePtr)->QueryInterface(kProxyObject_Identity_Class_IID, (void**)&aIdentificationObject);
if (NS_FAILED(rv))
{
// okay, aInstancePtr was not a proxy. Lets create one.
nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance();
if (manager == nsnull)
{
NS_IF_RELEASE((*aInstancePtr));
return NS_ERROR_FAILURE;
}
rv = manager->GetProxyForObject(self->GetTarget(),
aIID,
self->GetRealObject(),
self->GetProxyType(),
(void**) &aIdentificationObject);
}
NS_IF_RELEASE((*aInstancePtr));
(*aInstancePtr) = NS_STATIC_CAST(nsProxyEventObject*, aIdentificationObject);
}
return rv;
}
/***************************************************************************/
// This 'ProxyEventClassIdentity' class and singleton allow us to figure out if
// any given nsISupports* is implemented by a nsProxy object. This is done
// using a QueryInterface call on the interface pointer with our ID. If
// that call returns NS_OK and the pointer is to a nsProxyEventObject. It must
// be released when done.
// NS_PROXYEVENT_IDENTITY_CLASS_IID defined in nsProxyEventPrivate.h
class ProxyEventClassIdentity
{
// no instance methods...
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_IDENTITY_CLASS_IID)
static void* GetSingleton()
{
static ProxyEventClassIdentity* singleton = NULL;
if(!singleton)
singleton = new ProxyEventClassIdentity();
return singleton;
}
};
NS_DEFINE_STATIC_IID_ACCESSOR(ProxyEventClassIdentity,
NS_PROXYEVENT_IDENTITY_CLASS_IID)
NS_IMETHODIMP
nsProxyEventClass::DelegatedQueryInterface(nsProxyEventObject* self,
REFNSIID aIID,
void** aInstancePtr)
{
if(aIID.Equals(NS_GET_IID(ProxyEventClassIdentity)))
{
*aInstancePtr = NS_STATIC_CAST(void*, self);
NS_ADDREF(self);
return NS_OK;
}
nsProxyEventObject* sibling;
{
nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance();
nsAutoMonitor mon(manager->GetMonitor());
// This includes checking for nsISupports and the iid of self.
// And it also checks for other wrappers that have been constructed
// for this object.
if (nsnull != (sibling = self->LockedFind(aIID)))
{
NS_ADDREF(sibling);
*aInstancePtr = (void*) sibling;
return NS_OK;
}
// check if asking for an interface that we inherit from
nsCOMPtr<nsIInterfaceInfo> current = GetInterfaceInfo();
nsCOMPtr<nsIInterfaceInfo> parent;
while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(parent))) && parent)
{
current = parent;
nsIID* iid;
if (NS_SUCCEEDED(current->GetInterfaceIID(&iid)) && iid)
{
PRBool found = aIID.Equals(*iid);
nsMemory::Free(iid);
if (found)
{
*aInstancePtr = (void*) self;
NS_ADDREF(self);
return NS_OK;
}
}
}
}
return CallQueryInterfaceOnProxy(self, aIID, (nsProxyEventObject**)aInstancePtr);
}

View File

@@ -21,6 +21,7 @@
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Benjamin Smedberg <benjamin@smedbergs.us>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@@ -40,9 +41,8 @@
#include "prmem.h"
#include "nscore.h"
#include "nsProxyEvent.h"
#include "nsIProxyObjectManager.h"
#include "nsProxyEventPrivate.h"
#include "nsIThreadInternal.h"
#include "nsServiceManagerUtils.h"
@@ -53,423 +53,22 @@
#include "nsAutoLock.h"
static NS_DEFINE_IID(kProxyObject_Identity_Class_IID, NS_PROXYEVENT_IDENTITY_CLASS_IID);
////////////////////////////////////////////////////////////////////////////////
class nsProxyEventKey : public nsHashKey
{
public:
nsProxyEventKey(void* rootObjectKey, void* targetKey, PRInt32 proxyType)
: mRootObjectKey(rootObjectKey), mTargetKey(targetKey), mProxyType(proxyType) {
}
PRUint32 HashCode(void) const {
return NS_PTR_TO_INT32(mRootObjectKey) ^
NS_PTR_TO_INT32(mTargetKey) ^ mProxyType;
}
PRBool Equals(const nsHashKey *aKey) const {
const nsProxyEventKey* other = (const nsProxyEventKey*)aKey;
return mRootObjectKey == other->mRootObjectKey
&& mTargetKey == other->mTargetKey
&& mProxyType == other->mProxyType;
}
nsHashKey *Clone() const {
return new nsProxyEventKey(mRootObjectKey, mTargetKey, mProxyType);
}
protected:
void* mRootObjectKey;
void* mTargetKey;
PRInt32 mProxyType;
};
////////////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_xpcom_proxy
static PRMonitor* mon = nsnull;
static PRUint32 totalProxyObjects = 0;
static PRUint32 outstandingProxyObjects = 0;
void
nsProxyEventObject::DebugDump(const char * message, PRUint32 hashKey)
{
if (mon == nsnull)
{
mon = PR_NewMonitor();
}
PR_EnterMonitor(mon);
if (message)
{
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf("%s\n", message);
if(strcmp(message, "Create") == 0)
{
totalProxyObjects++;
outstandingProxyObjects++;
}
else if(strcmp(message, "Delete") == 0)
{
outstandingProxyObjects--;
}
}
printf("nsProxyEventObject @ %x with mRefCnt = %d\n", this, mRefCnt);
PRBool isRoot = mRoot == nsnull;
printf("%s wrapper around @ %x\n", isRoot ? "ROOT":"non-root\n", GetRealObject());
nsCOMPtr<nsISupports> rootObject = do_QueryInterface(mProxyObject->mRealObject);
nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mTarget);
nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType);
printf("Hashkey: %d\n", key.HashCode());
char* name;
GetClass()->GetInterfaceInfo()->GetName(&name);
printf("interface name is %s\n", name);
if(name)
nsMemory::Free(name);
char * iid = GetClass()->GetProxiedIID().ToString();
printf("IID number is %s\n", iid);
delete iid;
printf("nsProxyEventClass @ %x\n", mClass);
if(mNext)
{
if(isRoot)
{
printf("Additional wrappers for this object...\n");
}
mNext->DebugDump(nsnull, 0);
}
printf("[proxyobjects] %d total used in system, %d outstading\n", totalProxyObjects, outstandingProxyObjects);
if (message)
printf("-=-=-=-=-=-=-=-=-=-=-=-=-\n");
PR_ExitMonitor(mon);
}
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// nsProxyEventObject
//
//////////////////////////////////////////////////////////////////////////////////////////////////
nsProxyEventObject*
nsProxyEventObject::GetNewOrUsedProxy(nsIEventTarget *target,
PRInt32 proxyType,
nsISupports *aObj,
REFNSIID aIID)
{
nsresult rv;
if (!aObj)
return nsnull;
nsISupports* rawObject = aObj;
//
// make sure that the object pass in is not a proxy...
// if the object *is* a proxy, then be nice and build the proxy
// for the real object...
//
nsCOMPtr<nsProxyEventObject> identificationObject;
rv = rawObject->QueryInterface(kProxyObject_Identity_Class_IID,
getter_AddRefs(identificationObject));
if (NS_SUCCEEDED(rv)) {
//
// ATTENTION!!!!
//
// If you are hitting any of the assertions in this block of code,
// please contact dougt@netscape.com.
//
// if you hit this assertion, you might want to check out how
// you are using proxies. You shouldn't need to be creating
// a proxy from a proxy. -- dougt@netscape.com
NS_WARNING("Someone is building a proxy from a proxy");
NS_ASSERTION(identificationObject, "where did my identification object go!");
if (!identificationObject) {
return nsnull;
}
// someone is asking us to create a proxy for a proxy. Lets get
// the real object and build aproxy for that!
rawObject = identificationObject->GetRealObject();
NS_ASSERTION(rawObject, "where did my real object go!");
if (!rawObject) {
return nsnull;
}
}
//
// Get the root nsISupports of the |real| object.
//
nsCOMPtr<nsISupports> rootObject;
rootObject = do_QueryInterface(rawObject, &rv);
if (NS_FAILED(rv) || !rootObject) {
NS_ASSERTION(NS_FAILED(rv), "where did my root object go!");
return nsnull;
}
// Get the root nsISupports of the event queue... This is used later,
// as part of the hashtable key...
nsCOMPtr<nsISupports> destQRoot = do_QueryInterface(target, &rv);
if (NS_FAILED(rv) || !destQRoot) {
return nsnull;
}
//
// Enter the proxy object creation lock.
//
// This lock protects thev linked list which chains proxies together
// (ie. mRoot and mNext) and ensures that there is no hashtable contention
// between the time that a proxy is looked up (and not found) in the
// hashtable and then added...
//
nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance();
if (!manager) {
return nsnull;
}
nsAutoMonitor mon(manager->GetMonitor());
// Get the hash table containing root proxy objects...
nsHashtable *realToProxyMap = manager->GetRealObjectToProxyObjectMap();
if (!realToProxyMap) {
return nsnull;
}
// Now, lookup the root nsISupports of the raw object in the hashtable
// The key consists of 3 pieces of information:
// - root nsISupports of the raw object
// - event queue of the current thread
// - type of proxy being constructed...
//
nsProxyEventKey rootkey(rootObject.get(), destQRoot.get(), proxyType);
nsCOMPtr<nsProxyEventObject> rootProxy;
nsCOMPtr<nsProxyEventObject> proxy;
nsProxyEventObject* peo;
// find in our hash table
rootProxy = (nsProxyEventObject*) realToProxyMap->Get(&rootkey);
if(rootProxy) {
//
// At least one proxy has already been created for this raw object...
//
// Look for the specific interface proxy in the list off of
// the root proxy...
//
peo = rootProxy->LockedFind(aIID);
if(peo) {
// An existing proxy is available... So use it.
NS_ADDREF(peo);
return peo;
}
}
else {
// build the root proxy
nsCOMPtr<nsProxyEventClass> rootClazz;
rootClazz = dont_AddRef(nsProxyEventClass::GetNewOrUsedClass(
NS_GET_IID(nsISupports)));
if (!rootClazz) {
return nsnull;
}
peo = new nsProxyEventObject(target,
proxyType,
rootObject,
rootObject,
rootClazz,
nsnull);
if(!peo) {
// Ouch... Out of memory!
return nsnull;
}
// Add this root proxy into the hash table...
realToProxyMap->Put(&rootkey, peo);
if(aIID.Equals(NS_GET_IID(nsISupports))) {
//
// Since the requested proxy is for the nsISupports interface of
// the raw object, use the new root proxy as the specific proxy
// too...
//
NS_ADDREF(peo);
return peo;
}
// This assignment is an owning reference to the new ProxyEventObject.
// So, it will automatically get deleted if any subsequent early
// returns are taken...
rootProxy = do_QueryInterface(peo);
}
//
// at this point we have a proxy for the root nsISupports (ie. rootProxy)
// but we need to build the specific proxy for this interface...
//
NS_ASSERTION(rootProxy, "What happened to the root proxy!");
// Get a class for this IID.
nsCOMPtr<nsProxyEventClass> proxyClazz;
proxyClazz = dont_AddRef(nsProxyEventClass::GetNewOrUsedClass(aIID));
if(!proxyClazz) {
return nsnull;
}
// Get the raw interface for this IID
nsCOMPtr<nsISupports> rawInterface;
rv = rawObject->QueryInterface(aIID, getter_AddRefs(rawInterface));
if (NS_FAILED(rv) || !rawInterface) {
NS_ASSERTION(NS_FAILED(rv), "where did my rawInterface object go!");
return nsnull;
}
peo = new nsProxyEventObject(target,
proxyType,
rawInterface,
rootObject,
proxyClazz,
rootProxy);
if (!peo) {
// Ouch... Out of memory!
return nsnull;
}
//
// Add the new specific proxy to the head of the list of proxies hanging
// off of the rootProxy...
//
peo->mNext = rootProxy->mNext;
rootProxy->mNext = peo;
NS_ADDREF(peo);
return peo;
}
nsProxyEventObject* nsProxyEventObject::LockedFind(REFNSIID aIID)
{
if(aIID.Equals(mClass->GetProxiedIID())) {
return this;
}
if(aIID.Equals(NS_GET_IID(nsISupports))) {
return this;
}
nsProxyEventObject* cur = (mRoot ? mRoot : mNext);
while(cur) {
if(aIID.Equals(cur->GetClass()->GetProxiedIID())) {
return cur;
}
cur = cur->mNext;
}
return nsnull;
}
nsProxyEventObject::nsProxyEventObject()
: mNext(nsnull)
{
NS_WARNING("This constructor should never be called");
}
nsProxyEventObject::nsProxyEventObject(nsIEventTarget *target,
PRInt32 proxyType,
nsISupports* aObj,
nsISupports* aRootObj,
nsProxyEventObject::nsProxyEventObject(nsProxyObject *aParent,
nsProxyEventClass* aClass,
nsProxyEventObject* root)
: mClass(aClass),
mRoot(root),
nsISomeInterface* aRealInterface)
: mRealInterface(aRealInterface),
mClass(aClass),
mProxyObject(aParent),
mNext(nsnull)
{
NS_IF_ADDREF(mRoot);
// XXX protect against OOM errors
mProxyObject = new nsProxyObject(target, proxyType, aObj, aRootObj);
#ifdef DEBUG_xpcom_proxy
DebugDump("Create", 0);
#endif
}
nsProxyEventObject::~nsProxyEventObject()
{
#ifdef DEBUG_xpcom_proxy
DebugDump("Delete", 0);
#endif
if (mRoot) {
//
// This proxy is not the root interface so it must be removed
// from the chain of proxies...
//
nsProxyEventObject* cur = mRoot;
while(cur) {
if(cur->mNext == this) {
cur->mNext = mNext;
mNext = nsnull;
break;
}
cur = cur->mNext;
}
NS_ASSERTION(cur, "failed to find wrapper in its own chain");
}
else {
//
// This proxy is for the root interface. Each proxy in the chain
// has a strong reference to the root... So, when its refcount goes
// to zero, it safe to remove it because no proxies are in its chain.
//
if (! nsProxyObjectManager::IsManagerShutdown()) {
nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance();
nsHashtable *realToProxyMap = manager->GetRealObjectToProxyObjectMap();
// This destructor is always called within the POM monitor.
// XXX assert this!
NS_ASSERTION(!mNext, "There are still proxies in the chain!");
if (realToProxyMap != nsnull) {
nsCOMPtr<nsISupports> rootTarget =
do_QueryInterface(mProxyObject->mTarget);
nsProxyEventKey key(mProxyObject->mRootObject, rootTarget,
mProxyObject->mProxyType);
#ifdef DEBUG_dougt
void* value =
#endif
realToProxyMap->Remove(&key);
#ifdef DEBUG_dougt
NS_ASSERTION(value, "failed to remove from realToProxyMap");
#endif
}
}
}
// I am worried about ordering.
// do not remove assignments.
mProxyObject = nsnull;
mClass = nsnull;
NS_IF_RELEASE(mRoot);
mProxyObject->LockedRemove(this);
}
//
@@ -481,13 +80,7 @@ NS_IMPL_THREADSAFE_ADDREF(nsProxyEventObject)
NS_IMETHODIMP_(nsrefcnt)
nsProxyEventObject::Release(void)
{
//
// Be pessimistic about whether the manager or even the monitor exist...
// This is to protect against shutdown issues where a proxy object could
// be destroyed after (or while) the Proxy Manager is being destroyed...
//
nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance();
nsAutoMonitor mon(manager ? manager->GetMonitor() : nsnull);
nsAutoMonitor mon(nsProxyObjectManager::GetInstance()->GetMonitor());
nsrefcnt count;
NS_PRECONDITION(0 != mRefCnt, "dup release");
@@ -511,14 +104,14 @@ nsProxyEventObject::Release(void)
NS_IMETHODIMP
nsProxyEventObject::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if( aIID.Equals(GetIID()) )
if( aIID.Equals(GetClass()->GetProxiedIID()) )
{
*aInstancePtr = NS_STATIC_CAST(nsISupports*, this);
NS_ADDREF_THIS();
return NS_OK;
}
return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
return mProxyObject->QueryInterface(aIID, aInstancePtr);
}
//
@@ -528,34 +121,146 @@ nsProxyEventObject::QueryInterface(REFNSIID aIID, void** aInstancePtr)
NS_IMETHODIMP
nsProxyEventObject::GetInterfaceInfo(nsIInterfaceInfo** info)
{
NS_ENSURE_ARG_POINTER(info);
*info = mClass->GetInterfaceInfo();
NS_ASSERTION(*info, "proxy class without interface");
if (!*info) {
return NS_ERROR_UNEXPECTED;
}
NS_ADDREF(*info);
return NS_OK;
}
nsresult
nsProxyEventObject::convertMiniVariantToVariant(const nsXPTMethodInfo *methodInfo,
nsXPTCMiniVariant * params,
nsXPTCVariant **fullParam,
uint8 *outParamCount)
{
uint8 paramCount = methodInfo->GetParamCount();
*outParamCount = paramCount;
*fullParam = nsnull;
if (!paramCount) return NS_OK;
*fullParam = (nsXPTCVariant*)malloc(sizeof(nsXPTCVariant) * paramCount);
if (*fullParam == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
for (int i = 0; i < paramCount; i++)
{
const nsXPTParamInfo& paramInfo = methodInfo->GetParam(i);
if ((GetProxyType() & NS_PROXY_ASYNC) && paramInfo.IsDipper())
{
NS_WARNING("Async proxying of out parameters is not supported");
free(*fullParam);
return NS_ERROR_PROXY_INVALID_OUT_PARAMETER;
}
uint8 flags = paramInfo.IsOut() ? nsXPTCVariant::PTR_IS_DATA : 0;
(*fullParam)[i].Init(params[i], paramInfo.GetType(), flags);
}
return NS_OK;
}
class nsProxyThreadFilter : public nsIThreadEventFilter
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSITHREADEVENTFILTER
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsProxyThreadFilter, nsIThreadEventFilter)
NS_DEFINE_IID(kFilterIID, NS_PROXYEVENT_FILTER_IID);
NS_IMETHODIMP_(PRBool)
nsProxyThreadFilter::AcceptEvent(nsIRunnable *event)
{
PROXY_LOG(("PROXY(%p): filter event [%p]\n", this, event));
// If we encounter one of our proxy events that is for a synchronous method
// call, then we want to put it in our event queue for processing. Else,
// we want to allow the event to be dispatched to the thread's event queue
// for processing later once we complete the current sync method call.
nsRefPtr<nsProxyObjectCallInfo> poci;
event->QueryInterface(kFilterIID, getter_AddRefs(poci));
return poci && poci->IsSync();
}
NS_IMETHODIMP
nsProxyEventObject::CallMethod(PRUint16 methodIndex,
const nsXPTMethodInfo* info,
const nsXPTMethodInfo* methodInfo,
nsXPTCMiniVariant * params)
{
NS_ASSERTION(methodIndex > 2,
"Calling QI/AddRef/Release through CallMethod");
nsresult rv;
if (mProxyObject) {
rv = mProxyObject->Post(methodIndex,
(nsXPTMethodInfo*)info,
params,
mClass->GetInterfaceInfo());
} else {
rv = NS_ERROR_NULL_POINTER;
if (methodInfo->IsNotXPCOM())
return NS_ERROR_PROXY_INVALID_IN_PARAMETER;
nsXPTCVariant *fullParam;
uint8 paramCount;
rv = convertMiniVariantToVariant(methodInfo, params,
&fullParam, &paramCount);
if (NS_FAILED(rv))
return rv;
PRBool callDirectly = PR_FALSE;
if (GetProxyType() & NS_PROXY_SYNC &&
NS_SUCCEEDED(GetTarget()->IsOnCurrentThread(&callDirectly)) &&
callDirectly) {
// invoke directly using xptc
rv = XPTC_InvokeByIndex(mRealInterface, methodIndex,
paramCount, fullParam);
if (fullParam)
free(fullParam);
return rv;
}
nsRefPtr<nsProxyObjectCallInfo> proxyInfo =
new nsProxyObjectCallInfo(this, methodInfo, methodIndex,
fullParam, paramCount);
if (!proxyInfo)
return NS_ERROR_OUT_OF_MEMORY;
if (! (GetProxyType() & NS_PROXY_SYNC)) {
return GetTarget()->Dispatch(proxyInfo, NS_DISPATCH_NORMAL);
}
// Post synchronously
nsIThread *thread = NS_GetCurrentThread();
nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
NS_ENSURE_STATE(threadInt);
// Install thread filter to limit event processing only to
// nsProxyObjectCallInfo instances. XXX Add support for sequencing?
nsRefPtr<nsProxyThreadFilter> filter = new nsProxyThreadFilter();
if (!filter)
return NS_ERROR_OUT_OF_MEMORY;
threadInt->PushEventQueue(filter);
proxyInfo->SetCallersTarget(thread);
// Dispatch can fail if the thread is shutting down
rv = GetTarget()->Dispatch(proxyInfo, NS_DISPATCH_NORMAL);
if (NS_SUCCEEDED(rv)) {
while (!proxyInfo->GetCompleted()) {
if (!NS_ProcessNextEvent(thread)) {
rv = NS_ERROR_UNEXPECTED;
break;
}
}
} else {
NS_WARNING("Failed to dispatch nsProxyCallEvent");
}
threadInt->PopEventQueue();
PROXY_LOG(("PROXY(%p): PostAndWait exit [%p %x]\n", this, proxyInfo, rv));
return rv;
}

View File

@@ -39,128 +39,221 @@
#ifndef nsProxyEventPrivate_h__
#define nsProxyEventPrivate_h__
#include "nscore.h"
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsHashtable.h"
#include "nsIEventTarget.h"
#include "nsIInterfaceInfo.h"
#include "nsIProxyObjectManager.h"
#include "xptcall.h" // defines nsXPTCVariant
#include "nsProxyEvent.h"
#include "nsIProxyObjectManager.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "nsClassHashtable.h"
#include "nsHashtable.h"
#include "prmon.h"
#include "prlog.h"
class nsProxyEventObject;
class nsProxyEventClass;
#define NS_PROXYEVENT_CLASS_IID \
{ 0xeea90d42, \
0xb059, \
0x11d2, \
{0x91, 0x5e, 0xc1, 0x2b, 0x69, 0x6c, 0x93, 0x33}\
}
/**
* To make types clearer, we distinguish between a canonical nsISupports* and
* a proxied interface pointer which represents an arbitrary interface known
* at runtime.
*/
typedef nsISupports nsISomeInterface;
#define NS_PROXYEVENT_IDENTITY_CLASS_IID \
#define NS_PROXYOBJECT_CLASS_IID \
{ 0xeea90d45, 0xb059, 0x11d2, \
{ 0x91, 0x5e, 0xc1, 0x2b, 0x69, 0x6c, 0x93, 0x33 } }
// This IID is used to filter runnables during synchronous event handling.
// The returned pointer is type nsProxyObjectCallInfo
#define NS_PROXYEVENT_OBJECT_IID \
#define NS_PROXYEVENT_FILTER_IID \
{ 0xec373590, 0x9164, 0x11d3, \
{0x8c, 0x73, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} }
class nsProxyEventClass : public nsISupports
/**
* An object representing an IID and its associated interfaceinfo. Instances
* of this class are obtained via nsProxyObjectManager::GetClass.
*/
class nsProxyEventClass
{
public:
NS_DECL_ISUPPORTS
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_CLASS_IID)
static nsProxyEventClass* GetNewOrUsedClass(REFNSIID aIID);
NS_IMETHOD DelegatedQueryInterface( nsProxyEventObject* self,
REFNSIID aIID,
void** aInstancePtr);
nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
const nsIID& GetProxiedIID() const {return mIID; }
protected:
nsProxyEventClass();
nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo);
private:
~nsProxyEventClass();
nsIID mIID;
nsCOMPtr<nsIInterfaceInfo> mInfo;
uint32* mDescriptors;
nsresult CallQueryInterfaceOnProxy(nsProxyEventObject* self,
REFNSIID aIID,
nsProxyEventObject** aInstancePtr);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyEventClass, NS_PROXYEVENT_CLASS_IID)
/**
* A class which provides the XPCOM identity for a proxied object.
* Instances of this class are obtained from the POM, and are uniquely
* hashed on a proxytype/eventtarget/realobject key.
*/
class nsProxyObject : public nsISupports
{
public:
NS_DECL_ISUPPORTS
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYOBJECT_CLASS_IID)
nsProxyObject(nsIEventTarget *destQueue, PRInt32 proxyType,
nsISupports *realObject);
nsISupports* GetRealObject() const { return mRealObject; }
nsIEventTarget* GetTarget() const { return mTarget; }
PRInt32 GetProxyType() const { return mProxyType; }
nsresult LockedFind(REFNSIID iid, void **aResult);
void LockedRemove(nsProxyEventObject* aObject);
friend class nsProxyObjectManager;
private:
~nsProxyObject();
PRInt32 mProxyType;
nsCOMPtr<nsIEventTarget> mTarget; /* event target */
nsCOMPtr<nsISupports> mRealObject; /* the non-proxy object that this object is proxying
This is a strong ref. */
nsProxyEventObject *mFirst;
class nsProxyObjectDestructorEvent : public nsRunnable
{
nsProxyObjectDestructorEvent(nsProxyObject *doomed) :
mDoomed(doomed)
{}
NS_DECL_NSIRUNNABLE
friend class nsProxyObject;
private:
nsProxyObject *mDoomed;
};
friend nsProxyObjectDestructorEvent;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyObject, NS_PROXYOBJECT_CLASS_IID)
/**
* Object representing a single interface implemented on a proxied object.
* This object is maintained in a singly-linked list from the associated
* "parent" nsProxyObject.
*/
class nsProxyEventObject : public nsXPTCStubBase
{
public:
NS_DECL_ISUPPORTS
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_OBJECT_IID)
static nsProxyEventObject* GetNewOrUsedProxy(nsIEventTarget *target,
PRInt32 proxyType,
nsISupports *aObj,
REFNSIID aIID);
NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo** info);
// call this method and return result
NS_IMETHOD CallMethod(PRUint16 methodIndex, const nsXPTMethodInfo* info, nsXPTCMiniVariant* params);
nsProxyEventClass* GetClass() const { return mClass; }
nsISomeInterface* GetProxiedInterface() const { return mRealInterface; }
nsIEventTarget* GetTarget() const { return mProxyObject->GetTarget(); }
PRInt32 GetProxyType() const { return mProxyObject->GetProxyType(); }
nsProxyEventClass* GetClass() const { return mClass; }
nsIEventTarget* GetTarget() const { return (mProxyObject ? mProxyObject->GetTarget() : nsnull);}
nsISupports* GetRealObject() const { return (mProxyObject ? mProxyObject->GetRealObject(): nsnull);}
PRInt32 GetProxyType() const { return (mProxyObject ? mProxyObject->GetProxyType() : nsnull);}
nsresult convertMiniVariantToVariant(const nsXPTMethodInfo *methodInfo,
nsXPTCMiniVariant *params,
nsXPTCVariant **fullParam,
uint8 *outParamCount);
nsProxyEventObject();
nsProxyEventObject(nsIEventTarget *target,
PRInt32 proxyType,
nsISupports* aObj,
nsISupports* aRootObj, // result of QI(nsISupports)
nsProxyEventClass* aClass,
nsProxyEventObject* root);
nsProxyEventObject* LockedFind(REFNSIID aIID);
nsProxyEventObject(nsProxyObject *aParent,
nsProxyEventClass *aClass,
nsISomeInterface *aRealInterface);
#ifdef DEBUG_xpcom_proxy
void DebugDump(const char * message, PRUint32 hashKey);
#endif
friend class nsProxyObject;
private:
~nsProxyEventObject();
protected:
void LockedRemoveProxy();
nsCOMPtr<nsISomeInterface> mRealInterface;
nsProxyEventClass *mClass;
nsCOMPtr<nsProxyObject> mProxyObject;
protected:
nsCOMPtr<nsProxyEventClass> mClass;
nsRefPtr<nsProxyObject> mProxyObject;
// Owning reference...
nsProxyEventObject *mRoot;
// Weak reference...
nsProxyEventObject *mNext;
// Weak reference, maintained by the parent nsProxyObject
nsProxyEventObject *mNext;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyEventObject, NS_PROXYEVENT_OBJECT_IID)
#define NS_PROXYEVENT_IID \
{ /* 9a24dc5e-2b42-4a5a-aeca-37b8c8fd8ccd */ \
0x9a24dc5e, \
0x2b42, \
0x4a5a, \
{0xae, 0xca, 0x37, 0xb8, 0xc8, 0xfd, 0x8c, 0xcd} \
}
/**
* A class representing a particular proxied method call.
*/
class nsProxyObjectCallInfo : public nsRunnable
{
public:
NS_DECL_NSIRUNNABLE
NS_IMETHOD QueryInterface(REFNSIID aIID, void **aResult);
NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_IID)
nsProxyObjectCallInfo(nsProxyEventObject* owner,
const nsXPTMethodInfo *methodInfo,
PRUint32 methodIndex,
nsXPTCVariant* parameterList,
PRUint32 parameterCount);
~nsProxyObjectCallInfo();
PRUint32 GetMethodIndex() const { return mMethodIndex; }
nsXPTCVariant* GetParameterList() const { return mParameterList; }
PRUint32 GetParameterCount() const { return mParameterCount; }
nsresult GetResult() const { return mResult; }
PRBool GetCompleted();
void SetCompleted();
void PostCompleted();
void SetResult(nsresult rv) { mResult = rv; }
nsIEventTarget* GetCallersTarget();
void SetCallersTarget(nsIEventTarget* target);
PRBool IsSync() const
{
return mOwner->GetProxyType() & NS_PROXY_SYNC;
}
private:
nsresult mResult; /* this is the return result of the called function */
const nsXPTMethodInfo *mMethodInfo;
PRUint32 mMethodIndex; /* which method to be called? */
nsXPTCVariant *mParameterList; /* marshalled in parameter buffer */
PRUint32 mParameterCount; /* number of params */
PRInt32 mCompleted; /* is true when the method has been called. */
nsCOMPtr<nsIEventTarget> mCallersTarget; /* this is the dispatch target that we must post a message back to
when we are done invoking the method (only NS_PROXY_SYNC). */
nsRefPtr<nsProxyEventObject> mOwner; /* this is the strong referenced nsProxyObject */
void RefCountInInterfacePointers(PRBool addRef);
void CopyStrings(PRBool copy);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyObjectCallInfo, NS_PROXYEVENT_IID)
////////////////////////////////////////////////////////////////////////////////
// nsProxyObjectManager
@@ -180,18 +273,23 @@ public:
static PRBool IsManagerShutdown();
static void Shutdown();
nsHashtable* GetRealObjectToProxyObjectMap() { return &mProxyObjectMap;}
nsHashtable* GetIIDToProxyClassMap() { return &mProxyClassMap; }
nsresult GetClass(REFNSIID aIID, nsProxyEventClass **aResult);
void Remove(nsProxyObject* aProxy);
PRMonitor* GetMonitor() const { return mProxyCreationMonitor; }
#ifdef PR_LOGGING
static PRLogModuleInfo *sLog;
#endif
private:
~nsProxyObjectManager();
static nsProxyObjectManager* mInstance;
nsHashtable mProxyObjectMap;
nsHashtable mProxyClassMap;
nsClassHashtable<nsIDHashKey, nsProxyEventClass> mProxyClassMap;
PRMonitor *mProxyCreationMonitor;
};
@@ -203,4 +301,6 @@ private:
{0x91, 0x5e, 0xc1, 0x2b, 0x69, 0x6c, 0x93, 0x33} \
}
#define PROXY_LOG(args) PR_LOG(nsProxyObjectManager::sLog, PR_LOG_DEBUG, args)
#endif // nsProxyEventPrivate_h__

View File

@@ -46,18 +46,51 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsProxyEvent.h"
#include "nsIProxyObjectManager.h"
#include "nsProxyEventPrivate.h"
#include "nsThreadUtils.h"
#include "nsIComponentManager.h"
#include "nsIProxyObjectManager.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "nsIThread.h"
#include "nsAutoLock.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "xptiprivate.h"
#ifdef PR_LOGGING
PRLogModuleInfo *nsProxyObjectManager::sLog = PR_NewLogModule("xpcomproxy");
#endif
class nsProxyEventKey : public nsHashKey
{
public:
nsProxyEventKey(void* rootObjectKey, void* targetKey, PRInt32 proxyType)
: mRootObjectKey(rootObjectKey), mTargetKey(targetKey), mProxyType(proxyType) {
}
PRUint32 HashCode(void) const {
return NS_PTR_TO_INT32(mRootObjectKey) ^
NS_PTR_TO_INT32(mTargetKey) ^ mProxyType;
}
PRBool Equals(const nsHashKey *aKey) const {
const nsProxyEventKey* other = (const nsProxyEventKey*)aKey;
return mRootObjectKey == other->mRootObjectKey
&& mTargetKey == other->mTargetKey
&& mProxyType == other->mProxyType;
}
nsHashKey *Clone() const {
return new nsProxyEventKey(mRootObjectKey, mTargetKey, mProxyType);
}
protected:
void* mRootObjectKey;
void* mTargetKey;
PRInt32 mProxyType;
};
/////////////////////////////////////////////////////////////////////////
// nsProxyObjectManager
@@ -68,23 +101,15 @@ nsProxyObjectManager* nsProxyObjectManager::mInstance = nsnull;
NS_IMPL_THREADSAFE_ISUPPORTS1(nsProxyObjectManager, nsIProxyObjectManager)
nsProxyObjectManager::nsProxyObjectManager()
: mProxyObjectMap(256, PR_TRUE),
mProxyClassMap(256, PR_TRUE)
: mProxyObjectMap(256, PR_FALSE)
{
mProxyCreationMonitor = PR_NewMonitor();
}
static PRIntn
PurgeProxyClasses(nsHashKey *aKey, void *aData, void* closure)
{
nsProxyEventClass* ptr = NS_STATIC_CAST(nsProxyEventClass*, aData);
NS_RELEASE(ptr);
return kHashEnumerateNext;
mProxyClassMap.Init(256);
}
nsProxyObjectManager::~nsProxyObjectManager()
{
mProxyClassMap.Reset(PurgeProxyClasses, nsnull);
mProxyClassMap.Clear();
if (mProxyCreationMonitor)
PR_DestroyMonitor(mProxyCreationMonitor);
@@ -155,12 +180,76 @@ nsProxyObjectManager::GetProxyForObject(nsIEventTarget* aTarget,
return aObj->QueryInterface(aIID, aProxyObject);
}
// check to see if proxy is there or not.
*aProxyObject = nsProxyEventObject::GetNewOrUsedProxy(aTarget, proxyType,
aObj, aIID);
if (!*aProxyObject)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsISupports> realObj = do_QueryInterface(aObj);
// Make sure the object passed in is not a proxy; if it is, be nice and
// build the proxy for the real object.
nsCOMPtr<nsProxyObject> po = do_QueryInterface(aObj);
if (po) {
realObj = po->GetRealObject();
}
nsCOMPtr<nsISupports> realEQ = do_QueryInterface(aTarget);
nsProxyEventKey rootKey(realObj, realEQ, proxyType);
// Enter the Grand Monitor here.
nsAutoMonitor mon(mProxyCreationMonitor);
nsCOMPtr<nsProxyObject> root = (nsProxyObject*) mProxyObjectMap.Get(&rootKey);
if (!root) {
root = new nsProxyObject(aTarget, proxyType, realObj);
if (!root)
return NS_ERROR_OUT_OF_MEMORY;
mProxyObjectMap.Put(&rootKey, root);
}
return root->LockedFind(aIID, aProxyObject);
}
void
nsProxyObjectManager::Remove(nsProxyObject *aProxy)
{
nsCOMPtr<nsISupports> realEQ = do_QueryInterface(aProxy->GetTarget());
nsProxyEventKey rootKey(aProxy->GetRealObject(), realEQ, aProxy->GetProxyType());
nsAutoMonitor mon(mProxyCreationMonitor);
if (!mProxyObjectMap.Remove(&rootKey)) {
NS_ERROR("nsProxyObject not found in global hash.");
}
}
nsresult
nsProxyObjectManager::GetClass(REFNSIID aIID, nsProxyEventClass **aResult)
{
nsAutoMonitor mon(mProxyCreationMonitor);
nsProxyEventClass *pec;
mProxyClassMap.Get(aIID, &pec);
if (!pec) {
nsIInterfaceInfoManager *iim =
xptiInterfaceInfoManager::GetInterfaceInfoManagerNoAddRef();
if (!iim)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIInterfaceInfo> ii;
nsresult rv = iim->GetInfoForIID(&aIID, getter_AddRefs(ii));
if (NS_FAILED(rv))
return rv;
pec = new nsProxyEventClass(aIID, ii);
if (!pec)
return NS_ERROR_OUT_OF_MEMORY;
if (!mProxyClassMap.Put(aIID, pec)) {
delete pec;
return NS_ERROR_OUT_OF_MEMORY;
}
}
*aResult = pec;
return NS_OK;
}