Bug 675221 part B - remove the implementation of XPCOM proxies, sr=dougt

This commit is contained in:
Benjamin Smedberg 2012-01-11 11:28:21 -05:00
parent 51e3a00405
commit 00e37f7b43
19 changed files with 1 additions and 3694 deletions

View File

@ -56,7 +56,6 @@ DIRS = \
components \
threads \
reflect \
proxy \
system \
../chrome \
build \
@ -73,7 +72,7 @@ TOOL_DIRS += \
tests \
sample \
typelib/xpt/tests \
proxy/tests
$(NULL)
# Can't build internal xptcall tests that use symbols which are not exported.
#TOOL_DIRS += \

View File

@ -24,8 +24,6 @@
COMPONENT(ATOMSERVICE, nsAtomServiceConstructor)
COMPONENT(OBSERVERSERVICE, nsObserverService::Create)
COMPONENT(XPCOMPROXY, nsProxyObjectManager::Create)
COMPONENT(TIMER, nsTimerImplConstructor)
#define COMPONENT_SUPPORTS(TYPE, Type) \

View File

@ -78,9 +78,6 @@
#include "nsThreadManager.h"
#include "nsThreadPool.h"
#include "nsIProxyObjectManager.h"
#include "nsProxyEventPrivate.h" // access to the impl of nsProxyObjectManager for the generic factory registration.
#include "xptinfo.h"
#include "nsIInterfaceInfoManager.h"
#include "xptiprivate.h"
@ -273,7 +270,6 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsChromeRegistry,
NS_GENERIC_FACTORY_CONSTRUCTOR(nsChromeProtocolHandler)
#define NS_PERSISTENTPROPERTIES_CID NS_IPERSISTENTPROPERTIES_CID /* sigh */
#define NS_XPCOMPROXY_CID NS_PROXYEVENT_MANAGER_CID
static already_AddRefed<nsIFactory>
CreateINIParserFactory(const mozilla::Module& module,
@ -665,8 +661,6 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
nsComponentManagerImpl::gComponentManager->FreeServices();
}
nsProxyObjectManager::Shutdown();
// Release the directory service
NS_IF_RELEASE(nsDirectoryService::gService);

View File

@ -1,49 +0,0 @@
#
# ***** 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 *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xpcom
DIRS = public src
include $(topsrcdir)/config/rules.mk

View File

@ -1,57 +0,0 @@
#
# ***** 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xpcom
XPIDL_MODULE = proxyObject
EXPORTS = \
nsProxiedService.h \
$(NULL)
XPIDLSRCS = \
nsIProxyObjectManager.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -1,144 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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):
* Doug Turner <dougt@netscape.com> (Original Author)
* Dan Mosedale <dmose@netscape.com>
* Darin Fisher <darin@meer.net>
*
* 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 ***** */
#include "nsISupports.idl"
interface nsIEventTarget;
/**
* An interface for the proxy object manager.
*
* See http://www.mozilla.org/projects/xpcom/Proxies.html
*/
[scriptable, uuid(ee8ce1e3-0319-4bd9-8f70-7258b21c7733)]
interface nsIProxyObjectManager : nsISupports
{
/**
* Construct a proxy object that invokes methods on the real object
* synchronously (i.e., the calling thread is blocked until the real method
* call returns). This flag causes methods invoked on the proxy object to
* emmulate a real method call.
*
* For C++ callers, NS_PROXY_SYNC is a synonym for this flag.
*/
const long INVOKE_SYNC = 0x0001;
/**
* Construct a proxy object that invokes methods on the real object
* asynchronously (i.e., the calling thread does not wait for the real
* method call to occur).
*
* WARNING: do not pass pointers into the stack when using this flag.
*
* For C++ callers, NS_PROXY_ASYNC is a synonym for this flag.
*/
const long INVOKE_ASYNC = 0x0002;
/**
* Always create the proxy object even if for same thread as current thread.
*
* For C++ callers, NS_PROXY_ALWAYS is a synonym for this flag.
*/
const long FORCE_PROXY_CREATION = 0x0004;
/**
* Create a proxy for the given object. The proxy implements the specified
* interface, but when its methods are invoked, it causes the corresponding
* method on the actual object to be called via the designated event
* target. Typically, the event target identifies a thread where the
* method call should occur.
*
* @param target
* If target is null, then the current thread is used as the target.
* Otherwise, target identifies the nsIEventTarget from which proxy
* method calls should be executed.
* @param iid
* Identifies the interface being proxied. The given object must QI to
* this type.
* @param object
* The object being proxied.
* @param proxyType
* Specifies the type of proxy to construct. Either INVOKE_SYNC or
* INVOKE_ASYNC must be specified. FORCE_PROXY_CREATION may be bit-wise
* OR'd with either of those flags.
* @param result
* This param holds the resulting proxy object upon successful return.
*/
void getProxyForObject(in nsIEventTarget target,
in nsIIDRef iid,
in nsISupports object,
in PRInt32 proxyType,
[iid_is(iid),retval] out nsQIResult result);
};
%{C++
/**
* convenience macros
*/
#define NS_PROXY_SYNC nsIProxyObjectManager::INVOKE_SYNC
#define NS_PROXY_ASYNC nsIProxyObjectManager::INVOKE_ASYNC
#define NS_PROXY_ALWAYS nsIProxyObjectManager::FORCE_PROXY_CREATION
/**
* Pass this value as the target to {NS_}GetProxyForObject to specify the current
* thread as the target for the proxy object.
*/
#define NS_PROXY_TO_CURRENT_THREAD ((nsIEventTarget *) 0)
/**
* Pass this value as the target to NS_GetProxyForObject to specify the main
* thread as the target for the proxy object.
*/
#define NS_PROXY_TO_MAIN_THREAD ((nsIEventTarget *) 1)
#ifdef MOZILLA_INTERNAL_API
/**
* Helper function for code that already has a link-time dependency on the
* internal API (MOZILLA_INTERNAL_API) and needs to get proxies in a bunch of
* different places. This way, the caller isn't forced to get the proxy object
* manager themselves every single time, thus making the calling code more
* readable. The parameters are the same as for GetProxyForObject.
*/
extern nsresult
NS_GetProxyForObject(nsIEventTarget *target, REFNSIID iid, nsISupports* object,
PRInt32 proxyType, void** result);
#endif
%}

View File

@ -1,143 +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):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* 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 nsProxiedService_h__
#define nsProxiedService_h__
#include "nsServiceManagerUtils.h"
#include "nsIProxyObjectManager.h"
#include "nsXPCOMCIDInternal.h"
////////////////////////////////////////////////////////////////////////////////
// NS_WITH_PROXIED_SERVICE: macro to make using services that need to be proxied
// before using them easier.
// Now you can replace this:
// {
// nsresult rv;
// nsCOMPtr<nsIMyService> pIMyService =
// do_GetService(kMyServiceCID, &rv);
// if(NS_FAILED(rv))
// return;
// nsCOMPtr<nsIProxyObjectManager> pIProxyObjectManager =
// do_GetService(kProxyObjectManagerCID, &rv);
// if(NS_FAILED(rv))
// return;
// nsIMyService pIProxiedObject = NULL;
// rv = pIProxyObjectManager->GetProxyForObject(pIDispatchTarget,
// NS_GET_IID(nsIMyService),
// pIMyService, NS_PROXY_SYNC,
// (void**)&pIProxiedObject);
// pIProxiedObject->DoIt(...); // Executed on same thread as pIProxyQueue
// ...
// pIProxiedObject->Release(); // Must be done as not managed for you.
// }
// with this:
// {
// nsresult rv;
// NS_WITH_PROXIED_SERVICE(nsIMyService, pIMyService, kMyServiceCID,
// pIDispatchTarget, &rv);
// if(NS_FAILED(rv))
// return;
// pIMyService->DoIt(...); // Executed on the same thread as pIProxyQueue
// }
// and the automatic destructor will take care of releasing the service and
// the proxied object for you.
//
// Note that this macro requires you to link with the xpcom DLL to pick up the
// static member functions from nsServiceManager.
#define NS_WITH_PROXIED_SERVICE(T, var, cid, Q, rvAddr) \
nsProxiedService _serv##var(cid, NS_GET_IID(T), Q, false, rvAddr); \
T* var = (T*)(nsISupports*)_serv##var;
#define NS_WITH_ALWAYS_PROXIED_SERVICE(T, var, cid, Q, rvAddr) \
nsProxiedService _serv##var(cid, NS_GET_IID(T), Q, true, rvAddr); \
T* var = (T*)(nsISupports*)_serv##var;
////////////////////////////////////////////////////////////////////////////////
// nsProxiedService
////////////////////////////////////////////////////////////////////////////////
class NS_STACK_CLASS nsProxiedService
{
public:
nsProxiedService(const nsCID &aClass, const nsIID &aIID,
nsIEventTarget* aTarget, bool always, nsresult* rv)
{
nsCOMPtr<nsISupports> svc = do_GetService(aClass, rv);
if (NS_SUCCEEDED(*rv))
InitProxy(svc, aIID, aTarget, always, rv);
}
nsProxiedService(const char* aContractID, const nsIID &aIID,
nsIEventTarget* aTarget, bool always, nsresult* rv)
{
nsCOMPtr<nsISupports> svc = do_GetService(aContractID, rv);
if (NS_SUCCEEDED(*rv))
InitProxy(svc, aIID, aTarget, always, rv);
}
operator nsISupports*() const
{
return mProxiedService;
}
private:
void InitProxy(nsISupports *aObj, const nsIID &aIID,
nsIEventTarget* aTarget, bool always, nsresult*rv)
{
PRInt32 proxyType = NS_PROXY_SYNC;
if (always)
proxyType |= NS_PROXY_ALWAYS;
nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = do_GetService(NS_XPCOMPROXY_CONTRACTID, rv);
if (NS_FAILED(*rv))
return;
*rv = proxyObjMgr->GetProxyForObject(aTarget,
aIID,
aObj,
proxyType,
getter_AddRefs(mProxiedService));
}
nsCOMPtr<nsISupports> mProxiedService;
};
#endif // nsProxiedService_h__

View File

@ -1,66 +0,0 @@
#
# ***** 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xpcom
LIBRARY_NAME = xpcomproxy_s
MOZILLA_INTERNAL_API = 1
CPPSRCS = \
nsProxyEvent.cpp \
nsProxyEventClass.cpp \
nsProxyEventObject.cpp \
nsProxyObjectManager.cpp \
$(NULL)
DEFINES += -D_IMPL_NS_COM -DEXPORT_XPTC_API
LOCAL_INCLUDES += -I$(top_srcdir)/xpcom/threads/
# No shared lib; Force creation of static lib
FORCE_STATIC_LIB = 1
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES += -I$(srcdir)/../../reflect/xptinfo/src

View File

@ -1,513 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/*
*
* ***** 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 *****
*
* This Original Code has been modified by IBM Corporation.
* Modifications made by IBM described herein are
* Copyright (c) International Business Machines
* Corporation, 2000
*
* Modifications to Mozilla code or documentation
* identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2
*/
#include "nsProxyEventPrivate.h"
#include "nsProxyRelease.h"
#include "nsIProxyObjectManager.h"
#include "nsCRT.h"
#include "pratom.h"
#include "prmem.h"
#include "xptcall.h"
#include "nsXPCOMCID.h"
#include "nsServiceManagerUtils.h"
#include "nsIComponentManager.h"
#include "nsThreadUtils.h"
#include "nsEventQueue.h"
#include "nsMemory.h"
using namespace mozilla;
/**
* Map the nsAUTF8String, nsUTF8String classes to the nsACString and
* nsCString classes respectively for now. These defines need to be removed
* once Jag lands his nsUTF8String implementation.
*/
#define nsAUTF8String nsACString
#define nsUTF8String nsCString
class nsProxyCallCompletedEvent : public nsRunnable
{
public:
nsProxyCallCompletedEvent(nsProxyObjectCallInfo *info)
: mInfo(info)
{}
NS_DECL_NSIRUNNABLE
NS_IMETHOD QueryInterface(REFNSIID aIID, void **aResult);
private:
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);
}
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsProxyObject::nsProxyObjectDestructorEvent::Run()
{
delete mDoomed;
return NS_OK;
}
//-----------------------------------------------------------------------------
nsProxyObjectCallInfo::nsProxyObjectCallInfo(nsProxyEventObject* owner,
const XPTMethodDescriptor *methodInfo,
PRUint32 methodIndex,
nsXPTCVariant* parameterList,
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!");
RefCountInInterfacePointers(true);
if (mOwner->GetProxyType() & NS_PROXY_ASYNC)
CopyStrings(true);
}
nsProxyObjectCallInfo::~nsProxyObjectCallInfo()
{
RefCountInInterfacePointers(false);
if (mOwner->GetProxyType() & NS_PROXY_ASYNC)
CopyStrings(false);
mOwner = nsnull;
if (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 = NS_InvokeByIndex(mOwner->GetProxiedInterface(),
mMethodIndex,
mParameterCount,
mParameterList);
if (IsSync()) {
PostCompleted();
}
return NS_OK;
}
void
nsProxyObjectCallInfo::RefCountInInterfacePointers(bool addRef)
{
for (PRUint32 i = 0; i < mParameterCount; i++)
{
nsXPTParamInfo paramInfo = mMethodInfo->params[i];
if (paramInfo.GetType().IsInterfacePointer() )
{
nsISupports* anInterface = nsnull;
if (paramInfo.IsIn())
{
anInterface = ((nsISupports*)mParameterList[i].val.p);
if (anInterface)
{
if (addRef)
anInterface->AddRef();
else
anInterface->Release();
}
}
}
}
}
void
nsProxyObjectCallInfo::CopyStrings(bool copy)
{
for (PRUint32 i = 0; i < mParameterCount; i++)
{
const nsXPTParamInfo paramInfo = mMethodInfo->params[i];
if (paramInfo.IsIn())
{
const nsXPTType& type = paramInfo.GetType();
uint8 type_tag = type.TagPart();
void *ptr = mParameterList[i].val.p;
if (!ptr)
continue;
if (copy)
{
switch (type_tag)
{
case nsXPTType::T_CHAR_STR:
mParameterList[i].val.p =
PL_strdup((const char *)ptr);
break;
case nsXPTType::T_WCHAR_STR:
mParameterList[i].val.p =
nsCRT::strdup((const PRUnichar *)ptr);
break;
case nsXPTType::T_DOMSTRING:
case nsXPTType::T_ASTRING:
mParameterList[i].val.p =
new nsString(*((nsAString*) ptr));
break;
case nsXPTType::T_CSTRING:
mParameterList[i].val.p =
new nsCString(*((nsACString*) ptr));
break;
case nsXPTType::T_UTF8STRING:
mParameterList[i].val.p =
new nsUTF8String(*((nsAUTF8String*) ptr));
break;
default:
// Other types are ignored
break;
}
}
else
{
switch (type_tag)
{
case nsXPTType::T_CHAR_STR:
PL_strfree((char*) ptr);
break;
case nsXPTType::T_WCHAR_STR:
nsCRT::free((PRUnichar*)ptr);
break;
case nsXPTType::T_DOMSTRING:
case nsXPTType::T_ASTRING:
delete (nsString*) ptr;
break;
case nsXPTType::T_CSTRING:
delete (nsCString*) ptr;
break;
case nsXPTType::T_UTF8STRING:
delete (nsUTF8String*) ptr;
break;
default:
// Other types are ignored
break;
}
}
}
}
}
bool
nsProxyObjectCallInfo::GetCompleted()
{
return !!mCompleted;
}
void
nsProxyObjectCallInfo::SetCompleted()
{
PROXY_LOG(("PROXY(%p): SetCompleted\n", this));
PR_ATOMIC_SET(&mCompleted, 1);
}
void
nsProxyObjectCallInfo::PostCompleted()
{
PROXY_LOG(("PROXY(%p): PostCompleted\n", this));
if (mCallersTarget) {
nsCOMPtr<nsIRunnable> event =
new nsProxyCallCompletedEvent(this);
if (event &&
NS_SUCCEEDED(mCallersTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
return;
}
// OOM? caller does not have a target? This is an error!
NS_WARNING("Failed to dispatch nsProxyCallCompletedEvent");
SetCompleted();
}
nsIEventTarget*
nsProxyObjectCallInfo::GetCallersTarget()
{
return mCallersTarget;
}
void
nsProxyObjectCallInfo::SetCallersTarget(nsIEventTarget* target)
{
mCallersTarget = target;
}
nsProxyObject::nsProxyObject(nsIEventTarget *target, PRInt32 proxyType,
nsISupports *realObject) :
mProxyType(proxyType),
mTarget(target),
mRealObject(realObject),
mFirst(nsnull)
{
MOZ_COUNT_CTOR(nsProxyObject);
#ifdef DEBUG
nsCOMPtr<nsISupports> canonicalTarget = do_QueryInterface(target);
NS_ASSERTION(target == canonicalTarget,
"Non-canonical nsISupports passed to nsProxyObject constructor");
#endif
}
nsProxyObject::~nsProxyObject()
{
// Proxy the release of mRealObject to protect against it being deleted on
// the wrong thread.
nsISupports *doomed = nsnull;
mRealObject.swap(doomed);
NS_ProxyRelease(mTarget, doomed);
MOZ_COUNT_DTOR(nsProxyObject);
}
NS_IMETHODIMP_(nsrefcnt)
nsProxyObject::AddRef()
{
MutexAutoLock lock(nsProxyObjectManager::GetInstance()->GetLock());
return LockedAddRef();
}
NS_IMETHODIMP_(nsrefcnt)
nsProxyObject::Release()
{
MutexAutoLock lock(nsProxyObjectManager::GetInstance()->GetLock());
return LockedRelease();
}
nsrefcnt
nsProxyObject::LockedAddRef()
{
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsProxyObject", sizeof(nsProxyObject));
return mRefCnt;
}
nsrefcnt
nsProxyObject::LockedRelease()
{
NS_PRECONDITION(0 != mRefCnt, "dup release");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsProxyObject");
if (mRefCnt)
return mRefCnt;
nsProxyObjectManager *pom = nsProxyObjectManager::GetInstance();
pom->LockedRemove(this);
MutexAutoUnlock unlock(pom->GetLock());
delete this;
return 0;
}
NS_IMETHODIMP
nsProxyObject::QueryInterface(REFNSIID aIID, void **aResult)
{
if (aIID.Equals(GetIID())) {
*aResult = this;
AddRef();
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsISupports))) {
*aResult = static_cast<nsISupports*>(this);
AddRef();
return NS_OK;
}
nsProxyObjectManager *pom = nsProxyObjectManager::GetInstance();
NS_ASSERTION(pom, "Deleting a proxy without a global proxy-object-manager.");
MutexAutoLock lock(pom->GetLock());
return LockedFind(aIID, aResult);
}
nsresult
nsProxyObject::LockedFind(REFNSIID aIID, void **aResult)
{
// This method is only called when the global lock is held.
#ifdef DEBUG
nsProxyObjectManager::GetInstance()->GetLock().AssertCurrentThreadOwns();
#endif
nsProxyEventObject *peo;
for (peo = mFirst; peo; peo = peo->mNext) {
if (peo->GetClass()->GetProxiedIID().Equals(aIID)) {
*aResult = static_cast<nsISupports*>(peo->mXPTCStub);
peo->LockedAddRef();
return NS_OK;
}
}
nsProxyEventObject *newpeo;
// Both GetClass and QueryInterface call out to XPCOM, so we unlock for them
nsProxyObjectManager* pom = nsProxyObjectManager::GetInstance();
{
MutexAutoUnlock unlock(pom->GetLock());
nsProxyEventClass *pec;
nsresult rv = pom->GetClass(aIID, &pec);
if (NS_FAILED(rv))
return rv;
nsISomeInterface* newInterface;
rv = mRealObject->QueryInterface(aIID, (void**) &newInterface);
if (NS_FAILED(rv))
return rv;
newpeo = new nsProxyEventObject(this, pec,
already_AddRefed<nsISomeInterface>(newInterface), &rv);
if (!newpeo) {
NS_RELEASE(newInterface);
return NS_ERROR_OUT_OF_MEMORY;
}
if (NS_FAILED(rv)) {
delete newpeo;
return rv;
}
}
// Now that we're locked again, check for races by repeating the
// linked-list check.
for (peo = mFirst; peo; peo = peo->mNext) {
if (peo->GetClass()->GetProxiedIID().Equals(aIID)) {
// Best to AddRef for our caller before unlocking.
peo->LockedAddRef();
{
// Deleting an nsProxyEventObject can call Release on an
// nsProxyObject, which can only happen when not holding
// the lock.
MutexAutoUnlock unlock(pom->GetLock());
delete newpeo;
}
*aResult = static_cast<nsISupports*>(peo->mXPTCStub);
return NS_OK;
}
}
newpeo->mNext = mFirst;
mFirst = newpeo;
newpeo->LockedAddRef();
*aResult = static_cast<nsISupports*>(newpeo->mXPTCStub);
return NS_OK;
}
void
nsProxyObject::LockedRemove(nsProxyEventObject *peo)
{
nsProxyEventObject **i;
for (i = &mFirst; *i; i = &((*i)->mNext)) {
if (*i == peo) {
*i = peo->mNext;
return;
}
}
NS_ERROR("Didn't find nsProxyEventObject in nsProxyObject chain!");
}

View File

@ -1,88 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim:set ts=4 sw=4 sts=4 ci et: */
/* ***** 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):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* 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 ***** */
#include "nsProxyEventPrivate.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "nsMemory.h"
#include "nsHashtable.h"
#include "xptcall.h"
// LIFETIME_CACHE will cache class for the entire cyle of the application.
#define LIFETIME_CACHE
static uint32 zero_methods_descriptor;
//////////////////////////////////////////////////////////////////////////////////////////////////
// nsProxyEventClass
//////////////////////////////////////////////////////////////////////////////////////////////////
nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo)
: mIID(aIID),
mInfo(aInfo),
mDescriptors(NULL)
{
uint16 methodCount;
if(NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount)))
{
if(methodCount)
{
int wordCount = (methodCount/32)+1;
if(NULL != (mDescriptors = new uint32[wordCount]))
{
memset(mDescriptors, 0, wordCount * sizeof(uint32));
}
}
else
{
mDescriptors = &zero_methods_descriptor;
}
}
}
nsProxyEventClass::~nsProxyEventClass()
{
if (mDescriptors && mDescriptors != &zero_methods_descriptor)
delete [] mDescriptors;
}

View File

@ -1,274 +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):
* 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"),
* 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 ***** */
#include "prprf.h"
#include "prmem.h"
#include "nscore.h"
#include "nsProxyEventPrivate.h"
#include "nsIThreadInternal.h"
#include "nsServiceManagerUtils.h"
#include "nsHashtable.h"
#include "nsIInterfaceInfoManager.h"
#include "xptcall.h"
using namespace mozilla;
nsProxyEventObject::nsProxyEventObject(nsProxyObject *aParent,
nsProxyEventClass* aClass,
already_AddRefed<nsISomeInterface> aRealInterface,
nsresult *rv)
: mClass(aClass),
mProxyObject(aParent),
mRealInterface(aRealInterface),
mNext(nsnull)
{
*rv = InitStub(aClass->GetProxiedIID());
}
nsProxyEventObject::~nsProxyEventObject()
{
// This destructor must *not* be called within the POM lock
// XXX assert this!
// mRealInterface must be released before mProxyObject so that the last
// release of the proxied object is proxied to the correct thread.
// See bug 337492.
mRealInterface = nsnull;
}
//
// nsISupports implementation...
//
NS_IMETHODIMP_(nsrefcnt)
nsProxyEventObject::AddRef()
{
MutexAutoLock lock(nsProxyObjectManager::GetInstance()->GetLock());
return LockedAddRef();
}
nsrefcnt
nsProxyEventObject::LockedAddRef()
{
++mRefCnt;
NS_LOG_ADDREF(this, mRefCnt, "nsProxyEventObject", sizeof(nsProxyEventObject));
return mRefCnt;
}
NS_IMETHODIMP_(nsrefcnt)
nsProxyEventObject::Release(void)
{
{
MutexAutoLock lock(nsProxyObjectManager::GetInstance()->GetLock());
NS_PRECONDITION(0 != mRefCnt, "dup release");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "nsProxyEventObject");
if (mRefCnt)
return mRefCnt;
mProxyObject->LockedRemove(this);
}
// call the destructor outside of the lock so that we aren't holding the
// lock when we release the object
delete this;
return 0;
}
NS_IMETHODIMP
nsProxyEventObject::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if( aIID.Equals(GetClass()->GetProxiedIID()) )
{
*aInstancePtr = static_cast<nsISupports*>(mXPTCStub);
NS_ADDREF_THIS();
return NS_OK;
}
return mProxyObject->QueryInterface(aIID, aInstancePtr);
}
//
// nsXPTCStubBase implementation...
//
nsresult
nsProxyEventObject::convertMiniVariantToVariant(const XPTMethodDescriptor *methodInfo,
nsXPTCMiniVariant * params,
nsXPTCVariant **fullParam,
uint8 *outParamCount)
{
uint8 paramCount = methodInfo->num_args;
*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->params[i];
if ((GetProxyType() & NS_PROXY_ASYNC) &&
(paramInfo.IsOut() || 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_(bool)
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 XPTMethodDescriptor* methodInfo,
nsXPTCMiniVariant * params)
{
NS_ASSERTION(methodIndex > 2,
"Calling QI/AddRef/Release through CallMethod");
nsresult rv;
if (XPT_MD_IS_NOTXPCOM(methodInfo->flags))
return NS_ERROR_PROXY_INVALID_IN_PARAMETER;
nsXPTCVariant *fullParam;
uint8 paramCount;
rv = convertMiniVariantToVariant(methodInfo, params,
&fullParam, &paramCount);
if (NS_FAILED(rv))
return rv;
bool callDirectly = false;
if (GetProxyType() & NS_PROXY_SYNC &&
NS_SUCCEEDED(GetTarget()->IsOnCurrentThread(&callDirectly)) &&
callDirectly) {
// invoke directly using xptc
rv = NS_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;
}
}
rv = proxyInfo->GetResult();
} else {
NS_WARNING("Failed to dispatch nsProxyCallEvent");
}
threadInt->PopEventQueue();
PROXY_LOG(("PROXY(%p): PostAndWait exit [%p %x]\n", this, proxyInfo.get(), rv));
return rv;
}

View File

@ -1,320 +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):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* 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 nsProxyEventPrivate_h__
#define nsProxyEventPrivate_h__
#include "nsISupports.h"
#include "nsIFactory.h"
#include "nsIEventTarget.h"
#include "nsIInterfaceInfo.h"
#include "nsIProxyObjectManager.h"
#include "nsXPTCUtils.h"
#include "mozilla/Mutex.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "nsClassHashtable.h"
#include "nsHashtable.h"
#include "prlog.h"
class nsProxyEventObject;
/**
* 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_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_FILTER_IID \
{ 0xec373590, 0x9164, 0x11d3, \
{0x8c, 0x73, 0x00, 0x00, 0x64, 0x65, 0x73, 0x74} }
/**
* An object representing an IID and its associated interfaceinfo. Instances
* of this class are obtained via nsProxyObjectManager::GetClass.
*/
class nsProxyEventClass
{
public:
nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
const nsIID& GetProxiedIID() const {return mIID; }
nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo);
~nsProxyEventClass();
nsIID mIID;
nsCOMPtr<nsIInterfaceInfo> mInfo;
uint32* mDescriptors;
};
/**
* 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; }
// these are the equivalents of AddRef/Release, but must be called
// while holding the global POM lock
nsrefcnt LockedAddRef();
nsrefcnt LockedRelease();
// LockedFind should be called holding the POM lock. It will
// temporarily unlock the lock during execution.
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 class 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 : protected nsAutoXPTCStub
{
public:
NS_DECL_ISUPPORTS
// call this method and return result
NS_IMETHOD CallMethod(PRUint16 methodIndex,
const XPTMethodDescriptor* 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(); }
nsresult convertMiniVariantToVariant(const XPTMethodDescriptor *methodInfo,
nsXPTCMiniVariant *params,
nsXPTCVariant **fullParam,
uint8 *outParamCount);
nsProxyEventObject(nsProxyObject *aParent,
nsProxyEventClass *aClass,
already_AddRefed<nsISomeInterface> aRealInterface,
nsresult *rv);
// AddRef, but you must be holding the global POM lock
nsrefcnt LockedAddRef();
friend class nsProxyObject;
private:
~nsProxyEventObject();
// Member ordering is important: See note in the destructor.
nsProxyEventClass *mClass;
nsCOMPtr<nsProxyObject> mProxyObject;
nsCOMPtr<nsISomeInterface> mRealInterface;
// Weak reference, maintained by the parent nsProxyObject
nsProxyEventObject *mNext;
};
#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 XPTMethodDescriptor *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; }
bool GetCompleted();
void SetCompleted();
void PostCompleted();
void SetResult(nsresult rv) { mResult = rv; }
nsIEventTarget* GetCallersTarget();
void SetCallersTarget(nsIEventTarget* target);
bool IsSync() const
{
return !!(mOwner->GetProxyType() & NS_PROXY_SYNC);
}
private:
nsresult mResult; /* this is the return result of the called function */
const XPTMethodDescriptor *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(bool addRef);
void CopyStrings(bool copy);
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsProxyObjectCallInfo, NS_PROXYEVENT_IID)
////////////////////////////////////////////////////////////////////////////////
// nsProxyObjectManager
////////////////////////////////////////////////////////////////////////////////
class nsProxyObjectManager: public nsIProxyObjectManager
{
typedef mozilla::Mutex Mutex;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROXYOBJECTMANAGER
static nsresult Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr);
nsProxyObjectManager();
static nsProxyObjectManager *GetInstance();
static bool IsManagerShutdown();
static void Shutdown();
nsresult GetClass(REFNSIID aIID, nsProxyEventClass **aResult);
void LockedRemove(nsProxyObject* aProxy);
Mutex& GetLock() { return mProxyCreationLock; }
#ifdef PR_LOGGING
static PRLogModuleInfo *sLog;
#endif
private:
~nsProxyObjectManager();
static nsProxyObjectManager* gInstance;
nsHashtable mProxyObjectMap;
nsClassHashtable<nsIDHashKey, nsProxyEventClass> mProxyClassMap;
Mutex mProxyCreationLock;
};
#define NS_XPCOMPROXY_CLASSNAME "nsProxyObjectManager"
#define NS_PROXYEVENT_MANAGER_CID \
{ 0xeea90d41, \
0xb059, \
0x11d2, \
{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

@ -1,351 +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):
* Doug Turner <dougt@netscape.com> (Original Author)
* Judson Valeski <valeski@netscape.com>
* Dan Matejka <danm@netscape.com>
* Scott Collins <scc@netscape.com>
* Heikki Toivonen <heiki@citec.fi>
* Patrick Beard <beard@netscape.com>
* Pierre Phaneuf <pp@ludusdesign.com>
* Warren Harris <warren@netscape.com>
* Chris Seawood <cls@seawood.org>
* Chris Waterson <waterson@netscape.com>
* Dan Mosedale <dmose@netscape.com>
*
* 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 ***** */
#include "nsProxyEventPrivate.h"
#include "nsIComponentManager.h"
#include "nsIProxyObjectManager.h"
#include "nsIServiceManager.h"
#include "nsIThread.h"
#include "nsCOMPtr.h"
#include "nsThreadUtils.h"
#include "xptiprivate.h"
using namespace mozilla;
#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;
}
bool 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
/////////////////////////////////////////////////////////////////////////
nsProxyObjectManager* nsProxyObjectManager::gInstance = nsnull;
NS_IMPL_QUERY_INTERFACE1(nsProxyObjectManager, nsIProxyObjectManager)
NS_IMETHODIMP_(nsrefcnt)
nsProxyObjectManager::AddRef()
{
return 2;
}
NS_IMETHODIMP_(nsrefcnt)
nsProxyObjectManager::Release()
{
return 1;
}
nsProxyObjectManager::nsProxyObjectManager()
: mProxyObjectMap(256, false)
, mProxyCreationLock("nsProxyObjectManager.mProxyCreationLock")
{
mProxyClassMap.Init(256);
}
nsProxyObjectManager::~nsProxyObjectManager()
{
mProxyClassMap.Clear();
nsProxyObjectManager::gInstance = nsnull;
}
bool
nsProxyObjectManager::IsManagerShutdown()
{
return gInstance == nsnull;
}
nsProxyObjectManager *
nsProxyObjectManager::GetInstance()
{
if (!gInstance)
gInstance = new nsProxyObjectManager();
return gInstance;
}
void
nsProxyObjectManager::Shutdown()
{
delete gInstance;
NS_ASSERTION(!gInstance, "Destructor didn't null gInstance?");
}
nsresult
nsProxyObjectManager::Create(nsISupports* outer, const nsIID& aIID,
void* *aInstancePtr)
{
nsProxyObjectManager *proxyObjectManager = GetInstance();
if (!proxyObjectManager)
return NS_ERROR_OUT_OF_MEMORY;
return proxyObjectManager->QueryInterface(aIID, aInstancePtr);
}
class nsProxyLockedRefPtr
{
public:
nsProxyLockedRefPtr(nsProxyObject* aPtr) :
mProxyObject(aPtr)
{
if (mProxyObject)
mProxyObject->LockedAddRef();
}
~nsProxyLockedRefPtr()
{
if (mProxyObject)
mProxyObject->LockedRelease();
}
operator nsProxyObject*() const
{
return mProxyObject;
}
nsProxyObject* operator->() const
{
return mProxyObject;
}
private:
nsProxyObject *mProxyObject;
};
NS_IMETHODIMP
nsProxyObjectManager::GetProxyForObject(nsIEventTarget* aTarget,
REFNSIID aIID,
nsISupports* aObj,
PRInt32 proxyType,
void** aProxyObject)
{
NS_ENSURE_ARG_POINTER(aObj);
*aProxyObject = nsnull;
// handle special values
nsCOMPtr<nsIThread> thread;
if (aTarget == NS_PROXY_TO_CURRENT_THREAD) {
aTarget = NS_GetCurrentThread();
} else if (aTarget == NS_PROXY_TO_MAIN_THREAD) {
thread = do_GetMainThread();
aTarget = thread.get();
}
// check to see if the target is on our thread. If so, just return the
// real object.
if (!(proxyType & NS_PROXY_ASYNC) && !(proxyType & NS_PROXY_ALWAYS))
{
bool result;
aTarget->IsOnCurrentThread(&result);
if (result)
return aObj->QueryInterface(aIID, aProxyObject);
}
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);
{
MutexAutoLock lock(mProxyCreationLock);
nsProxyLockedRefPtr root =
(nsProxyObject*) mProxyObjectMap.Get(&rootKey);
if (root)
return root->LockedFind(aIID, aProxyObject);
}
// don't lock while creating the nsProxyObject
nsProxyObject *newRoot = new nsProxyObject(aTarget, proxyType, realObj);
if (!newRoot)
return NS_ERROR_OUT_OF_MEMORY;
// lock again, and check for a race putting into mProxyObjectMap
{
MutexAutoLock lock(mProxyCreationLock);
nsProxyLockedRefPtr root =
(nsProxyObject*) mProxyObjectMap.Get(&rootKey);
if (root) {
delete newRoot;
return root->LockedFind(aIID, aProxyObject);
}
mProxyObjectMap.Put(&rootKey, newRoot);
nsProxyLockedRefPtr kungFuDeathGrip(newRoot);
return newRoot->LockedFind(aIID, aProxyObject);
}
}
void
nsProxyObjectManager::LockedRemove(nsProxyObject *aProxy)
{
nsCOMPtr<nsISupports> realEQ = do_QueryInterface(aProxy->GetTarget());
nsProxyEventKey rootKey(aProxy->GetRealObject(), realEQ, aProxy->GetProxyType());
if (!mProxyObjectMap.Remove(&rootKey)) {
NS_ERROR("nsProxyObject not found in global hash.");
}
}
nsresult
nsProxyObjectManager::GetClass(REFNSIID aIID, nsProxyEventClass **aResult)
{
{
MutexAutoLock lock(mProxyCreationLock);
if (mProxyClassMap.Get(aIID, aResult)) {
NS_ASSERTION(*aResult, "Null data in mProxyClassMap");
return NS_OK;
}
}
nsIInterfaceInfoManager *iim =
xptiInterfaceInfoManager::GetSingleton();
if (!iim)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIInterfaceInfo> ii;
nsresult rv = iim->GetInfoForIID(&aIID, getter_AddRefs(ii));
if (NS_FAILED(rv))
return rv;
nsProxyEventClass *pec = new nsProxyEventClass(aIID, ii);
if (!pec)
return NS_ERROR_OUT_OF_MEMORY;
// Re-lock to put this class into our map. Before putting, check to see
// if another thread raced to put before us
MutexAutoLock lock(mProxyCreationLock);
if (mProxyClassMap.Get(aIID, aResult)) {
NS_ASSERTION(*aResult, "Null data in mProxyClassMap");
delete pec;
return NS_OK;
}
if (!mProxyClassMap.Put(aIID, pec)) {
delete pec;
return NS_ERROR_OUT_OF_MEMORY;
}
*aResult = pec;
return NS_OK;
}
/**
* Helper function for code that already has a link-time dependency on
* libxpcom and needs to get proxies in a bunch of different places.
* This way, the caller isn't forced to get the proxy object manager
* themselves every single time, thus making the calling code more
* readable.
*/
nsresult
NS_GetProxyForObject(nsIEventTarget *target,
REFNSIID aIID,
nsISupports* aObj,
PRInt32 proxyType,
void** aProxyObject)
{
static NS_DEFINE_CID(proxyObjMgrCID, NS_PROXYEVENT_MANAGER_CID);
nsresult rv;
// get the proxy object manager
//
nsCOMPtr<nsIProxyObjectManager> proxyObjMgr =
do_GetService(proxyObjMgrCID, &rv);
if (NS_FAILED(rv))
return rv;
// and try to get the proxy object
//
return proxyObjMgr->GetProxyForObject(target, aIID, aObj,
proxyType, aProxyObject);
}

View File

@ -1,62 +0,0 @@
#
# ***** 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xpcom_tests
XPIDL_MODULE = proxytest
CPPSRCS = proxytests.cpp \
proxy-create-threadsafety.cpp \
$(NULL)
XPIDLSRCS = nsITestProxy.idl
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX))
LIBS = \
$(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \
$(XPCOM_LIBS) \
$(NSPR_LIBS) \
$(MOZ_JS_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -1,9 +0,0 @@
#include "nsISupports.idl"
[uuid(1979e980-1cfd-11d3-915e-0000863011c4)]
interface nsITestProxy : nsISupports
{
long Test(in long p1, in long p2);
void Test2();
void Test3(in nsISupports p1, out nsISupports p2);
};

View File

@ -1,255 +0,0 @@
/* -*- Mode: C; tab-width: 8; 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
* Mark 'Mook' Yen <mook@songbirdnest.com>
* Portions created by the Initial Developer are Copyright (C) 2008
* 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 ***** */
#include <stdio.h>
#include "nsXPCOM.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIServiceManager.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nscore.h"
#include "nspr.h"
#include "nsITestProxy.h"
#include "nsISupportsPrimitives.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h"
#include "nsIRunnable.h"
#include "nsIProxyObjectManager.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsISupportsUtils.h"
using namespace mozilla;
/*
A quick diagram of how this test works:
This tests for bug 400450, where the creation of a proxy event object
causes a deadlock. We use a counter and a monitor to make things
be more deterministic.
The leftmost column marks the thread that is running.
M Create two threads
1 Get a proxy
1 proxy event object created
1 (Proxy object QIs the target)
1 Lock [*]
2 Get a proxy
2 proxy event object created
2 (Proxy object calls QI)
2 Unlock [*]
2 proxy event object stored
2 proxy obtained
1 proxy event object released
*/
/***************************************************************************/
/* ProxyTest */
/***************************************************************************/
class ProxyTest : public nsIRunnable,
public nsITestProxy,
public nsISupportsPrimitive
{
public:
ProxyTest()
: mCounterLock("ProxyTest.mCounterLock")
, mEvilReentrantMonitor("ProxyTest.mEvilReentrantMonitor")
, mCounter(0)
{}
NS_IMETHOD Run()
{
nsresult rv;
nsCOMPtr<nsIProxyObjectManager> pom =
do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsPrimitive> prim;
rv = pom->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsISupportsPrimitive),
NS_ISUPPORTS_CAST(nsIRunnable*, this),
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(prim));
NS_ENSURE_SUCCESS(rv, rv);
/* we don't actually need to use the proxied object */
return NS_OK;
}
NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32 *_retval)
{
nsresult rv;
if (!NS_IsMainThread())
return NS_ERROR_UNEXPECTED;
/* note that we don't have an event queue... */
rv = NS_NewThread(getter_AddRefs(mThreadOne),
static_cast<nsIRunnable*>(this));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewThread(getter_AddRefs(mThreadTwo),
static_cast<nsIRunnable*>(this));
NS_ENSURE_SUCCESS(rv, rv);
rv = mThreadOne->Shutdown();
NS_ENSURE_SUCCESS(rv, rv);
rv = mThreadTwo->Shutdown();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHOD Test2(void)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD GetType(PRUint16 *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
NS_ASSERTION(aInstancePtr,
"QueryInterface requires a non-NULL destination!");
nsISupports* foundInterface;
if ( aIID.Equals(NS_GET_IID(nsIRunnable)) ) {
foundInterface = static_cast<nsIRunnable*>(this);
} else if ( aIID.Equals(NS_GET_IID(nsITestProxy)) ) {
foundInterface = static_cast<nsITestProxy*>(this);
} else if ( aIID.Equals(NS_GET_IID(nsISupports)) ) {
foundInterface = NS_ISUPPORTS_CAST(nsIRunnable*, this);
} else if ( aIID.Equals(NS_GET_IID(nsISupportsPrimitive)) ) {
{
MutexAutoLock counterLock(mCounterLock);
switch(mCounter) {
case 0:
++mCounter;
{
/* be evil here and hang */
MutexAutoUnlock counterUnlock(mCounterLock);
ReentrantMonitorAutoEnter evilReentrantMonitor(mEvilReentrantMonitor);
nsresult rv = evilReentrantMonitor.Wait();
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case 1:
++mCounter;
{
/* okay, we had our fun, un-hang */
MutexAutoUnlock counterUnlock(mCounterLock);
ReentrantMonitorAutoEnter evilReentrantMonitor(mEvilReentrantMonitor);
nsresult rv = evilReentrantMonitor.Notify();
NS_ENSURE_SUCCESS(rv, rv);
break;
}
default: {
/* nothing special here */
++mCounter;
}
}
++mCounter;
}
// remeber to admit to supporting this interface
foundInterface = static_cast<nsISupportsPrimitive*>(this);
} else {
foundInterface = nsnull;
}
nsresult status;
if (!foundInterface) {
status = NS_ERROR_NO_INTERFACE;
} else {
NS_ADDREF(foundInterface);
status = NS_OK;
}
*aInstancePtr = foundInterface;
return status;
}
NS_IMETHOD_(nsrefcnt) AddRef(void);
NS_IMETHOD_(nsrefcnt) Release(void);
protected:
nsAutoRefCnt mRefCnt;
NS_DECL_OWNINGTHREAD
private:
Mutex mCounterLock;
ReentrantMonitor mEvilReentrantMonitor;
PRInt32 mCounter;
nsCOMPtr<nsIThread> mThreadOne;
nsCOMPtr<nsIThread> mThreadTwo;
};
NS_IMPL_THREADSAFE_ADDREF(ProxyTest)
NS_IMPL_THREADSAFE_RELEASE(ProxyTest)
int
main(int argc, char **argv)
{
NS_InitXPCOM2(nsnull, nsnull, nsnull);
// Scope code so everything is destroyed before we run call NS_ShutdownXPCOM
{
nsCOMPtr<nsITestProxy> tester = new ProxyTest();
tester->Test(0, 0, nsnull);
}
NS_ShutdownXPCOM(nsnull);
return 0;
}

View File

@ -1,554 +0,0 @@
/* -*- Mode: C; tab-width: 8; 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):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* 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 ***** */
#include <stdio.h>
#include "nsXPCOM.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIServiceManager.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nscore.h"
#include "nspr.h"
#include "prmon.h"
#include "nsITestProxy.h"
#include "nsIRunnable.h"
#include "nsIProxyObjectManager.h"
#include "nsIThreadPool.h"
#include "nsXPCOMCIDInternal.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "prlog.h"
#ifdef PR_LOGGING
static PRLogModuleInfo *sLog = PR_NewLogModule("Test");
#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
#else
#define LOG(args) printf args
#endif
namespace proxytests {
static nsresult
GetThreadFromPRThread(PRThread *prthread, nsIThread **result)
{
LOG(("TEST: GetThreadFromPRThread [%p]\n", prthread));
nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
NS_ENSURE_STATE(tm);
return tm->GetThreadFromPRThread(prthread, result);
}
/***************************************************************************/
/* nsTestXPCFoo */
/***************************************************************************/
class nsTestXPCFoo : public nsITestProxy
{
NS_DECL_ISUPPORTS
NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval);
NS_IMETHOD Test2();
NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2);
nsTestXPCFoo();
};
nsTestXPCFoo::nsTestXPCFoo()
{
NS_ADDREF_THIS();
}
NS_IMPL_ISUPPORTS1(nsTestXPCFoo, nsITestProxy)
NS_IMETHODIMP nsTestXPCFoo::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval)
{
LOG(("TEST: Thread (%d) Test Called successfully! Party on...\n", p1));
*retval = p1+p2;
return NS_OK;
}
NS_IMETHODIMP nsTestXPCFoo::Test2()
{
LOG(("TEST: The quick brown netscape jumped over the old lazy ie..\n"));
return NS_OK;
}
NS_IMETHODIMP nsTestXPCFoo::Test3(nsISupports *p1, nsISupports **p2)
{
if (p1 != nsnull)
{
nsITestProxy *test;
p1->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test);
test->Test2();
PRInt32 a;
test->Test( 1, 2, &a);
LOG(("TEST: \n1+2=%d\n",a));
}
*p2 = new nsTestXPCFoo();
return NS_OK;
}
/***************************************************************************/
/* nsTestXPCFoo2 */
/***************************************************************************/
class nsTestXPCFoo2 : public nsITestProxy
{
NS_DECL_ISUPPORTS
NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval);
NS_IMETHOD Test2();
NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2);
nsTestXPCFoo2();
};
nsTestXPCFoo2::nsTestXPCFoo2()
{
NS_ADDREF_THIS();
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsTestXPCFoo2, nsITestProxy)
NS_IMETHODIMP nsTestXPCFoo2::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval)
{
LOG(("TEST: calling back to caller!\n"));
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
LOG(("TEST: ProxyObjectManager: %p \n", (void *) manager.get()));
PR_ASSERT(manager);
nsCOMPtr<nsIThread> thread;
GetThreadFromPRThread((PRThread *) p1, getter_AddRefs(thread));
NS_ENSURE_STATE(thread);
nsCOMPtr<nsITestProxy> proxyObject;
manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), this, NS_PROXY_SYNC, (void**)&proxyObject);
proxyObject->Test3(nsnull, nsnull);
LOG(("TEST: Deleting Proxy Object\n"));
return NS_OK;
}
NS_IMETHODIMP nsTestXPCFoo2::Test2()
{
LOG(("TEST: nsTestXPCFoo2::Test2() called\n"));
return NS_OK;
}
NS_IMETHODIMP nsTestXPCFoo2::Test3(nsISupports *p1, nsISupports **p2)
{
LOG(("TEST: Got called"));
return NS_OK;
}
#if 0
struct ArgsStruct {
nsIThread* thread;
PRInt32 threadNumber;
};
// This will create two objects both descendants of a single IID.
void TestCase_TwoClassesOneInterface(void *arg)
{
ArgsStruct *argsStruct = (ArgsStruct*) arg;
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
printf("ProxyObjectManager: %p \n", (void *) manager.get());
PR_ASSERT(manager);
nsITestProxy *proxyObject;
nsITestProxy *proxyObject2;
nsTestXPCFoo* foo = new nsTestXPCFoo();
nsTestXPCFoo2* foo2 = new nsTestXPCFoo2();
PR_ASSERT(foo);
PR_ASSERT(foo2);
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo2, NS_PROXY_SYNC, (void**)&proxyObject2);
if (proxyObject && proxyObject2)
{
// release ownership of the real object.
PRInt32 a;
nsresult rv;
PRInt32 threadNumber = argsStruct->threadNumber;
printf("Deleting real Object (%d)\n", threadNumber);
NS_RELEASE(foo);
printf("Deleting real Object 2 (%d)\n", threadNumber);
NS_RELEASE(foo2);
printf("Thread (%d) Prior to calling proxyObject->Test.\n", threadNumber);
rv = proxyObject->Test(threadNumber, 0, &a);
printf("Thread (%d) error: %d.\n", threadNumber, rv);
printf("Thread (%d) Prior to calling proxyObject->Test2.\n", threadNumber);
rv = proxyObject->Test2();
printf("Thread (%d) error: %d.\n", threadNumber, rv);
printf("Thread (%d) Prior to calling proxyObject2->Test2.\n", threadNumber);
rv = proxyObject2->Test2();
printf("Thread (%d) proxyObject2 error: %d.\n", threadNumber, rv);
printf("Deleting Proxy Object (%d)\n", threadNumber );
NS_RELEASE(proxyObject);
printf("Deleting Proxy Object 2 (%d)\n", threadNumber );
NS_RELEASE(proxyObject2);
}
PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters
}
#endif
void TestCase_NestedLoop(nsIThread *thread, PRInt32 index)
{
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
LOG(("TEST: ProxyObjectManager: %p\n", (void *) manager.get()));
PR_ASSERT(manager);
nsITestProxy *proxyObject;
nsTestXPCFoo2* foo = new nsTestXPCFoo2();
PR_ASSERT(foo);
manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
if (proxyObject)
{
// release ownership of the real object.
nsresult rv;
LOG(("TEST: Deleting real Object (%d)\n", index));
NS_RELEASE(foo);
PRInt32 retval;
LOG(("TEST: Getting EventThread...\n"));
//nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
PRThread *curThread = PR_GetCurrentThread();
if (curThread)
{
LOG(("TEST: Thread (%d) Prior to calling proxyObject->Test.\n", index));
rv = proxyObject->Test(NS_PTR_TO_INT32((void*)curThread), 0, &retval); // XXX broken on 64-bit arch
LOG(("TEST: Thread (%d) proxyObject error: %x.\n", index, rv));
LOG(("TEST: Deleting Proxy Object (%d)\n", index));
NS_RELEASE(proxyObject);
}
PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters
}
}
#if 0
void TestCase_nsISupports(void *arg)
{
ArgsStruct *argsStruct = (ArgsStruct*) arg;
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
PR_ASSERT(manager);
nsITestProxy *proxyObject;
nsTestXPCFoo* foo = new nsTestXPCFoo();
PR_ASSERT(foo);
manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject);
if (proxyObject != nsnull)
{
nsISupports *bISupports = nsnull, *cISupports = nsnull;
proxyObject->Test3(foo, &bISupports);
proxyObject->Test3(bISupports, &cISupports);
nsITestProxy *test;
bISupports->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test);
test->Test2();
NS_RELEASE(foo);
NS_RELEASE(proxyObject);
}
}
#endif
/***************************************************************************/
/* ProxyTest */
/***************************************************************************/
class ProxyTest : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
ProxyTest(PRThread *eventLoopThread, PRInt32 index)
: mEventLoopThread(eventLoopThread)
, mIndex(index)
{}
NS_IMETHOD Run()
{
//TestCase_TwoClassesOneInterface(arg);
//TestCase_nsISupports(arg);
nsCOMPtr<nsIThread> thread;
GetThreadFromPRThread(mEventLoopThread, getter_AddRefs(thread));
TestCase_NestedLoop(thread, mIndex);
return NS_OK;
}
private:
PRThread *mEventLoopThread;
PRInt32 mIndex;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(ProxyTest, nsIRunnable)
class TestSyncProxyToSelf : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Run()
{
LOG(("TEST: Verifing calling Proxy on eventQ thread.\n"));
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
nsITestProxy *proxyObject;
nsTestXPCFoo *foo = new nsTestXPCFoo();
NS_ENSURE_STATE(foo);
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
manager->GetProxyForObject(thread,
NS_GET_IID(nsITestProxy), foo,
NS_PROXY_SYNC, (void**)&proxyObject);
PRInt32 a;
proxyObject->Test(1, 2, &a);
proxyObject->Test2();
NS_RELEASE(proxyObject);
delete foo;
LOG(("TEST: End of Verification calling Proxy on eventQ thread.\n"));
return NS_OK;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(TestSyncProxyToSelf, nsIRunnable)
//---------------------------------------------------------------------------
// Test to make sure we can call methods on a "main thread only" object from
// a background thread.
class MainThreadOnly : public nsIRunnable {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD Run() {
NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread");
*mNumRuns -= 1;
return NS_OK;
}
MainThreadOnly(PRUint32 *numRuns) : mNumRuns(numRuns) {}
~MainThreadOnly() {
NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread");
}
bool IsDone() { return mNumRuns == 0; }
private:
PRUint32 *mNumRuns;
};
NS_IMPL_ISUPPORTS1(MainThreadOnly, nsIRunnable) // not threadsafe!
static nsresult
RunApartmentTest()
{
LOG(("RunApartmentTest: start\n"));
const PRUint32 numDispatched = 160;
PRUint32 numCompleted = 0;
nsCOMPtr<nsIRunnable> obj = new MainThreadOnly(&numCompleted);
nsCOMPtr<nsIProxyObjectManager> manager =
do_GetService(NS_XPCOMPROXY_CONTRACTID);
nsCOMPtr<nsIRunnable> objProxy;
manager->GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD,
NS_GET_IID(nsIRunnable),
obj,
NS_PROXY_ASYNC,
getter_AddRefs(objProxy));
nsCOMPtr<nsIThread> thread;
NS_NewThread(getter_AddRefs(thread));
obj = nsnull;
nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
pool->SetThreadLimit(8);
for (PRUint32 i = 0; i < numDispatched; ++i)
pool->Dispatch(objProxy, NS_DISPATCH_NORMAL);
objProxy = nsnull;
nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
while (numCompleted < numDispatched) {
NS_ProcessNextEvent(curThread);
}
pool->Shutdown();
LOG(("RunApartmentTest: end\n"));
return NS_OK;
}
} // namespace
using namespace proxytests;
int
main(int argc, char **argv)
{
int numberOfThreads = 1;
if (argc > 1)
numberOfThreads = atoi(argv[1]);
NS_InitXPCOM2(nsnull, nsnull, nsnull);
// Scope code so everything is destroyed before we run call NS_ShutdownXPCOM
{
RunApartmentTest();
nsCOMPtr<nsIThread> eventLoopThread;
NS_NewThread(getter_AddRefs(eventLoopThread));
nsCOMPtr<nsIRunnable> test = new TestSyncProxyToSelf();
eventLoopThread->Dispatch(test, NS_DISPATCH_NORMAL);
PRThread *eventLoopPRThread;
eventLoopThread->GetPRThread(&eventLoopPRThread);
PR_ASSERT(eventLoopPRThread);
LOG(("TEST: Spawn Threads:\n"));
nsCOMArray<nsIThread> threads;
for (PRInt32 spawn = 0; spawn < numberOfThreads; spawn++)
{
test = new ProxyTest(eventLoopPRThread, spawn);
nsCOMPtr<nsIThread> thread;
NS_NewThread(getter_AddRefs(thread), test);
threads.AppendObject(thread);
LOG(("TEST: \tThread (%d) spawned\n", spawn));
PR_Sleep( PR_MillisecondsToInterval(250) );
}
LOG(("TEST: All Threads Spawned.\n"));
LOG(("TEST: Wait for threads.\n"));
for (PRInt32 i = 0; i < numberOfThreads; i++)
{
LOG(("TEST: Thread (%d) Join...\n", i));
nsresult rv = threads[i]->Shutdown();
LOG(("TEST: Thread (%d) Joined. (error: %x).\n", i, rv));
}
LOG(("TEST: Shutting down event loop thread\n"));
eventLoopThread->Shutdown();
}
LOG(("TEST: Calling Cleanup.\n"));
NS_ShutdownXPCOM(nsnull);
LOG(("TEST: Return zero.\n"));
return 0;
}

View File

@ -114,7 +114,6 @@ endif
# TestExpirationTracker.cpp \
# TestPipes.cpp \
# TestPriorityQueue.cpp \
# TestProxies.cpp \
# TestStorageStream.cpp \
# TestStrings.cpp \
# TestSynchronization.cpp \

View File

@ -1,798 +0,0 @@
/* -*- Mode: C; tab-width: 8; 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 Proxy Test Code.
*
* The Initial Developer of the Original Code is
* Ben Turner <bent.mozilla@gmail.com>.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 ***** */
#include "TestHarness.h"
#include "nsIEventTarget.h"
#include "nsIProxyObjectManager.h"
#include "nsIRunnable.h"
#include "nsIThread.h"
#include "nsIThreadPool.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsXPCOMCIDInternal.h"
#include "prlog.h"
#include "mozilla/Mutex.h"
using namespace mozilla;
typedef nsresult(*TestFuncPtr)();
#define TEST_NAME "TestProxies"
#ifdef PR_LOGGING
static PRLogModuleInfo* sLog = PR_NewLogModule(TEST_NAME);
#endif
#define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args)
static nsIThread* gMainThread = nsnull;
static nsIThread* gTestThread = nsnull;
static nsresult
GetProxyForObject(nsIEventTarget* aTarget,
REFNSIID aIID,
nsISupports* aObj,
PRInt32 aProxyType,
void** aProxyObject)
{
nsresult rv;
nsCOMPtr<nsIProxyObjectManager> proxyObjMgr =
do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return proxyObjMgr->GetProxyForObject(aTarget, aIID, aObj, aProxyType,
aProxyObject);
}
class nsAutoTestThread
{
public:
nsAutoTestThread(nsIThread** aGlobal = nsnull)
: mGlobal(aGlobal)
{
nsCOMPtr<nsIThread> newThread;
nsresult rv = NS_NewThread(getter_AddRefs(newThread));
if (NS_FAILED(rv))
return;
rv = newThread->GetPRThread(&mNativeThread);
if (NS_FAILED(rv))
return;
LOG(("Created test thread [0x%p]", static_cast<void*>(mNativeThread)));
newThread.swap(mThread);
if (mGlobal)
*mGlobal = mThread;
}
~nsAutoTestThread()
{
if (mGlobal)
*mGlobal = nsnull;
#ifdef PR_LOGGING
void* nativeThread = static_cast<void*>(mNativeThread);
#endif
LOG(("Shutting down test thread [0x%p]", nativeThread));
mThread->Shutdown();
LOG(("Test thread successfully shut down [0x%p]", nativeThread));
}
operator nsIThread*() const
{
return mThread;
}
nsIThread* operator->() const
{
return mThread;
}
private:
nsIThread** mGlobal;
nsCOMPtr<nsIThread> mThread;
PRThread* mNativeThread;
};
class SimpleRunnable : public nsRunnable
{
public:
SimpleRunnable(const char* aType = "SimpleRunnable")
: mType(aType)
{ }
NS_IMETHOD Run()
{
LOG(("%s::Run() [0x%p]", mType,
static_cast<void*>(static_cast<nsISupports*>(this))));
return NS_OK;
}
private:
const char* mType;
};
class TestTargetThreadRunnable : public SimpleRunnable
{
public:
TestTargetThreadRunnable(nsIThread* aTarget)
: SimpleRunnable("TestTargetThreadRunnable"),
mTarget(aTarget)
{ }
NS_IMETHOD Run()
{
nsresult rv = SimpleRunnable::Run();
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
if (currentThread != mTarget) {
NS_ERROR("Proxy sent call to wrong thread!");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
private:
nsCOMPtr<nsIThread> mTarget;
};
class ChainedProxyRunnable : public SimpleRunnable
{
public:
ChainedProxyRunnable(nsIThread* aSecondTarget,
nsIThread* aThirdTarget = nsnull)
: SimpleRunnable("ChainedProxyRunnable"), mSecondTarget(aSecondTarget),
mThirdTarget(aThirdTarget)
{ }
NS_IMETHOD Run()
{
nsresult rv = SimpleRunnable::Run();
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<SimpleRunnable> runnable = mThirdTarget ?
new ChainedProxyRunnable(mThirdTarget) :
new SimpleRunnable();
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy;
rv = GetProxyForObject(mSecondTarget, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
private:
nsCOMPtr<nsIThread> mSecondTarget;
nsCOMPtr<nsIThread> mThirdTarget;
};
class IncrementingRunnable : public SimpleRunnable
{
public:
IncrementingRunnable(PRUint32* aCounter, Mutex* aLock = nsnull)
: SimpleRunnable("IncrementingRunnable"), mCounter(aCounter), mLock(aLock)
{ }
NS_IMETHOD Run()
{
nsresult rv = SimpleRunnable::Run();
NS_ENSURE_SUCCESS(rv, rv);
if (mLock)
mLock->Lock();
(*mCounter)++;
if (mLock)
mLock->Unlock();
return NS_OK;
}
private:
PRUint32* mCounter;
Mutex* mLock;
};
class NonThreadsafeRunnable : public nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NonThreadsafeRunnable(PRUint32* aCounter,
const char* aType = "NonThreadsafeRunnable")
: mCounter(aCounter),
mType(aType)
{ }
virtual ~NonThreadsafeRunnable()
{ };
NS_IMETHOD Run()
{
LOG(("%s::Run() [0x%p]", mType,
static_cast<void*>(static_cast<nsISupports*>(this))));
(*mCounter)++;
return NS_OK;
}
private:
PRUint32* mCounter;
const char* mType;
};
NS_IMPL_ISUPPORTS1(NonThreadsafeRunnable, nsIRunnable)
class MainThreadRunnable : public NonThreadsafeRunnable
{
public:
NS_DECL_ISUPPORTS_INHERITED
MainThreadRunnable(PRUint32* aCounter)
: NonThreadsafeRunnable(aCounter, "MainThreadRunnable")
{
if (!NS_IsMainThread()) {
NS_ERROR("Not running on the main thread!");
}
}
virtual ~MainThreadRunnable()
{
if (!NS_IsMainThread()) {
NS_ERROR("Not running on the main thread!");
}
}
NS_IMETHOD Run()
{
if (!NS_IsMainThread()) {
NS_ERROR("Not running on the main thread!");
return NS_ERROR_FAILURE;
}
nsresult rv = NonThreadsafeRunnable::Run();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
};
NS_IMPL_ISUPPORTS_INHERITED0(MainThreadRunnable, NonThreadsafeRunnable)
class ProxyGetter : public nsRunnable
{
public:
ProxyGetter(nsIRunnable* aRunnable, nsIRunnable** retval)
: mRunnable(aRunnable), _retval(retval)
{ }
NS_IMETHOD Run()
{
*_retval = nsnull;
if (NS_IsMainThread()) {
NS_ERROR("Shouldn't be running on the main thread!");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIRunnable> proxy;
nsresult rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable),
mRunnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
proxy.forget(_retval);
return NS_OK;
}
private:
nsIRunnable* mRunnable;
nsIRunnable** _retval;
};
class RunnableGetter : public nsRunnable
{
public:
RunnableGetter(PRUint32* aCounter, nsIRunnable** retval)
: mCounter(aCounter), _retval(retval)
{ }
NS_IMETHOD Run()
{
*_retval = nsnull;
if (NS_IsMainThread()) {
NS_ERROR("Shouldn't be running on the main thread!");
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIRunnable> runnable = new NonThreadsafeRunnable(mCounter);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
runnable.forget(_retval);
return NS_OK;
}
private:
PRUint32* mCounter;
nsIRunnable** _retval;
};
nsresult
TestTargetThread()
{
LOG(("--- Running TestTargetThread ---"));
nsRefPtr<TestTargetThreadRunnable> runnable =
new TestTargetThreadRunnable(gMainThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy;
nsresult rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
runnable = new TestTargetThreadRunnable(gTestThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS, getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
TestNonThreadsafeProxy()
{
LOG(("--- Running TestNonThreadsafeProxy 1 ---"));
// Make sure a non-threadsafe object and proxy to it (both created on the same
// thread) can be used on the same thread.
PRUint32 counter = 0;
nsCOMPtr<nsIRunnable> runnable(new MainThreadRunnable(&counter));
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy;
nsresult rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 otherCounter = 0; otherCounter < 5;) {
rv = gTestThread->Dispatch(proxy, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
}
// Make sure a non-threadsafe object and proxy to it (both created on the same
// thread) can be used on a different thread.
LOG(("--- Running TestNonThreadsafeProxy 2 ---"));
counter = 0;
runnable = new NonThreadsafeRunnable(&counter);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
runnable = nsnull;
for (PRUint32 otherCounter = 0; otherCounter < 5;) {
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
}
NS_ENSURE_TRUE(counter == 5, NS_ERROR_FAILURE);
// Make sure a non-threadsafe object and proxy to it (created on different
// threads) can be used by any thread.
LOG(("--- Running TestNonThreadsafeProxy 3 ---"));
counter = 0;
proxy = nsnull;
runnable = new MainThreadRunnable(&counter);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxyGetter =
new ProxyGetter(runnable, getter_AddRefs(proxy));
NS_ENSURE_TRUE(proxyGetter, NS_ERROR_OUT_OF_MEMORY);
rv = gTestThread->Dispatch(proxyGetter, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(proxy, NS_ERROR_FAILURE);
for (PRUint32 otherCounter = 0; otherCounter < 5;) {
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
}
// Make sure a non-threadsafe object (created on thread 1) and proxy to it
// (created on thread 2) can be used by thread 3.
LOG(("--- Running TestNonThreadsafeProxy 4 ---"));
counter = 0;
proxy = nsnull;
runnable = nsnull;
nsCOMPtr<nsIRunnable> runnableGetter =
new RunnableGetter(&counter, getter_AddRefs(runnable));
NS_ENSURE_TRUE(runnableGetter, NS_ERROR_OUT_OF_MEMORY);
rv = gTestThread->Dispatch(runnableGetter, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(runnable, NS_ERROR_FAILURE);
proxyGetter = new ProxyGetter(runnable, getter_AddRefs(proxy));
NS_ENSURE_TRUE(proxyGetter, NS_ERROR_OUT_OF_MEMORY);
nsAutoTestThread otherTestThread;
NS_ENSURE_TRUE(otherTestThread, NS_ERROR_FAILURE);
rv = otherTestThread->Dispatch(proxyGetter, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(proxy, NS_ERROR_FAILURE);
for (PRUint32 otherCounter = 0; otherCounter < 5;) {
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
}
return NS_OK;
}
nsresult
TestChainedProxy()
{
LOG(("--- Running TestChainedProxy ---"));
nsRefPtr<ChainedProxyRunnable> runnable =
new ChainedProxyRunnable(gMainThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy;
nsresult rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
// This will do a test->main call
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
runnable = new ChainedProxyRunnable(gTestThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
// This will do a main->test call
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
runnable = new ChainedProxyRunnable(gMainThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
// This will do a main->main call
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
runnable = new ChainedProxyRunnable(gTestThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
// This will do a test->test call
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
runnable = new ChainedProxyRunnable(gMainThread, gTestThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
// This will do a test->main->test call
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
TestReleaseOfRealObjects()
{
LOG(("--- Running TestReleaseOfRealObjects ---"));
PRUint32 counter = 0, otherCounter = 0;
nsRefPtr<IncrementingRunnable> runnable(new IncrementingRunnable(&counter));
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy1;
nsresult rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy1));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRunnable> proxy2;
rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy2));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRunnable> proxy3;
rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy3));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_FALSE(proxy1 == proxy2, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(proxy2 == proxy3, NS_ERROR_FAILURE);
proxy3 = nsnull;
rv = proxy1->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
rv = proxy2->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
runnable = nsnull;
rv = proxy1->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
rv = proxy2->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
proxy1 = nsnull;
rv = proxy2->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
return NS_OK;
}
nsresult
TestCurrentThreadProxy()
{
LOG(("--- Running TestCurrentThreadProxy ---"));
PRUint32 counter = 0, otherCounter = 0;
nsRefPtr<IncrementingRunnable> runnable(new IncrementingRunnable(&counter));
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy1;
nsresult rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_SYNC,
getter_AddRefs(proxy1));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRunnable> proxy2;
rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy2));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_FALSE(proxy1 == proxy2, NS_ERROR_FAILURE);
nsCOMPtr<nsIRunnable> realRunnable(do_QueryInterface(runnable));
NS_ENSURE_TRUE(realRunnable, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(static_cast<void*>(realRunnable) == static_cast<void*>(runnable),
NS_ERROR_FAILURE);
rv = proxy1->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
rv = proxy2->Run();
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(counter == ++otherCounter, NS_ERROR_FAILURE);
return NS_OK;
}
nsresult
TestAsyncProxy()
{
LOG(("--- Running TestAsyncProxy ---"));
// Test async proxies to the current thread.
PRUint32 counter = 0;
nsRefPtr<SimpleRunnable> runnable(new IncrementingRunnable(&counter));
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIRunnable> proxy;
nsresult rv = GetProxyForObject(gMainThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_ASYNC,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
runnable = nsnull;
for (PRUint32 i = 0; i < 5; i++) {
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
}
while (counter < 5) {
rv = NS_ProcessPendingEvents(gMainThread, PR_SecondsToInterval(1));
NS_ENSURE_SUCCESS(rv, rv);
}
// Now test async proxies to another thread.
Mutex* counterLock = new Mutex("counterLock");
NS_ENSURE_TRUE(counterLock, NS_ERROR_OUT_OF_MEMORY);
counter = 0;
runnable = new IncrementingRunnable(&counter, counterLock);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable), runnable,
NS_PROXY_ASYNC, getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
for (PRUint32 i = 0; i < 5; i++) {
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
}
PRUint32 safeCounter = 0;
while (safeCounter < 5) {
rv = NS_ProcessPendingEvents(gMainThread, PR_SecondsToInterval(1));
NS_ENSURE_SUCCESS(rv, rv);
MutexAutoLock lock(*counterLock);
safeCounter = counter;
}
delete counterLock;
// Now test async proxies to another thread that create sync proxies to this
// thread.
runnable = new ChainedProxyRunnable(gMainThread);
NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
rv = GetProxyForObject(gTestThread, NS_GET_IID(nsIRunnable),
runnable, NS_PROXY_ASYNC,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
rv = proxy->Run();
NS_ENSURE_SUCCESS(rv, rv);
// That was async, so make sure to wait for all the events on the test thread
// to be processed before we return. This is easy to do with an empty sync
// event.
nsCOMPtr<nsIRunnable> flusher = new nsRunnable();
NS_ENSURE_TRUE(flusher, NS_ERROR_OUT_OF_MEMORY);
LOG(("Flushing events on test thread"));
rv = gTestThread->Dispatch(flusher, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("Flushing events completed"));
return NS_OK;
}
int main(int argc, char** argv)
{
ScopedXPCOM xpcom(TEST_NAME);
NS_ENSURE_FALSE(xpcom.failed(), 1);
nsCOMPtr<nsIThread> mainThread(do_GetMainThread());
NS_ENSURE_TRUE(mainThread, 1);
LOG(("Got main thread"));
gMainThread = mainThread;
nsAutoTestThread testThread(&gTestThread);
NS_ENSURE_TRUE(testThread, 1);
static TestFuncPtr testsToRun[] = {
TestTargetThread,
// TestNonThreadsafeProxy, /* Not currently supported! */
TestChainedProxy,
TestReleaseOfRealObjects,
TestCurrentThreadProxy,
TestAsyncProxy
};
static PRUint32 testCount = sizeof(testsToRun) / sizeof(testsToRun[0]);
for (PRUint32 i = 0; i < testCount; i++) {
nsresult rv = testsToRun[i]();
NS_ENSURE_SUCCESS(rv, 1);
}
LOG(("--- Finished all tests ---"));
return 0;
}