summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect')
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in52
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup0
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in53
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl79
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in66
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup0
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp4210
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h365
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in33
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js106
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp268
-rw-r--r--src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js66
12 files changed, 5298 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in
new file mode 100644
index 00000000..e6a89153
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.in
@@ -0,0 +1,52 @@
+# vim: noexpandtab ts=4 sw=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 IPC.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2002
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Darin Fisher <darin@netscape.com>
+#
+# 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 *****
+
+DEPTH = ../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = public src
+
+ifdef ENABLE_TESTS
+DIRS += test
+endif
+
+include $(topsrcdir)/config/rules.mk
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/Makefile.kup
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in
new file mode 100644
index 00000000..b2b3dbaa
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/Makefile.in
@@ -0,0 +1,53 @@
+# vim: noexpandtab ts=4 sw=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 IPC.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2002
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Darin Fisher <darin@netscape.com>
+#
+# 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 *****
+
+DEPTH = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipcd
+XPIDL_MODULE= ipcd_dconnect
+
+XPIDLSRCS = \
+ ipcIDConnectService.idl \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl
new file mode 100644
index 00000000..6ca16a2d
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/public/ipcIDConnectService.idl
@@ -0,0 +1,79 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * 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 "nsISupports.idl"
+
+/**
+ * This service provides methods similar to nsIComponentManager and
+ * nsIServiceManager. A ClientID parameter specifies the remote process
+ * in which the object should live.
+ *
+ * ipcIService can be used to determine the ClientID of a remote process.
+ *
+ * It is assumed that both processes have access to the same typelibs.
+ */
+[scriptable, uuid(fe07ed16-2710-4a1e-a4e2-81302b62bf0e)]
+interface ipcIDConnectService : nsISupports
+{
+ void createInstance(
+ in unsigned long aClientID,
+ in nsCIDRef aClass,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result
+ );
+
+ void createInstanceByContractID(
+ in unsigned long aClientID,
+ in string aContractID,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result
+ );
+
+ void getService(
+ in unsigned long aClientID,
+ in nsCIDRef aClass,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result
+ );
+
+ void getServiceByContractID(
+ in unsigned long aClientID,
+ in string aContractID,
+ in nsIIDRef aIID,
+ [iid_is(aIID),retval] out nsQIResult result
+ );
+};
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in
new file mode 100644
index 00000000..a9efaadf
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.in
@@ -0,0 +1,66 @@
+# vim: noexpandtab ts=4 sw=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 IPC.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2002
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+# Darin Fisher <darin@netscape.com>
+#
+# 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 *****
+
+DEPTH = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipcd
+LIBRARY_NAME = ipcddconnect_s
+FORCE_STATIC_LIB = 1
+MODULE_NAME = ipcd
+
+FORCE_USE_PIC = 1
+
+REQUIRES = \
+ xpcom \
+ string \
+ $(NULL)
+
+CPPSRCS = \
+ ipcDConnectService.cpp \
+ $(NULL)
+
+LOCAL_INCLUDES = \
+ -I$(srcdir)/../../../shared/src \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/Makefile.kup
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp
new file mode 100644
index 00000000..32565df2
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.cpp
@@ -0,0 +1,4210 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ * Dmitry A. Kuminov <dmik@innotek.de>
+ *
+ * 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 "ipcDConnectService.h"
+#include "ipcMessageWriter.h"
+#include "ipcMessageReader.h"
+#include "ipcLog.h"
+
+#include "nsIServiceManagerUtils.h"
+#include "nsIInterfaceInfo.h"
+#include "nsIInterfaceInfoManager.h"
+#include "nsIExceptionService.h"
+#include "nsString.h"
+#include "nsVoidArray.h"
+#include "nsCRT.h"
+#include "nsDeque.h"
+#include "xptcall.h"
+
+#ifdef VBOX
+# include <map>
+# include <list>
+# include <iprt/err.h>
+# include <iprt/req.h>
+# include <iprt/mem.h>
+# include <iprt/time.h>
+# include <iprt/thread.h>
+#endif /* VBOX */
+
+#if defined(DCONNECT_MULTITHREADED)
+
+#if !defined(DCONNECT_WITH_IPRT_REQ_POOL)
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#endif
+
+#if defined(DEBUG) && !defined(DCONNECT_STATS)
+#define DCONNECT_STATS
+#endif
+
+#if defined(DCONNECT_STATS)
+#include <stdio.h>
+#endif
+
+#endif
+
+// XXX TODO:
+// 1. add thread affinity field to SETUP messages
+
+//-----------------------------------------------------------------------------
+
+#define DCONNECT_IPC_TARGETID \
+{ /* 43ca47ef-ebc8-47a2-9679-a4703218089f */ \
+ 0x43ca47ef, \
+ 0xebc8, \
+ 0x47a2, \
+ {0x96, 0x79, 0xa4, 0x70, 0x32, 0x18, 0x08, 0x9f} \
+}
+static const nsID kDConnectTargetID = DCONNECT_IPC_TARGETID;
+
+//-----------------------------------------------------------------------------
+
+#define DCON_WAIT_TIMEOUT PR_INTERVAL_NO_TIMEOUT
+
+//-----------------------------------------------------------------------------
+
+//
+// +--------------------------------------+
+// | major opcode : 1 byte |
+// +--------------------------------------+
+// | minor opcode : 1 byte |
+// +--------------------------------------+
+// | flags : 2 bytes |
+// +--------------------------------------+
+// . .
+// . variable payload .
+// . .
+// +--------------------------------------+
+//
+
+// dconnect major opcodes
+#define DCON_OP_SETUP 1
+#define DCON_OP_RELEASE 2
+#define DCON_OP_INVOKE 3
+
+#define DCON_OP_SETUP_REPLY 4
+#define DCON_OP_INVOKE_REPLY 5
+
+// dconnect minor opcodes for DCON_OP_SETUP
+#define DCON_OP_SETUP_NEW_INST_CLASSID 1
+#define DCON_OP_SETUP_NEW_INST_CONTRACTID 2
+#define DCON_OP_SETUP_GET_SERV_CLASSID 3
+#define DCON_OP_SETUP_GET_SERV_CONTRACTID 4
+#define DCON_OP_SETUP_QUERY_INTERFACE 5
+
+// dconnect minor opcodes for RELEASE
+// dconnect minor opcodes for INVOKE
+
+// DCON_OP_SETUP_REPLY and DCON_OP_INVOKE_REPLY flags
+#define DCON_OP_FLAGS_REPLY_EXCEPTION 0x0001
+
+// Within this time all the worker threads must be terminated.
+#define VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS (5000)
+
+#pragma pack(1)
+
+struct DConnectOp
+{
+ PRUint8 opcode_major;
+ PRUint8 opcode_minor;
+ PRUint16 flags;
+ PRUint32 request_index; // initialized with NewRequestIndex
+};
+
+// SETUP structs
+
+struct DConnectSetup : DConnectOp
+{
+ nsID iid;
+};
+
+struct DConnectSetupClassID : DConnectSetup
+{
+ nsID classid;
+};
+
+struct DConnectSetupContractID : DConnectSetup
+{
+ char contractid[1]; // variable length
+};
+
+struct DConnectSetupQueryInterface : DConnectSetup
+{
+ DConAddr instance;
+};
+
+// SETUP_REPLY struct
+
+struct DConnectSetupReply : DConnectOp
+{
+ DConAddr instance;
+ nsresult status;
+ // optionally followed by a specially serialized nsIException instance (see
+ // ipcDConnectService::SerializeException) if DCON_OP_FLAGS_REPLY_EXCEPTION is
+ // present in flags
+};
+
+// RELEASE struct
+
+struct DConnectRelease : DConnectOp
+{
+ DConAddr instance;
+};
+
+// INVOKE struct
+
+struct DConnectInvoke : DConnectOp
+{
+ DConAddr instance;
+ PRUint16 method_index;
+ // followed by an array of in-param blobs
+};
+
+// INVOKE_REPLY struct
+
+struct DConnectInvokeReply : DConnectOp
+{
+ nsresult result;
+ // followed by an array of out-param blobs if NS_SUCCEEDED(result), and
+ // optionally by a specially serialized nsIException instance (see
+ // ipcDConnectService::SerializeException) if DCON_OP_FLAGS_REPLY_EXCEPTION is
+ // present in flags
+};
+
+#pragma pack()
+
+//-----------------------------------------------------------------------------
+
+struct DConAddrPlusPtr
+{
+ DConAddr addr;
+ void *p;
+};
+
+//-----------------------------------------------------------------------------
+
+ipcDConnectService *ipcDConnectService::mInstance = nsnull;
+
+//-----------------------------------------------------------------------------
+
+static nsresult
+SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen,
+ void **aInstancePtr);
+
+//-----------------------------------------------------------------------------
+
+// A wrapper class holding an instance to an in-process XPCOM object.
+
+class DConnectInstance
+{
+public:
+ DConnectInstance(PRUint32 peer, nsIInterfaceInfo *iinfo, nsISupports *instance)
+ : mPeer(peer)
+ , mIInfo(iinfo)
+ , mInstance(instance)
+ {}
+
+ nsISupports *RealInstance() { return mInstance; }
+ nsIInterfaceInfo *InterfaceInfo() { return mIInfo; }
+ PRUint32 Peer() { return mPeer; }
+
+ DConnectInstanceKey::Key GetKey() {
+ const nsID *iid;
+ mIInfo->GetIIDShared(&iid);
+ return DConnectInstanceKey::Key(mPeer, mInstance, iid);
+ }
+
+ NS_IMETHODIMP_(nsrefcnt) AddRef(void)
+ {
+ NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
+ nsrefcnt count;
+ count = PR_AtomicIncrement((PRInt32*)&mRefCnt);
+ return count;
+ }
+
+ NS_IMETHODIMP_(nsrefcnt) Release(void)
+ {
+ nsrefcnt count;
+ NS_PRECONDITION(0 != mRefCnt, "dup release");
+ count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
+ if (0 == count) {
+ NS_PRECONDITION(PRInt32(mRefCntIPC) == 0, "non-zero IPC refcnt");
+ mRefCnt = 1; /* stabilize */
+ delete this;
+ return 0;
+ }
+ return count;
+ }
+
+ // this gets called after calling AddRef() on an instance passed to the
+ // client over IPC in order to have a count of IPC client-related references
+ // separately from the overall reference count
+ NS_IMETHODIMP_(nsrefcnt) AddRefIPC(void)
+ {
+ NS_PRECONDITION(PRInt32(mRefCntIPC) >= 0, "illegal refcnt");
+ nsrefcnt count = PR_AtomicIncrement((PRInt32*)&mRefCntIPC);
+ return count;
+ }
+
+ // this gets called before calling Release() when DCON_OP_RELEASE is
+ // received from the IPC client and in other cases to balance AddRefIPC()
+ NS_IMETHODIMP_(nsrefcnt) ReleaseIPC(PRBool locked = PR_FALSE)
+ {
+ NS_PRECONDITION(0 != mRefCntIPC, "dup release");
+ nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCntIPC);
+ if (0 == count) {
+ // If the last IPC reference is released, remove this instance from the map.
+ // ipcDConnectService is guaranteed to still exist here
+ // (DConnectInstance lifetime is bound to ipcDConnectService)
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ if (dConnect)
+ dConnect->DeleteInstance(this, locked);
+ else
+ NS_NOTREACHED("ipcDConnectService has gone before DConnectInstance");
+ }
+ return count;
+ }
+
+private:
+ nsAutoRefCnt mRefCnt;
+ nsAutoRefCnt mRefCntIPC;
+ PRUint32 mPeer; // peer process "owning" this instance
+ nsCOMPtr<nsIInterfaceInfo> mIInfo;
+ nsCOMPtr<nsISupports> mInstance;
+};
+
+void
+ipcDConnectService::ReleaseWrappers(nsVoidArray &wrappers, PRUint32 peer)
+{
+ nsAutoLock lock (mLock);
+
+ for (PRInt32 i=0; i<wrappers.Count(); ++i)
+ {
+ DConnectInstance *wrapper = (DConnectInstance *)wrappers[i];
+ if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer)
+ {
+ wrapper->ReleaseIPC(PR_TRUE /* locked */);
+ wrapper->Release();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static nsresult
+SerializeParam(ipcMessageWriter &writer, const nsXPTType &t, const nsXPTCMiniVariant &v)
+{
+ switch (t.TagPart())
+ {
+ case nsXPTType::T_I8:
+ case nsXPTType::T_U8:
+ writer.PutInt8(v.val.u8);
+ break;
+
+ case nsXPTType::T_I16:
+ case nsXPTType::T_U16:
+ writer.PutInt16(v.val.u16);
+ break;
+
+ case nsXPTType::T_I32:
+ case nsXPTType::T_U32:
+ writer.PutInt32(v.val.u32);
+ break;
+
+ case nsXPTType::T_I64:
+ case nsXPTType::T_U64:
+ writer.PutBytes(&v.val.u64, sizeof(PRUint64));
+ break;
+
+ case nsXPTType::T_FLOAT:
+ writer.PutBytes(&v.val.f, sizeof(float));
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ writer.PutBytes(&v.val.d, sizeof(double));
+ break;
+
+ case nsXPTType::T_BOOL:
+ writer.PutBytes(&v.val.b, sizeof(PRBool));
+ break;
+
+ case nsXPTType::T_CHAR:
+ writer.PutBytes(&v.val.c, sizeof(char));
+ break;
+
+ case nsXPTType::T_WCHAR:
+ writer.PutBytes(&v.val.wc, sizeof(PRUnichar));
+ break;
+
+ case nsXPTType::T_IID:
+ {
+ AssertReturn(v.val.p, NS_ERROR_INVALID_POINTER);
+ writer.PutBytes(v.val.p, sizeof(nsID));
+ }
+ break;
+
+ case nsXPTType::T_CHAR_STR:
+ {
+ if (v.val.p)
+ {
+ int len = strlen((const char *) v.val.p);
+ writer.PutInt32(len);
+ writer.PutBytes(v.val.p, len);
+ }
+ else
+ {
+ // put -1 to indicate null string
+ writer.PutInt32((PRUint32) -1);
+ }
+ }
+ break;
+
+ case nsXPTType::T_WCHAR_STR:
+ {
+ if (v.val.p)
+ {
+ int len = 2 * nsCRT::strlen((const PRUnichar *) v.val.p);
+ writer.PutInt32(len);
+ writer.PutBytes(v.val.p, len);
+ }
+ else
+ {
+ // put -1 to indicate null string
+ writer.PutInt32((PRUint32) -1);
+ }
+ }
+ break;
+
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ NS_NOTREACHED("this should be handled elsewhere");
+ return NS_ERROR_UNEXPECTED;
+
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ {
+ const nsAString *str = (const nsAString *) v.val.p;
+
+ PRUint32 len = 2 * str->Length();
+ nsAString::const_iterator begin;
+ const PRUnichar *data = str->BeginReading(begin).get();
+
+ writer.PutInt32(len);
+ writer.PutBytes(data, len);
+ }
+ break;
+
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ {
+ const nsACString *str = (const nsACString *) v.val.p;
+
+ PRUint32 len = str->Length();
+ nsACString::const_iterator begin;
+ const char *data = str->BeginReading(begin).get();
+
+ writer.PutInt32(len);
+ writer.PutBytes(data, len);
+ }
+ break;
+
+ case nsXPTType::T_ARRAY:
+ // arrays are serialized after all other params outside this routine
+ break;
+
+ case nsXPTType::T_VOID:
+ case nsXPTType::T_PSTRING_SIZE_IS:
+ case nsXPTType::T_PWSTRING_SIZE_IS:
+ default:
+ LOG(("unexpected parameter type: %d\n", t.TagPart()));
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+static nsresult
+DeserializeParam(ipcMessageReader &reader, const nsXPTType &t, nsXPTCVariant &v)
+{
+ // defaults
+ v.ptr = nsnull;
+ v.type = t;
+ v.flags = 0;
+
+ switch (t.TagPart())
+ {
+ case nsXPTType::T_I8:
+ case nsXPTType::T_U8:
+ v.val.u8 = reader.GetInt8();
+ break;
+
+ case nsXPTType::T_I16:
+ case nsXPTType::T_U16:
+ v.val.u16 = reader.GetInt16();
+ break;
+
+ case nsXPTType::T_I32:
+ case nsXPTType::T_U32:
+ v.val.u32 = reader.GetInt32();
+ break;
+
+ case nsXPTType::T_I64:
+ case nsXPTType::T_U64:
+ reader.GetBytes(&v.val.u64, sizeof(v.val.u64));
+ break;
+
+ case nsXPTType::T_FLOAT:
+ reader.GetBytes(&v.val.f, sizeof(v.val.f));
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ reader.GetBytes(&v.val.d, sizeof(v.val.d));
+ break;
+
+ case nsXPTType::T_BOOL:
+ reader.GetBytes(&v.val.b, sizeof(v.val.b));
+ break;
+
+ case nsXPTType::T_CHAR:
+ reader.GetBytes(&v.val.c, sizeof(v.val.c));
+ break;
+
+ case nsXPTType::T_WCHAR:
+ reader.GetBytes(&v.val.wc, sizeof(v.val.wc));
+ break;
+
+ case nsXPTType::T_IID:
+ {
+ nsID *buf = (nsID *) nsMemory::Alloc(sizeof(nsID));
+ reader.GetBytes(buf, sizeof(nsID));
+ v.val.p = buf;
+ v.SetValIsAllocated();
+ }
+ break;
+
+ case nsXPTType::T_CHAR_STR:
+ {
+ PRUint32 len = reader.GetInt32();
+ if (len == (PRUint32) -1)
+ {
+ // it's a null string
+ v.val.p = nsnull;
+ }
+ else
+ {
+ char *buf = (char *) nsMemory::Alloc(len + 1);
+ reader.GetBytes(buf, len);
+ buf[len] = char(0);
+
+ v.val.p = buf;
+ v.SetValIsAllocated();
+ }
+ }
+ break;
+
+ case nsXPTType::T_WCHAR_STR:
+ {
+ PRUint32 len = reader.GetInt32();
+ if (len == (PRUint32) -1)
+ {
+ // it's a null string
+ v.val.p = nsnull;
+ }
+ else
+ {
+ PRUnichar *buf = (PRUnichar *) nsMemory::Alloc(len + 2);
+ reader.GetBytes(buf, len);
+ buf[len / 2] = PRUnichar(0);
+
+ v.val.p = buf;
+ v.SetValIsAllocated();
+ }
+ }
+ break;
+
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ {
+ reader.GetBytes(&v.val.u64, sizeof(DConAddr));
+ // stub creation will be handled outside this routine. we only
+ // deserialize the DConAddr into v.val.u64 temporarily.
+ }
+ break;
+
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ {
+ PRUint32 len = reader.GetInt32();
+
+ nsString *str = new nsString();
+ str->SetLength(len / 2);
+ PRUnichar *buf = str->BeginWriting();
+ reader.GetBytes(buf, len);
+
+ v.val.p = str;
+ v.SetValIsDOMString();
+ }
+ break;
+
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ {
+ PRUint32 len = reader.GetInt32();
+
+ nsCString *str = new nsCString();
+ str->SetLength(len);
+ char *buf = str->BeginWriting();
+ reader.GetBytes(buf, len);
+
+ v.val.p = str;
+
+ // this distinction here is pretty pointless
+ if (t.TagPart() == nsXPTType::T_CSTRING)
+ v.SetValIsCString();
+ else
+ v.SetValIsUTF8String();
+ }
+ break;
+
+ case nsXPTType::T_ARRAY:
+ // arrays are deserialized after all other params outside this routine
+ break;
+
+ case nsXPTType::T_VOID:
+ case nsXPTType::T_PSTRING_SIZE_IS:
+ case nsXPTType::T_PWSTRING_SIZE_IS:
+ default:
+ LOG(("unexpected parameter type\n"));
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+static nsresult
+SetupParam(const nsXPTParamInfo &p, nsXPTCVariant &v)
+{
+ const nsXPTType &t = p.GetType();
+
+ if (p.IsIn() && p.IsDipper())
+ {
+ v.ptr = nsnull;
+ v.flags = 0;
+
+ switch (t.TagPart())
+ {
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ v.val.p = new nsString();
+ if (!v.val.p)
+ return NS_ERROR_OUT_OF_MEMORY;
+ v.type = t;
+ v.SetValIsDOMString();
+ break;
+
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ v.val.p = new nsCString();
+ if (!v.val.p)
+ return NS_ERROR_OUT_OF_MEMORY;
+ v.type = t;
+ v.SetValIsCString();
+ break;
+
+ default:
+ LOG(("unhandled dipper: type=%d\n", t.TagPart()));
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ else if (p.IsOut() || p.IsRetval())
+ {
+ memset(&v.val, 0, sizeof(v.val));
+ v.ptr = &v.val;
+ v.type = t;
+ v.flags = 0;
+ v.SetPtrIsData();
+
+ // the ownership of output nsID, string, wstring, interface pointers and
+ // arrays is transferred to the receiving party. Therefore, we need to
+ // instruct FinishParam() to perform a cleanup after serializing them.
+ switch (t.TagPart())
+ {
+ case nsXPTType::T_IID:
+ case nsXPTType::T_CHAR_STR:
+ case nsXPTType::T_WCHAR_STR:
+ case nsXPTType::T_ARRAY:
+ v.SetValIsAllocated();
+ break;
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ v.SetValIsInterface();
+ break;
+ default:
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+static void
+FinishParam(nsXPTCVariant &v)
+{
+#ifdef VBOX
+ /* make valgrind happy */
+ if (!v.MustFreeVal())
+ return;
+#endif
+ if (!v.val.p)
+ return;
+
+ if (v.IsValAllocated())
+ nsMemory::Free(v.val.p);
+ else if (v.IsValInterface())
+ ((nsISupports *) v.val.p)->Release();
+ else if (v.IsValDOMString())
+ delete (nsAString *) v.val.p;
+ else if (v.IsValUTF8String() || v.IsValCString())
+ delete (nsACString *) v.val.p;
+}
+
+static nsresult
+DeserializeResult(ipcMessageReader &reader, const nsXPTType &t, nsXPTCMiniVariant &v)
+{
+ if (v.val.p == nsnull)
+ return NS_OK;
+
+ switch (t.TagPart())
+ {
+ case nsXPTType::T_I8:
+ case nsXPTType::T_U8:
+ *((PRUint8 *) v.val.p) = reader.GetInt8();
+ break;
+
+ case nsXPTType::T_I16:
+ case nsXPTType::T_U16:
+ *((PRUint16 *) v.val.p) = reader.GetInt16();
+ break;
+
+ case nsXPTType::T_I32:
+ case nsXPTType::T_U32:
+ *((PRUint32 *) v.val.p) = reader.GetInt32();
+ break;
+
+ case nsXPTType::T_I64:
+ case nsXPTType::T_U64:
+ reader.GetBytes(v.val.p, sizeof(PRUint64));
+ break;
+
+ case nsXPTType::T_FLOAT:
+ reader.GetBytes(v.val.p, sizeof(float));
+ break;
+
+ case nsXPTType::T_DOUBLE:
+ reader.GetBytes(v.val.p, sizeof(double));
+ break;
+
+ case nsXPTType::T_BOOL:
+ reader.GetBytes(v.val.p, sizeof(PRBool));
+ break;
+
+ case nsXPTType::T_CHAR:
+ reader.GetBytes(v.val.p, sizeof(char));
+ break;
+
+ case nsXPTType::T_WCHAR:
+ reader.GetBytes(v.val.p, sizeof(PRUnichar));
+ break;
+
+ case nsXPTType::T_IID:
+ {
+ nsID *buf = (nsID *) nsMemory::Alloc(sizeof(nsID));
+ reader.GetBytes(buf, sizeof(nsID));
+ *((nsID **) v.val.p) = buf;
+ }
+ break;
+
+ case nsXPTType::T_CHAR_STR:
+ {
+ PRUint32 len = reader.GetInt32();
+ if (len == (PRUint32) -1)
+ {
+ // it's a null string
+#ifdef VBOX
+ *((char **) v.val.p) = NULL;
+#else
+ v.val.p = 0;
+#endif
+ }
+ else
+ {
+ char *buf = (char *) nsMemory::Alloc(len + 1);
+ reader.GetBytes(buf, len);
+ buf[len] = char(0);
+
+ *((char **) v.val.p) = buf;
+ }
+ }
+ break;
+
+ case nsXPTType::T_WCHAR_STR:
+ {
+ PRUint32 len = reader.GetInt32();
+ if (len == (PRUint32) -1)
+ {
+ // it's a null string
+#ifdef VBOX
+ *((PRUnichar **) v.val.p) = 0;
+#else
+ v.val.p = 0;
+#endif
+ }
+ else
+ {
+ PRUnichar *buf = (PRUnichar *) nsMemory::Alloc(len + 2);
+ reader.GetBytes(buf, len);
+ buf[len / 2] = PRUnichar(0);
+
+ *((PRUnichar **) v.val.p) = buf;
+ }
+ }
+ break;
+
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ {
+ // stub creation will be handled outside this routine. we only
+ // deserialize the DConAddr and the original value of v.val.p
+ // into v.val.p temporarily. needs temporary memory alloc.
+ DConAddrPlusPtr *buf = (DConAddrPlusPtr *) nsMemory::Alloc(sizeof(DConAddrPlusPtr));
+ reader.GetBytes(&buf->addr, sizeof(DConAddr));
+ buf->p = v.val.p;
+ v.val.p = buf;
+ }
+ break;
+
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ {
+ PRUint32 len = reader.GetInt32();
+
+ nsAString *str = (nsAString *) v.val.p;
+
+ nsAString::iterator begin;
+ str->SetLength(len / 2);
+ str->BeginWriting(begin);
+
+ reader.GetBytes(begin.get(), len);
+ }
+ break;
+
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ {
+ PRUint32 len = reader.GetInt32();
+
+ nsACString *str = (nsACString *) v.val.p;
+
+ nsACString::iterator begin;
+ str->SetLength(len);
+ str->BeginWriting(begin);
+
+ reader.GetBytes(begin.get(), len);
+ }
+ break;
+
+ case nsXPTType::T_ARRAY:
+ // arrays are deserialized after all other params outside this routine
+ break;
+
+ case nsXPTType::T_VOID:
+ case nsXPTType::T_PSTRING_SIZE_IS:
+ case nsXPTType::T_PWSTRING_SIZE_IS:
+ default:
+ LOG(("unexpected parameter type\n"));
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+//
+// Returns an element from the nsXPTCMiniVariant array by properly casting it to
+// nsXPTCVariant when requested
+#define GET_PARAM(params, isXPTCVariantArray, idx) \
+ (isXPTCVariantArray ? ((nsXPTCVariant *) params) [idx] : params [idx])
+
+// isResult is PR_TRUE if the size_is and length_is params are out or retval
+// so that nsXPTCMiniVariants contain pointers to their locations instead of the
+// values themselves.
+static nsresult
+GetArrayParamInfo(nsIInterfaceInfo *iinfo, uint16 methodIndex,
+ const nsXPTMethodInfo &methodInfo, nsXPTCMiniVariant *params,
+ PRBool isXPTCVariantArray, const nsXPTParamInfo &paramInfo,
+ PRBool isResult, PRUint32 &size, PRUint32 &length,
+ nsXPTType &elemType)
+{
+ // XXX multidimensional arrays are not supported so dimension is always 0 for
+ // getting the size_is argument number of the array itself and 1 for getting
+ // the type of elements stored in the array.
+
+ nsresult rv;
+
+ // get the array size
+ PRUint8 sizeArg;
+ rv = iinfo->GetSizeIsArgNumberForParam(methodIndex, &paramInfo, 0, &sizeArg);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // get the number of valid elements
+ PRUint8 lenArg;
+ rv = iinfo->GetLengthIsArgNumberForParam(methodIndex, &paramInfo, 0, &lenArg);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // according to XPT specs
+ // (http://www.mozilla.org/scriptable/typelib_file.html), size_is and
+ // length_is for arrays is always uint32. Check this too.
+ {
+ nsXPTParamInfo pi = methodInfo.GetParam (sizeArg);
+ if (pi.GetType().TagPart() != nsXPTType::T_U32)
+ {
+ LOG(("unexpected size_is() parameter type: $d\n",
+ pi.GetType().TagPart()));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ pi = methodInfo.GetParam (lenArg);
+ if (pi.GetType().TagPart() != nsXPTType::T_U32)
+ {
+ LOG(("unexpected length_is() parameter type: $d\n",
+ pi.GetType().TagPart()));
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ if (isResult)
+ {
+ length = *((PRUint32 *) GET_PARAM(params,isXPTCVariantArray, lenArg).val.p);
+ size = *((PRUint32 *) GET_PARAM(params, isXPTCVariantArray, sizeArg).val.p);
+ }
+ else
+ {
+ length = GET_PARAM(params, isXPTCVariantArray, lenArg).val.u32;
+ size = GET_PARAM(params, isXPTCVariantArray, sizeArg).val.u32;
+ }
+
+ if (length > size)
+ {
+ NS_WARNING("length_is() value is greater than size_is() value");
+ length = size;
+ }
+
+ // get type of array elements
+ rv = iinfo->GetTypeForParam(methodIndex, &paramInfo, 1, &elemType);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (elemType.IsArithmetic() &&
+ (elemType.IsPointer() || elemType.IsUniquePointer() ||
+ elemType.IsReference()))
+ {
+ LOG(("arrays of pointers and references to arithmetic types are "
+ "not yet supported\n"));
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ if (elemType.IsArray())
+ {
+ LOG(("multidimensional arrays are not yet supported\n"));
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+GetTypeSize(const nsXPTType &type, PRUint32 &size, PRBool &isSimple)
+{
+ // get the type size in bytes
+ size = 0;
+ isSimple = PR_TRUE;
+ switch (type.TagPart())
+ {
+ case nsXPTType::T_I8: size = sizeof(PRInt8); break;
+ case nsXPTType::T_I16: size = sizeof(PRInt16); break;
+ case nsXPTType::T_I32: size = sizeof(PRInt32); break;
+ case nsXPTType::T_I64: size = sizeof(PRInt64); break;
+ case nsXPTType::T_U8: size = sizeof(PRUint8); break;
+ case nsXPTType::T_U16: size = sizeof(PRUint16); break;
+ case nsXPTType::T_U32: size = sizeof(PRUint32); break;
+ case nsXPTType::T_U64: size = sizeof(PRUint64); break;
+ case nsXPTType::T_FLOAT: size = sizeof(float); break;
+ case nsXPTType::T_DOUBLE: size = sizeof(double); break;
+ case nsXPTType::T_BOOL: size = sizeof(PRBool); break;
+ case nsXPTType::T_CHAR: size = sizeof(char); break;
+ case nsXPTType::T_WCHAR: size = sizeof(PRUnichar); break;
+ case nsXPTType::T_IID: /* fall through */
+ case nsXPTType::T_CHAR_STR: /* fall through */
+ case nsXPTType::T_WCHAR_STR: /* fall through */
+ case nsXPTType::T_ASTRING: /* fall through */
+ case nsXPTType::T_DOMSTRING: /* fall through */
+ case nsXPTType::T_UTF8STRING: /* fall through */
+ case nsXPTType::T_CSTRING: /* fall through */
+ size = sizeof(void *);
+ isSimple = PR_FALSE;
+ break;
+ case nsXPTType::T_INTERFACE: /* fall through */
+ case nsXPTType::T_INTERFACE_IS: /* fall through */
+ size = sizeof(DConAddr);
+ isSimple = PR_FALSE;
+ break;
+ default:
+ LOG(("unexpected parameter type: %d\n", type.TagPart()));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+static nsresult
+SerializeArrayParam(ipcDConnectService *dConnect,
+ ipcMessageWriter &writer, PRUint32 peerID,
+ nsIInterfaceInfo *iinfo, uint16 methodIndex,
+ const nsXPTMethodInfo &methodInfo,
+ nsXPTCMiniVariant *params, PRBool isXPTCVariantArray,
+ const nsXPTParamInfo &paramInfo,
+ void *array, nsVoidArray &wrappers)
+{
+ if (!array)
+ {
+ // put 0 to indicate null array
+ writer.PutInt8(0);
+ return NS_OK;
+ }
+
+ // put 1 to indicate non-null array
+ writer.PutInt8(1);
+
+ PRUint32 size = 0;
+ PRUint32 length = 0;
+ nsXPTType elemType;
+
+ nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params,
+ isXPTCVariantArray, paramInfo, PR_FALSE,
+ size, length, elemType);
+ if (NS_FAILED (rv))
+ return rv;
+
+ PRUint32 elemSize = 0;
+ PRBool isSimple = PR_TRUE;
+ rv = GetTypeSize(elemType, elemSize, isSimple);
+ if (NS_FAILED (rv))
+ return rv;
+
+ if (isSimple)
+ {
+ // this is a simple arithmetic type, write the whole array at once
+ writer.PutBytes(array, length * elemSize);
+ return NS_OK;
+ }
+
+ // iterate over valid (length_is) elements of the array
+ // and serialize each of them
+ nsXPTCMiniVariant v;
+ for (PRUint32 i = 0; i < length; ++i)
+ {
+ v.val.p = ((void **) array) [i];
+
+ if (elemType.IsInterfacePointer())
+ {
+ nsID iid;
+ rv = dConnect->GetIIDForMethodParam(iinfo, &methodInfo, paramInfo, elemType,
+ methodIndex, params, isXPTCVariantArray,
+ iid);
+ if (NS_SUCCEEDED(rv))
+ rv = dConnect->SerializeInterfaceParam(writer, peerID, iid,
+ (nsISupports *) v.val.p,
+ wrappers);
+ }
+ else
+ rv = SerializeParam(writer, elemType, v);
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+// isResult is PR_TRUE if the array param is out or retval
+static nsresult
+DeserializeArrayParam(ipcDConnectService *dConnect,
+ ipcMessageReader &reader, PRUint32 peerID,
+ nsIInterfaceInfo *iinfo, uint16 methodIndex,
+ const nsXPTMethodInfo &methodInfo,
+ nsXPTCMiniVariant *params, PRBool isXPTCVariantArray,
+ const nsXPTParamInfo &paramInfo,
+ PRBool isResult, void *&array)
+{
+ PRUint32 size = 0;
+ PRUint32 length = 0;
+ nsXPTType elemType;
+
+ nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params,
+ isXPTCVariantArray, paramInfo, isResult,
+ size, length, elemType);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint8 prefix = reader.GetInt8();
+ if (prefix == 0)
+ {
+ // it's a null array
+ array = nsnull;
+ return NS_OK;
+ }
+ // sanity
+ if (prefix != 1)
+ {
+ LOG(("unexpected array prefix: %u\n", prefix));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ PRUint32 elemSize = 0;
+ PRBool isSimple = PR_TRUE;
+ rv = GetTypeSize(elemType, elemSize, isSimple);
+ if (NS_FAILED (rv))
+ return rv;
+
+ // Note: for zero-sized arrays, we use the size of 1 because whether
+ // malloc(0) returns a null pointer or not (which is used in isNull())
+ // is implementation-dependent according to the C standard
+ void *arr = nsMemory::Alloc((size ? size : 1) * elemSize);
+ if (arr == nsnull)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // initialize the unused space of the array with zeroes
+ if (length < size)
+ memset(((PRUint8 *) arr) + length * elemSize, 0,
+ (size - length) * elemSize);
+
+ if (isSimple)
+ {
+ // this is a simple arithmetic type, read the whole array at once
+ reader.GetBytes(arr, length * elemSize);
+
+ array = arr;
+ return NS_OK;
+ }
+
+ // iterate over valid (length_is) elements of the array
+ // and deserialize each of them individually
+ nsXPTCVariant v;
+ for (PRUint32 i = 0; i < length; ++i)
+ {
+ rv = DeserializeParam(reader, elemType, v);
+
+ if (NS_SUCCEEDED(rv) && elemType.IsInterfacePointer())
+ {
+ // grab the DConAddr value temporarily stored in the param
+ PtrBits bits = v.val.u64;
+
+ // DeserializeInterfaceParamBits needs IID only if it's a remote object
+ nsID iid;
+ if (bits & PTRBITS_REMOTE_BIT)
+ rv = dConnect->GetIIDForMethodParam(iinfo, &methodInfo, paramInfo,
+ elemType, methodIndex,
+ params, isXPTCVariantArray, iid);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsISupports *obj = nsnull;
+ rv = dConnect->DeserializeInterfaceParamBits(bits, peerID, iid, obj);
+ if (NS_SUCCEEDED(rv))
+ v.val.p = obj;
+ }
+ }
+
+ if (NS_FAILED(rv))
+ break;
+
+ // note that we discard extended param informaton provided by nsXPTCVariant
+ // and will have to "reconstruct" it from the type tag in FinishArrayParam()
+ ((void **) arr) [i] = v.val.p;
+ }
+
+ if (NS_FAILED(rv))
+ nsMemory::Free(arr);
+ else
+ array = arr;
+
+ return rv;
+}
+
+static void
+FinishArrayParam(nsIInterfaceInfo *iinfo, uint16 methodIndex,
+ const nsXPTMethodInfo &methodInfo, nsXPTCMiniVariant *params,
+ PRBool isXPTCVariantArray, const nsXPTParamInfo &paramInfo,
+ const nsXPTCMiniVariant &arrayVal)
+{
+ // nothing to do for a null array
+ void *arr = arrayVal.val.p;
+ if (!arr)
+ return;
+
+ PRUint32 size = 0;
+ PRUint32 length = 0;
+ nsXPTType elemType;
+
+ // note that FinishArrayParam is called only from OnInvoke to free memory
+ // after the call has been served. When OnInvoke sets up out and retval
+ // parameters for the real method, it passes pointers to the nsXPTCMiniVariant
+ // elements of the params array themselves so that they will eventually
+ // receive the returned values. For this reason, both in 'in' param and
+ // 'out/retaval' param cases, size_is and length_is may be read by
+ // GetArrayParamInfo() by value. Therefore, isResult is always PR_FALSE.
+ nsresult rv = GetArrayParamInfo(iinfo, methodIndex, methodInfo, params,
+ isXPTCVariantArray, paramInfo, PR_FALSE,
+ size, length, elemType);
+ if (NS_FAILED (rv))
+ return;
+
+ nsXPTCVariant v;
+ v.ptr = nsnull;
+ v.flags = 0;
+
+ // iterate over valid (length_is) elements of the array
+ // and free each of them
+ for (PRUint32 i = 0; i < length; ++i)
+ {
+ v.type = elemType.TagPart();
+
+ switch (elemType.TagPart())
+ {
+ case nsXPTType::T_I8: /* fall through */
+ case nsXPTType::T_I16: /* fall through */
+ case nsXPTType::T_I32: /* fall through */
+ case nsXPTType::T_I64: /* fall through */
+ case nsXPTType::T_U8: /* fall through */
+ case nsXPTType::T_U16: /* fall through */
+ case nsXPTType::T_U32: /* fall through */
+ case nsXPTType::T_U64: /* fall through */
+ case nsXPTType::T_FLOAT: /* fall through */
+ case nsXPTType::T_DOUBLE: /* fall through */
+ case nsXPTType::T_BOOL: /* fall through */
+ case nsXPTType::T_CHAR: /* fall through */
+ case nsXPTType::T_WCHAR: /* fall through */
+ // nothing to free for arithmetic types
+ continue;
+ case nsXPTType::T_IID: /* fall through */
+ case nsXPTType::T_CHAR_STR: /* fall through */
+ case nsXPTType::T_WCHAR_STR: /* fall through */
+ v.val.p = ((void **) arr) [i];
+ v.SetValIsAllocated();
+ break;
+ case nsXPTType::T_INTERFACE: /* fall through */
+ case nsXPTType::T_INTERFACE_IS: /* fall through */
+ v.val.p = ((void **) arr) [i];
+ v.SetValIsInterface();
+ break;
+ case nsXPTType::T_ASTRING: /* fall through */
+ case nsXPTType::T_DOMSTRING: /* fall through */
+ v.val.p = ((void **) arr) [i];
+ v.SetValIsDOMString();
+ break;
+ case nsXPTType::T_UTF8STRING: /* fall through */
+ v.val.p = ((void **) arr) [i];
+ v.SetValIsUTF8String();
+ break;
+ case nsXPTType::T_CSTRING: /* fall through */
+ v.val.p = ((void **) arr) [i];
+ v.SetValIsCString();
+ break;
+ default:
+ LOG(("unexpected parameter type: %d\n", elemType.TagPart()));
+ return;
+ }
+
+ FinishParam(v);
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static PRUint32
+NewRequestIndex()
+{
+ static PRInt32 sRequestIndex;
+ return (PRUint32) PR_AtomicIncrement(&sRequestIndex);
+}
+
+//-----------------------------------------------------------------------------
+
+#ifdef VBOX
+typedef struct ClientDownInfo
+{
+ ClientDownInfo(PRUint32 aClient)
+ {
+ uClient = aClient;
+ uTimestamp = PR_IntervalNow();
+ }
+
+ PRUint32 uClient;
+ PRIntervalTime uTimestamp;
+} ClientDownInfo;
+typedef std::map<PRUint32, ClientDownInfo *> ClientDownMap;
+typedef std::list<ClientDownInfo *> ClientDownList;
+
+#define MAX_CLIENT_DOWN_SIZE 10000
+
+/* Protected by the queue monitor. */
+static ClientDownMap g_ClientDownMap;
+static ClientDownList g_ClientDownList;
+
+#endif /* VBOX */
+
+class DConnectMsgSelector : public ipcIMessageObserver
+{
+public:
+ DConnectMsgSelector(PRUint32 peer, PRUint8 opCodeMajor, PRUint32 requestIndex)
+ : mPeer (peer)
+ , mOpCodeMajor(opCodeMajor)
+ , mRequestIndex(requestIndex)
+ {}
+
+ // stack based only
+ NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
+ NS_IMETHOD_(nsrefcnt) Release() { return 1; }
+
+ NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr);
+
+ NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID, const nsID &aTarget,
+ const PRUint8 *aData, PRUint32 aDataLen)
+ {
+ // accept special "client dead" messages for a given peer
+ // (empty target id, zero data and data length)
+#ifndef VBOX
+ if (aSenderID == mPeer && aTarget.Equals(nsID()) && !aData && !aDataLen)
+ return NS_OK;
+#else /* VBOX */
+ if (aSenderID != IPC_SENDER_ANY && aTarget.Equals(nsID()) && !aData && !aDataLen)
+ {
+ // Insert new client down information. Start by expiring outdated
+ // entries and free one element if there's still no space (if needed).
+ PRIntervalTime now = PR_IntervalNow();
+ while (!g_ClientDownList.empty())
+ {
+ ClientDownInfo *cInfo = g_ClientDownList.back();
+ PRInt64 diff = (PRInt64)now - cInfo->uTimestamp;
+ if (diff < 0)
+ diff += (PRInt64)((PRIntervalTime)-1) + 1;
+ if (diff > PR_SecondsToInterval(15 * 60))
+ {
+ g_ClientDownMap.erase(cInfo->uClient);
+ g_ClientDownList.pop_back();
+ NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(),
+ "client down info inconsistency during expiry");
+ delete cInfo;
+ }
+ else
+ break;
+ }
+
+ ClientDownMap::iterator it = g_ClientDownMap.find(aSenderID);
+ if (it == g_ClientDownMap.end())
+ {
+ /* Getting size of a map is O(1), size of a list can be O(n). */
+ while (g_ClientDownMap.size() >= MAX_CLIENT_DOWN_SIZE)
+ {
+ ClientDownInfo *cInfo = g_ClientDownList.back();
+ g_ClientDownMap.erase(cInfo->uClient);
+ g_ClientDownList.pop_back();
+ NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(),
+ "client down info inconsistency during emergency evicting");
+ delete cInfo;
+ }
+
+ ClientDownInfo *cInfo = new ClientDownInfo(aSenderID);
+ RTMEM_MAY_LEAK(cInfo); /* tstVBoxAPIPerf leaks one allocated during ComPtr<IVirtualBoxClient>::createInprocObject(). */
+ g_ClientDownMap[aSenderID] = cInfo;
+ g_ClientDownList.push_front(cInfo);
+ NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(),
+ "client down info inconsistency after adding entry");
+ }
+ return (aSenderID == mPeer) ? NS_OK : IPC_WAIT_NEXT_MESSAGE;
+ }
+ // accept special "client up" messages for a given peer
+ // (empty target id, zero data and data length=1)
+ if (aTarget.Equals(nsID()) && !aData && aDataLen == 1)
+ {
+ ClientDownMap::iterator it = g_ClientDownMap.find(aSenderID);
+ if (it != g_ClientDownMap.end())
+ {
+ ClientDownInfo *cInfo = it->second;
+ g_ClientDownMap.erase(it);
+ g_ClientDownList.remove(cInfo);
+ NS_ASSERTION(g_ClientDownMap.size() == g_ClientDownList.size(),
+ "client down info inconsistency in client up case");
+ delete cInfo;
+ }
+ return (aSenderID == mPeer) ? NS_OK : IPC_WAIT_NEXT_MESSAGE;
+ }
+ // accept special "client check" messages for an anonymous sender
+ // (invalid sender id, empty target id, zero data and data length
+ if (aSenderID == IPC_SENDER_ANY && aTarget.Equals(nsID()) && !aData && !aDataLen)
+ {
+ LOG(("DConnectMsgSelector::OnMessageAvailable: poll liveness for mPeer=%d\n",
+ mPeer));
+ ClientDownMap::iterator it = g_ClientDownMap.find(mPeer);
+ return (it == g_ClientDownMap.end()) ? IPC_WAIT_NEXT_MESSAGE : NS_OK;
+ }
+#endif /* VBOX */
+ const DConnectOp *op = (const DConnectOp *) aData;
+ // accept only reply messages with the given peer/opcode/index
+ // (to prevent eating replies the other thread might be waiting for)
+ // as well as any non-reply messages (to serve external requests that
+ // might arrive while we're waiting for the given reply).
+ if (aDataLen >= sizeof(DConnectOp) &&
+ ((op->opcode_major != DCON_OP_SETUP_REPLY &&
+ op->opcode_major != DCON_OP_INVOKE_REPLY) ||
+ (aSenderID == mPeer &&
+ op->opcode_major == mOpCodeMajor &&
+ op->request_index == mRequestIndex)))
+ return NS_OK;
+ else
+ return IPC_WAIT_NEXT_MESSAGE;
+ }
+
+ const PRUint32 mPeer;
+ const PRUint8 mOpCodeMajor;
+ const PRUint32 mRequestIndex;
+};
+NS_IMPL_QUERY_INTERFACE1(DConnectMsgSelector, ipcIMessageObserver)
+
+class DConnectCompletion : public ipcIMessageObserver
+{
+public:
+ DConnectCompletion(PRUint32 peer, PRUint8 opCodeMajor, PRUint32 requestIndex)
+ : mSelector(peer, opCodeMajor, requestIndex)
+ {}
+
+ // stack based only
+ NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
+ NS_IMETHOD_(nsrefcnt) Release() { return 1; }
+
+ NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr);
+
+ NS_IMETHOD OnMessageAvailable(PRUint32 aSenderID, const nsID &aTarget,
+ const PRUint8 *aData, PRUint32 aDataLen)
+ {
+ const DConnectOp *op = (const DConnectOp *) aData;
+ LOG((
+ "DConnectCompletion::OnMessageAvailable: "
+ "senderID=%d, opcode_major=%d, index=%d (waiting for %d)\n",
+ aSenderID, op->opcode_major, op->request_index, mSelector.mRequestIndex
+ ));
+ if (aSenderID == mSelector.mPeer &&
+ op->opcode_major == mSelector.mOpCodeMajor &&
+ op->request_index == mSelector.mRequestIndex)
+ {
+ OnResponseAvailable(aSenderID, op, aDataLen);
+ }
+ else
+ {
+ // ensure ipcDConnectService is not deleted before we finish
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ if (dConnect)
+ dConnect->OnMessageAvailable(aSenderID, aTarget, aData, aDataLen);
+ }
+ return NS_OK;
+ }
+
+ virtual void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen) = 0;
+
+ DConnectMsgSelector &GetSelector()
+ {
+ return mSelector;
+ }
+
+protected:
+ DConnectMsgSelector mSelector;
+};
+NS_IMPL_QUERY_INTERFACE1(DConnectCompletion, ipcIMessageObserver)
+
+//-----------------------------------------------------------------------------
+
+class DConnectInvokeCompletion : public DConnectCompletion
+{
+public:
+ DConnectInvokeCompletion(PRUint32 peer, const DConnectInvoke *invoke)
+ : DConnectCompletion(peer, DCON_OP_INVOKE_REPLY, invoke->request_index)
+ , mReply(nsnull)
+ , mParamsLen(0)
+ {}
+
+ ~DConnectInvokeCompletion() { if (mReply) free(mReply); }
+
+ void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen)
+ {
+ mReply = (DConnectInvokeReply *) malloc(opLen);
+ memcpy(mReply, op, opLen);
+
+ // the length in bytes of the parameter blob
+ mParamsLen = opLen - sizeof(*mReply);
+ }
+
+ PRBool IsPending() const { return mReply == nsnull; }
+ nsresult GetResult() const { return mReply->result; }
+
+ const PRUint8 *Params() const { return (const PRUint8 *) (mReply + 1); }
+ PRUint32 ParamsLen() const { return mParamsLen; }
+
+ const DConnectInvokeReply *Reply() const { return mReply; }
+
+private:
+ DConnectInvokeReply *mReply;
+ PRUint32 mParamsLen;
+};
+
+//-----------------------------------------------------------------------------
+
+#define DCONNECT_STUB_ID \
+{ /* 132c1f14-5442-49cb-8fe6-e60214bbf1db */ \
+ 0x132c1f14, \
+ 0x5442, \
+ 0x49cb, \
+ {0x8f, 0xe6, 0xe6, 0x02, 0x14, 0xbb, 0xf1, 0xdb} \
+}
+static NS_DEFINE_IID(kDConnectStubID, DCONNECT_STUB_ID);
+
+// this class represents the non-local object instance.
+
+class DConnectStub : public nsXPTCStubBase
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ DConnectStub(nsIInterfaceInfo *aIInfo,
+ DConAddr aInstance,
+ PRUint32 aPeerID)
+ : mIInfo(aIInfo)
+ , mInstance(aInstance)
+ , mPeerID(aPeerID)
+ , mCachedISupports(0)
+ , mRefCntLevels(0)
+ {}
+
+ NS_HIDDEN ~DConnectStub();
+
+ // return a refcounted pointer to the InterfaceInfo for this object
+ // NOTE: on some platforms this MUST not fail or we crash!
+ NS_IMETHOD GetInterfaceInfo(nsIInterfaceInfo **aInfo);
+
+ // call this method and return result
+ NS_IMETHOD CallMethod(PRUint16 aMethodIndex,
+ const nsXPTMethodInfo *aInfo,
+ nsXPTCMiniVariant *aParams);
+
+ DConAddr Instance() { return mInstance; }
+ PRUint32 PeerID() { return mPeerID; }
+
+ DConnectStubKey::Key GetKey() {
+ return DConnectStubKey::Key(mPeerID, mInstance);
+ }
+
+ NS_IMETHOD_(nsrefcnt) AddRefIPC();
+
+private:
+ nsCOMPtr<nsIInterfaceInfo> mIInfo;
+
+ // uniquely identifies this object instance between peers.
+ DConAddr mInstance;
+
+ // the "client id" of our IPC peer. this guy owns the real object.
+ PRUint32 mPeerID;
+
+ // cached nsISupports stub for this object
+ DConnectStub *mCachedISupports;
+
+ // stack of reference counter values (protected by
+ // ipcDConnectService::StubLock())
+ nsDeque mRefCntLevels;
+};
+
+NS_IMETHODIMP_(nsrefcnt)
+DConnectStub::AddRefIPC()
+{
+ // in this special version, we memorize the resulting reference count in the
+ // associated stack array. This stack is then used by Release() to determine
+ // when it is necessary to send a RELEASE request to the peer owning the
+ // object in order to balance AddRef() the peer does on DConnectInstance every
+ // time it passes an object over IPC.
+
+ // NOTE: this function is to be called from DConnectInstance::CreateStub only!
+
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ NS_ASSERTION(dConnect, "no ipcDConnectService (uninitialized?)");
+ if (!dConnect)
+ return 0;
+
+ // dConnect->StubLock() must be already locked here by
+ // DConnectInstance::CreateStub
+
+ nsrefcnt count = AddRef();
+ mRefCntLevels.Push((void *)(uintptr_t) count);
+ return count;
+}
+
+nsresult
+ipcDConnectService::CreateStub(const nsID &iid, PRUint32 peer, DConAddr instance,
+ DConnectStub **result)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoLock lock (mLock);
+
+ if (mDisconnected)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // we also need the stub lock which protects DConnectStub::mRefCntLevels and
+ // ipcDConnectService::mStubs
+ nsAutoLock stubLock (mStubLock);
+
+ DConnectStub *stub = nsnull;
+
+ // first try to find an existing stub for a given peer and instance
+ // (we do not care about IID because every DConAddr instance represents
+ // exactly one interface of the real object on the peer's side)
+ if (!mStubs.Get(DConnectStubKey::Key(peer, instance), &stub))
+ {
+ stub = new DConnectStub(iinfo, instance, peer);
+
+ if (NS_UNLIKELY(!stub))
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ else
+ {
+ rv = StoreStub(stub);
+ if (NS_FAILED(rv))
+ delete stub;
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ stub->AddRefIPC();
+ *result = stub;
+ }
+
+ return rv;
+}
+
+nsresult
+ipcDConnectService::SerializeInterfaceParam(ipcMessageWriter &writer,
+ PRUint32 peer, const nsID &iid,
+ nsISupports *obj,
+ nsVoidArray &wrappers)
+{
+ nsAutoLock lock (mLock);
+
+ if (mDisconnected)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // we create an instance wrapper, and assume that the other side will send a
+ // RELEASE message when it no longer needs the instance wrapper. that will
+ // usually happen after the call returns.
+ //
+ // XXX a lazy scheme might be better, but for now simplicity wins.
+
+ // if the interface pointer references a DConnectStub corresponding
+ // to an object in the address space of the peer, then no need to
+ // create a new wrapper.
+
+ // if the interface pointer references an object for which we already have
+ // an existing wrapper, then we use it instead of creating a new one. this
+ // is based on the assumption that a valid COM object always returns exactly
+ // the same pointer value in response to every
+ // QueryInterface(NS_GET_IID(nsISupports), ...).
+
+ if (!obj)
+ {
+ // write null address
+ DConAddr nullobj = 0;
+ writer.PutBytes(&nullobj, sizeof(nullobj));
+ }
+ else
+ {
+ DConnectStub *stub = nsnull;
+ nsresult rv = obj->QueryInterface(kDConnectStubID, (void **) &stub);
+ if (NS_SUCCEEDED(rv) && (stub->PeerID() == peer))
+ {
+ DConAddr p = stub->Instance();
+ writer.PutBytes(&p, sizeof(p));
+ }
+ else
+ {
+ // create instance wrapper
+
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo));
+ if (NS_FAILED(rv))
+ return rv;
+
+ DConnectInstance *wrapper = nsnull;
+
+ // first try to find an existing wrapper for the given object
+ if (!FindInstanceAndAddRef(peer, obj, &iid, &wrapper))
+ {
+ wrapper = new DConnectInstance(peer, iinfo, obj);
+ if (!wrapper)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = StoreInstance(wrapper);
+ if (NS_FAILED(rv))
+ {
+ delete wrapper;
+ return rv;
+ }
+
+ // reference the newly created wrapper
+ wrapper->AddRef();
+ }
+
+ // increase the second, IPC-only, reference counter (mandatory before
+ // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove
+ // the wrapper from the instance map on failure)
+ wrapper->AddRefIPC();
+
+ if (!wrappers.AppendElement(wrapper))
+ {
+ wrapper->ReleaseIPC();
+ wrapper->Release();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // wrapper remains referenced when passing it to the client
+ // (will be released upon DCON_OP_RELEASE)
+
+ // send address of the instance wrapper, and set the low bit to indicate
+ // to the remote party that this is a remote instance wrapper.
+ PtrBits bits = ((PtrBits)(uintptr_t) wrapper);
+ NS_ASSERTION((bits & PTRBITS_REMOTE_BIT) == 0, "remote bit wrong)");
+ bits |= PTRBITS_REMOTE_BIT;
+ writer.PutBytes(&bits, sizeof(bits));
+ }
+ NS_IF_RELEASE(stub);
+ }
+ return NS_OK;
+}
+
+// NOTE: peer and iid are ignored if bits doesn't contain PTRBITS_REMOTE_BIT
+nsresult
+ipcDConnectService::DeserializeInterfaceParamBits(PtrBits bits, PRUint32 peer,
+ const nsID &iid,
+ nsISupports *&obj)
+{
+ nsresult rv;
+
+ obj = nsnull;
+
+ if (bits & PTRBITS_REMOTE_BIT)
+ {
+ // pointer is to a remote object. we need to build a stub.
+
+ bits &= ~PTRBITS_REMOTE_BIT;
+
+ DConnectStub *stub;
+ rv = CreateStub(iid, peer, (DConAddr) bits, &stub);
+ if (NS_SUCCEEDED(rv))
+ obj = stub;
+ }
+ else if (bits)
+ {
+ // pointer is to one of our instance wrappers. Replace it with the
+ // real instance.
+
+ DConnectInstance *wrapper = (DConnectInstance *) bits;
+ // make sure we've been sent a valid wrapper
+ if (!CheckInstanceAndAddRef(wrapper, peer))
+ {
+ NS_NOTREACHED("instance wrapper not found");
+ return NS_ERROR_INVALID_ARG;
+ }
+ obj = wrapper->RealInstance();
+ NS_ADDREF(obj);
+ NS_RELEASE(wrapper);
+ }
+ else
+ {
+ // obj is alredy nsnull
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+#define EXCEPTION_STUB_ID \
+{ /* 70578d68-b25e-4370-a70c-89bbe56e6699 */ \
+ 0x70578d68, \
+ 0xb25e, \
+ 0x4370, \
+ {0xa7, 0x0c, 0x89, 0xbb, 0xe5, 0x6e, 0x66, 0x99} \
+}
+static NS_DEFINE_IID(kExceptionStubID, EXCEPTION_STUB_ID);
+
+// ExceptionStub is used to cache all primitive-typed bits of a remote nsIException
+// instance (such as the error message or line number) to:
+//
+// a) reduce the number of IPC calls;
+// b) make sure exception information is available to the calling party even if
+// the called party terminates immediately after returning an exception.
+// To achieve this, all cacheable information is serialized together with
+// the instance wrapper itself.
+
+class ExceptionStub : public nsIException
+{
+public:
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIEXCEPTION
+
+ ExceptionStub(const nsACString &aMessage, nsresult aResult,
+ const nsACString &aName, const nsACString &aFilename,
+ PRUint32 aLineNumber, PRUint32 aColumnNumber,
+ DConnectStub *aXcptStub)
+ : mMessage(aMessage), mResult(aResult)
+ , mName(aName), mFilename(aFilename)
+ , mLineNumber (aLineNumber), mColumnNumber (aColumnNumber)
+ , mXcptStub (aXcptStub) { NS_ASSERTION(aXcptStub, "NULL"); }
+
+ ~ExceptionStub() {}
+
+ nsIException *Exception() { return (nsIException *)(nsISupports *) mXcptStub; }
+ DConnectStub *Stub() { return mXcptStub; }
+
+private:
+
+ nsCString mMessage;
+ nsresult mResult;
+ nsCString mName;
+ nsCString mFilename;
+ PRUint32 mLineNumber;
+ PRUint32 mColumnNumber;
+ nsRefPtr<DConnectStub> mXcptStub;
+};
+
+NS_IMPL_THREADSAFE_ADDREF(ExceptionStub)
+NS_IMPL_THREADSAFE_RELEASE(ExceptionStub)
+
+NS_IMETHODIMP
+ExceptionStub::QueryInterface(const nsID &aIID, void **aInstancePtr)
+{
+ NS_ASSERTION(aInstancePtr,
+ "QueryInterface requires a non-NULL destination!");
+
+ // used to discover if this is an ExceptionStub instance.
+ if (aIID.Equals(kExceptionStubID))
+ {
+ *aInstancePtr = this;
+ NS_ADDREF_THIS();
+ return NS_OK;
+ }
+
+ // regular NS_IMPL_QUERY_INTERFACE1 sequence
+
+ nsISupports* foundInterface = 0;
+
+ if (aIID.Equals(NS_GET_IID(nsIException)))
+ foundInterface = NS_STATIC_CAST(nsIException*, this);
+ else
+ if (aIID.Equals(NS_GET_IID(nsISupports)))
+ foundInterface = NS_STATIC_CAST(nsISupports*,
+ NS_STATIC_CAST(nsIException *, this));
+ else
+ if (mXcptStub)
+ {
+ // ask the real nsIException object
+ return mXcptStub->QueryInterface(aIID, aInstancePtr);
+ }
+
+ nsresult status;
+ if (!foundInterface)
+ status = NS_NOINTERFACE;
+ else
+ {
+ NS_ADDREF(foundInterface);
+ status = NS_OK;
+ }
+ *aInstancePtr = foundInterface;
+ return status;
+}
+
+/* readonly attribute string message; */
+NS_IMETHODIMP ExceptionStub::GetMessage(char **aMessage)
+{
+ AssertReturn(aMessage, NS_ERROR_INVALID_POINTER);
+ *aMessage = ToNewCString(mMessage);
+ return NS_OK;
+}
+
+/* readonly attribute nsresult result; */
+NS_IMETHODIMP ExceptionStub::GetResult(nsresult *aResult)
+{
+ AssertReturn(aResult, NS_ERROR_INVALID_POINTER);
+ *aResult = mResult;
+ return NS_OK;
+}
+
+/* readonly attribute string name; */
+NS_IMETHODIMP ExceptionStub::GetName(char **aName)
+{
+ AssertReturn(aName, NS_ERROR_INVALID_POINTER);
+ *aName = ToNewCString(mName);
+ return NS_OK;
+}
+
+/* readonly attribute string filename; */
+NS_IMETHODIMP ExceptionStub::GetFilename(char **aFilename)
+{
+ AssertReturn(aFilename, NS_ERROR_INVALID_POINTER);
+ *aFilename = ToNewCString(mFilename);
+ return NS_OK;
+}
+
+/* readonly attribute PRUint32 lineNumber; */
+NS_IMETHODIMP ExceptionStub::GetLineNumber(PRUint32 *aLineNumber)
+{
+ AssertReturn(aLineNumber, NS_ERROR_INVALID_POINTER);
+ *aLineNumber = mLineNumber;
+ return NS_OK;
+}
+
+/* readonly attribute PRUint32 columnNumber; */
+NS_IMETHODIMP ExceptionStub::GetColumnNumber(PRUint32 *aColumnNumber)
+{
+ AssertReturn(aColumnNumber, NS_ERROR_INVALID_POINTER);
+ *aColumnNumber = mColumnNumber;
+ return NS_OK;
+}
+
+/* readonly attribute nsIStackFrame location; */
+NS_IMETHODIMP ExceptionStub::GetLocation(nsIStackFrame **aLocation)
+{
+ if (Exception())
+ return Exception()->GetLocation (aLocation);
+ return NS_ERROR_UNEXPECTED;
+}
+
+/* readonly attribute nsIException inner; */
+NS_IMETHODIMP ExceptionStub::GetInner(nsIException **aInner)
+{
+ if (Exception())
+ return Exception()->GetInner (aInner);
+ return NS_ERROR_UNEXPECTED;
+}
+
+/* readonly attribute nsISupports data; */
+NS_IMETHODIMP ExceptionStub::GetData(nsISupports * *aData)
+{
+ if (Exception())
+ return Exception()->GetData (aData);
+ return NS_ERROR_UNEXPECTED;
+}
+
+/* string toString (); */
+NS_IMETHODIMP ExceptionStub::ToString(char **_retval)
+{
+ if (Exception())
+ return Exception()->ToString (_retval);
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult
+ipcDConnectService::SerializeException(ipcMessageWriter &writer,
+ PRUint32 peer, nsIException *xcpt,
+ nsVoidArray &wrappers)
+{
+ PRBool cache_fields = PR_FALSE;
+
+ // first, seralize the nsIException pointer. The code is merely the same as
+ // in SerializeInterfaceParam() except that when the exception to serialize
+ // is an ExceptionStub instance and the real instance it stores as mXcpt
+ // is a DConnectStub corresponding to an object in the address space of the
+ // peer, we simply pass that object back instead of creating a new wrapper.
+
+ {
+ nsAutoLock lock (mLock);
+
+ if (mDisconnected)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (!xcpt)
+ {
+ // write null address
+#ifdef VBOX
+ // see ipcDConnectService::DeserializeException()!
+ PtrBits bits = 0;
+ writer.PutBytes(&bits, sizeof(bits));
+#else
+ writer.PutBytes(&xcpt, sizeof(xcpt));
+#endif
+ }
+ else
+ {
+ ExceptionStub *stub = nsnull;
+ nsresult rv = xcpt->QueryInterface(kExceptionStubID, (void **) &stub);
+ if (NS_SUCCEEDED(rv) && (stub->Stub()->PeerID() == peer))
+ {
+ // send the wrapper instance back to the peer
+ DConAddr p = stub->Stub()->Instance();
+ writer.PutBytes(&p, sizeof(p));
+ }
+ else
+ {
+ // create instance wrapper
+
+ const nsID &iid = nsIException::GetIID();
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ rv = GetInterfaceInfo(iid, getter_AddRefs(iinfo));
+ if (NS_FAILED(rv))
+ return rv;
+
+ DConnectInstance *wrapper = nsnull;
+
+ // first try to find an existing wrapper for the given object
+ if (!FindInstanceAndAddRef(peer, xcpt, &iid, &wrapper))
+ {
+ wrapper = new DConnectInstance(peer, iinfo, xcpt);
+ if (!wrapper)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = StoreInstance(wrapper);
+ if (NS_FAILED(rv))
+ {
+ delete wrapper;
+ return rv;
+ }
+
+ // reference the newly created wrapper
+ wrapper->AddRef();
+ }
+
+ // increase the second, IPC-only, reference counter (mandatory before
+ // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove
+ // the wrapper from the instance map on failure)
+ wrapper->AddRefIPC();
+
+ if (!wrappers.AppendElement(wrapper))
+ {
+ wrapper->ReleaseIPC();
+ wrapper->Release();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // wrapper remains referenced when passing it to the client
+ // (will be released upon DCON_OP_RELEASE)
+
+ // send address of the instance wrapper, and set the low bit to indicate
+ // to the remote party that this is a remote instance wrapper.
+ PtrBits bits = ((PtrBits)(uintptr_t) wrapper) | PTRBITS_REMOTE_BIT;
+ writer.PutBytes(&bits, sizeof(bits));
+
+ // we want to cache fields to minimize the number of IPC calls when
+ // accessing exception data on the peer side
+ cache_fields = PR_TRUE;
+ }
+ NS_IF_RELEASE(stub);
+ }
+ }
+
+ if (!cache_fields)
+ return NS_OK;
+
+ nsresult rv;
+ nsXPIDLCString str;
+ PRUint32 num;
+
+ // message
+ rv = xcpt->GetMessage(getter_Copies(str));
+ if (NS_SUCCEEDED (rv))
+ {
+ PRUint32 len = str.Length();
+ nsACString::const_iterator begin;
+ const char *data = str.BeginReading(begin).get();
+ writer.PutInt32(len);
+ writer.PutBytes(data, len);
+ }
+ else
+ writer.PutInt32(0);
+
+ // result
+ nsresult res = 0;
+ xcpt->GetResult(&res);
+ writer.PutInt32(res);
+
+ // name
+ rv = xcpt->GetName(getter_Copies(str));
+ if (NS_SUCCEEDED (rv))
+ {
+ PRUint32 len = str.Length();
+ nsACString::const_iterator begin;
+ const char *data = str.BeginReading(begin).get();
+ writer.PutInt32(len);
+ writer.PutBytes(data, len);
+ }
+ else
+ writer.PutInt32(0);
+
+ // filename
+ rv = xcpt->GetFilename(getter_Copies(str));
+ if (NS_SUCCEEDED (rv))
+ {
+ PRUint32 len = str.Length();
+ nsACString::const_iterator begin;
+ const char *data = str.BeginReading(begin).get();
+ writer.PutInt32(len);
+ writer.PutBytes(data, len);
+ }
+ else
+ writer.PutInt32(0);
+
+ // lineNumber
+ num = 0;
+ xcpt->GetLineNumber(&num);
+ writer.PutInt32(num);
+
+ // columnNumber
+ num = 0;
+ xcpt->GetColumnNumber(&num);
+ writer.PutInt32(num);
+
+ return writer.HasError() ? NS_ERROR_OUT_OF_MEMORY : NS_OK;
+}
+
+nsresult
+ipcDConnectService::DeserializeException(ipcMessageReader &reader,
+ PRUint32 peer,
+ nsIException **xcpt)
+{
+ NS_ASSERTION (xcpt, "NULL");
+ if (!xcpt)
+ return NS_ERROR_INVALID_POINTER;
+
+ nsresult rv;
+ PRUint32 len;
+
+ PtrBits bits = 0;
+ reader.GetBytes(&bits, sizeof(DConAddr));
+ if (reader.HasError())
+ return NS_ERROR_INVALID_ARG;
+
+ if (bits & PTRBITS_REMOTE_BIT)
+ {
+ // pointer is a peer-side exception instance wrapper,
+ // read cahced exception data and create a stub for it.
+
+ nsCAutoString message;
+ len = reader.GetInt32();
+ if (len)
+ {
+ message.SetLength(len);
+ char *buf = message.BeginWriting();
+ reader.GetBytes(buf, len);
+ }
+
+ nsresult result = reader.GetInt32();
+
+ nsCAutoString name;
+ len = reader.GetInt32();
+ if (len)
+ {
+ name.SetLength(len);
+ char *buf = name.BeginWriting();
+ reader.GetBytes(buf, len);
+ }
+
+ nsCAutoString filename;
+ len = reader.GetInt32();
+ if (len)
+ {
+ filename.SetLength(len);
+ char *buf = filename.BeginWriting();
+ reader.GetBytes(buf, len);
+ }
+
+ PRUint32 lineNumber = reader.GetInt32();
+ PRUint32 columnNumber = reader.GetInt32();
+
+ if (reader.HasError())
+ rv = NS_ERROR_INVALID_ARG;
+ else
+ {
+ DConAddr addr = (DConAddr) (bits & ~PTRBITS_REMOTE_BIT);
+ nsRefPtr<DConnectStub> stub;
+ rv = CreateStub(nsIException::GetIID(), peer, addr,
+ getter_AddRefs(stub));
+ if (NS_SUCCEEDED(rv))
+ {
+ // create a special exception "stub" with cached error info
+ ExceptionStub *xcptStub =
+ new ExceptionStub (message, result,
+ name, filename,
+ lineNumber, columnNumber,
+ stub);
+ if (xcptStub)
+ {
+ *xcpt = xcptStub;
+ NS_ADDREF(xcptStub);
+ }
+ else
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+ else if (bits)
+ {
+ // pointer is to our instance wrapper for nsIException we've sent before
+ // (the remote method we've called had called us back and got an exception
+ // from us that it decided to return as its own result). Replace it with
+ // the real instance.
+ DConnectInstance *wrapper = (DConnectInstance *) bits;
+ if (CheckInstanceAndAddRef(wrapper, peer))
+ {
+ *xcpt = (nsIException *) wrapper->RealInstance();
+ NS_ADDREF(wrapper->RealInstance());
+ wrapper->Release();
+ rv = NS_OK;
+ }
+ else
+ {
+ NS_NOTREACHED("instance wrapper not found");
+ rv = NS_ERROR_INVALID_ARG;
+ }
+ }
+ else
+ {
+ // the peer explicitly passed us a NULL exception to indicate that the
+ // exception on the current thread should be reset
+ *xcpt = NULL;
+ return NS_OK;
+ }
+
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+DConnectStub::~DConnectStub()
+{
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name = NULL;
+ mIInfo->GetNameShared(&name);
+ LOG(("{%p} DConnectStub::<dtor>(): peer=%d instance=0x%Lx {%s}\n",
+ this, mPeerID, mInstance, name));
+ }
+#endif
+
+ // release the cached nsISupports instance if it's not the same object
+ if (mCachedISupports != 0 && mCachedISupports != this)
+ NS_RELEASE(mCachedISupports);
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+DConnectStub::AddRef()
+{
+ nsrefcnt count;
+ count = PR_AtomicIncrement((PRInt32*)&mRefCnt);
+ NS_LOG_ADDREF(this, count, "DConnectStub", sizeof(*this));
+ return count;
+}
+
+NS_IMETHODIMP_(nsrefcnt)
+DConnectStub::Release()
+{
+ nsrefcnt count;
+
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ if (dConnect)
+ {
+ // lock the stub lock on every release to make sure that once the counter
+ // drops to zero, we delete the stub from the set of stubs before a new
+ // request to create a stub on other thread tries to find the existing
+ // stub in the set (wchich could otherwise AddRef the object after it had
+ // Released to zero and pass it to the client right before its
+ // destruction).
+ nsAutoLock stubLock (dConnect->StubLock());
+
+ count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
+ NS_LOG_RELEASE(this, count, "DConnectStub");
+
+
+ #ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ mIInfo->GetNameShared(&name);
+ LOG(("{%p} DConnectStub::Release(): peer=%d instance=0x%Lx {%s}, new count=%d\n",
+ this, mPeerID, mInstance, name, count));
+ }
+ #endif
+
+ // mRefCntLevels may already be empty here (due to the "stabilize" trick below)
+ if (mRefCntLevels.GetSize() > 0)
+ {
+ nsrefcnt top = (nsrefcnt) (long) mRefCntLevels.Peek();
+ NS_ASSERTION(top <= count + 1, "refcount is beyond the top level");
+
+ if (top == count + 1)
+ {
+ // refcount dropped to a value stored in ipcDConnectService::CreateStub.
+ // Send a RELEASE request to the peer (see also AddRefIPC).
+
+ // remove the top refcount value
+ mRefCntLevels.Pop();
+
+ if (0 == count)
+ {
+ // this is the last reference, remove from the set before we leave
+ // the lock, to provide atomicity of these two operations
+ dConnect->DeleteStub (this);
+
+ NS_ASSERTION(mRefCntLevels.GetSize() == 0, "refcnt levels are still left");
+ }
+
+ // leave the lock before sending a message
+ stubLock.unlock();
+
+ nsresult rv;
+
+ DConnectRelease msg;
+ msg.opcode_major = DCON_OP_RELEASE;
+ msg.opcode_minor = 0;
+ msg.flags = 0;
+ msg.request_index = 0; // not used, set to some unused value
+ msg.instance = mInstance;
+
+ // fire off asynchronously... we don't expect any response to this message.
+ rv = IPC_SendMessage(mPeerID, kDConnectTargetID,
+ (const PRUint8 *) &msg, sizeof(msg));
+ if (NS_FAILED(rv))
+ NS_WARNING("failed to send RELEASE event");
+ }
+ }
+ }
+ else
+ {
+ count = PR_AtomicDecrement((PRInt32 *)&mRefCnt);
+ NS_LOG_RELEASE(this, count, "DConnectStub");
+ }
+
+ if (0 == count)
+ {
+ mRefCnt = 1; /* stabilize */
+ delete this;
+ return 0;
+ }
+
+ return count;
+}
+
+NS_IMETHODIMP
+DConnectStub::QueryInterface(const nsID &aIID, void **aInstancePtr)
+{
+ // used to discover if this is a DConnectStub instance.
+ if (aIID.Equals(kDConnectStubID))
+ {
+ *aInstancePtr = this;
+ NS_ADDREF_THIS();
+ return NS_OK;
+ }
+
+ // In order to truely support the COM Identity Rule across processes,
+ // we need to make the following code work:
+ //
+ // IFoo *foo = ...
+ // nsISupports unk;
+ // foo->QueryInterface(NS_GET_IID(nsISupports), (void **) &unk);
+ // unk->Release();
+ // nsISupports unk2;
+ // foo->QueryInterface(NS_GET_IID(nsISupports), (void **) &unk2);
+ // Assert (unk == unk2);
+ //
+ // I.e. querying nsISupports on the same object must always return the same
+ // pointer, even if the nsISupports object returned for the first time is
+ // released before it is requested for the second time, as long as the
+ // original object is kept alive (referenced by the client) between these
+ // two queries.
+ //
+ // This is done by remembering the nsISupports stub returned by the peer
+ // when nsISupports is queried for the first time. The remembered stub, when
+ // it is not the same as this object, is strongly referenced in order to
+ // keep it alive (and therefore have the same pointer value) as long as this
+ // object is alive.
+ //
+ // Besides supporting the Identity Rule, this also reduces the number of IPC
+ // calls, since an IPC call requesting nsISupports will be done only once
+ // per every stub object.
+
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ NS_ASSERTION(dConnect, "no ipcDConnectService (uninitialized?)");
+ if (!dConnect)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv;
+ PRBool needISupports = aIID.Equals(NS_GET_IID(nsISupports));
+
+ if (needISupports)
+ {
+ // XXX it would be sufficient to use cmpxchg here to protect access to
+ // mCachedISupports, but NSPR doesn't provide cross-platform cmpxchg
+ // functionality, so we have to use a shared lock instead...
+ PR_Lock(dConnect->StubQILock());
+
+ // check if we have already got a nsISupports stub for this object
+ if (mCachedISupports != 0)
+ {
+ *aInstancePtr = mCachedISupports;
+ NS_ADDREF(mCachedISupports);
+
+ PR_Unlock(dConnect->StubQILock());
+ return NS_OK;
+ }
+
+ // check if this object is nsISupports itself
+ {
+ nsIID *iid = 0;
+ rv = mIInfo->GetInterfaceIID(&iid);
+ NS_ASSERTION(NS_SUCCEEDED(rv) && iid,
+ "nsIInterfaceInfo::GetInterfaceIID failed");
+ if (NS_SUCCEEDED(rv) && iid &&
+ iid->Equals(NS_GET_IID(nsISupports)))
+ {
+ nsMemory::Free((void*)iid);
+
+ // nsISupports is queried on nsISupports, return ourselves
+ *aInstancePtr = this;
+ NS_ADDREF_THIS();
+ // cache ourselves weakly
+ mCachedISupports = this;
+
+ PR_Unlock(dConnect->StubQILock());
+ return NS_OK;
+ }
+ if (iid)
+ nsMemory::Free((void*)iid);
+ }
+
+ // stub lock remains held until we've queried the peer
+ }
+
+ // else, we need to query the peer object by making an IPC call
+
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ mIInfo->GetNameShared(&name);
+ const char *nameQ;
+ nsCOMPtr <nsIInterfaceInfo> iinfoQ;
+ dConnect->GetInterfaceInfo(aIID, getter_AddRefs(iinfoQ));
+ if (iinfoQ) {
+ iinfoQ->GetNameShared(&nameQ);
+ LOG(("calling QueryInterface {%s} on peer object "
+ "(stub=%p, instance=0x%Lx {%s})\n",
+ nameQ, this, mInstance, name));
+ }
+ }
+#endif
+
+ DConnectSetupQueryInterface msg;
+ msg.opcode_minor = DCON_OP_SETUP_QUERY_INTERFACE;
+ msg.iid = aIID;
+ msg.instance = mInstance;
+
+ rv = SetupPeerInstance(mPeerID, &msg, sizeof(msg), aInstancePtr);
+
+ if (needISupports)
+ {
+ if (NS_SUCCEEDED(rv))
+ {
+ // cache the nsISupports object (SetupPeerInstance returns DConnectStub)
+ mCachedISupports = (DConnectStub *) *aInstancePtr;
+ // use a weak reference if nsISupports is the same object as us
+ if (this != mCachedISupports)
+ NS_ADDREF(mCachedISupports);
+ }
+
+ PR_Unlock(dConnect->StubQILock());
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+DConnectStub::GetInterfaceInfo(nsIInterfaceInfo **aInfo)
+{
+ NS_ADDREF(*aInfo = mIInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DConnectStub::CallMethod(PRUint16 aMethodIndex,
+ const nsXPTMethodInfo *aInfo,
+ nsXPTCMiniVariant *aParams)
+{
+ LOG(("DConnectStub::CallMethod [methodIndex=%hu]\n", aMethodIndex));
+
+ nsresult rv;
+
+ // reset the exception early. this is necessary because we may return a
+ // failure from here without setting an exception (which might be expected
+ // by the caller to detect the error origin: the interface we are stubbing
+ // may indicate in some way that it always sets the exception info on
+ // failure, therefore an "infoless" failure means the origin is RPC).
+ // besides that, resetting the excetion before every IPC call is exactly the
+ // same thing as Win32 RPC does, so doing this is useful for getting
+ // similarity in behaviors.
+
+ nsCOMPtr <nsIExceptionService> es;
+ es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED (rv))
+ return rv;
+ nsCOMPtr <nsIExceptionManager> em;
+ rv = es->GetCurrentExceptionManager (getter_AddRefs(em));
+ if (NS_FAILED (rv))
+ return rv;
+ rv = em->SetCurrentException(NULL);
+ if (NS_FAILED (rv))
+ return rv;
+
+ // ensure ipcDConnectService is not deleted before we finish
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ if (!dConnect)
+ return NS_ERROR_FAILURE;
+
+ // dump arguments
+
+ PRUint8 i, paramCount = aInfo->GetParamCount();
+
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ GetInterfaceInfo(getter_AddRefs(iinfo));
+ iinfo->GetNameShared(&name);
+ LOG((" instance=0x%Lx {%s}\n", mInstance, name));
+ LOG((" name=%s\n", aInfo->GetName()));
+ LOG((" param-count=%u\n", (PRUint32) paramCount));
+ }
+#endif
+
+
+ ipcMessageWriter writer(16 * paramCount);
+
+ // INVOKE message header
+ DConnectInvoke invoke;
+ invoke.opcode_major = DCON_OP_INVOKE;
+ invoke.opcode_minor = 0;
+ invoke.flags = 0;
+ invoke.request_index = NewRequestIndex();
+ invoke.instance = mInstance;
+ invoke.method_index = aMethodIndex;
+
+ LOG((" request-index=%d\n", (PRUint32) invoke.request_index));
+
+ writer.PutBytes(&invoke, sizeof(invoke));
+
+ // list of wrappers that get created during parameter serialization. if we
+ // are unable to send the INVOKE message, then we'll clean these up.
+ nsVoidArray wrappers;
+
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
+
+ if (paramInfo.IsIn() && !paramInfo.IsDipper())
+ {
+ const nsXPTType &type = paramInfo.GetType();
+
+ if (type.IsInterfacePointer())
+ {
+ nsID iid;
+ rv = dConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
+ aMethodIndex, aParams, PR_FALSE, iid);
+ if (NS_SUCCEEDED(rv))
+ rv = dConnect->SerializeInterfaceParam(writer, mPeerID, iid,
+ (nsISupports *) aParams[i].val.p,
+ wrappers);
+ }
+ else
+ rv = SerializeParam(writer, type, aParams[i]);
+
+ AssertMsgBreak(NS_SUCCEEDED(rv), ("i=%d rv=%#x\n", i, rv));
+ }
+ else if ((paramInfo.IsOut() || paramInfo.IsRetval()) && !aParams[i].val.p)
+ {
+ // report error early if NULL pointer is passed as an output parameter
+ rv = NS_ERROR_NULL_POINTER;
+ AssertMsgFailedBreak(("i=%d IsOut=%d IsRetval=%d NS_ERROR_NULL_POINTER\n", i, paramInfo.IsOut(), paramInfo.IsRetval()));
+ break;
+ }
+ }
+
+ if (NS_FAILED(rv))
+ {
+ // INVOKE message wasn't sent; clean up wrappers
+ dConnect->ReleaseWrappers(wrappers, mPeerID);
+ return rv;
+ }
+
+ // serialize input array parameters after everything else since the
+ // deserialization procedure will need to get a size_is value which may be
+ // stored in any preceeding or following param
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
+
+ if (paramInfo.GetType().IsArray() &&
+ paramInfo.IsIn() && !paramInfo.IsDipper())
+ {
+ rv = SerializeArrayParam(dConnect, writer, mPeerID, mIInfo, aMethodIndex,
+ *aInfo, aParams, PR_FALSE, paramInfo,
+ aParams[i].val.p, wrappers);
+ if (NS_FAILED(rv))
+ {
+ // INVOKE message wasn't sent; clean up wrappers
+ dConnect->ReleaseWrappers(wrappers, mPeerID);
+ return rv;
+ }
+ }
+ }
+
+ // temporarily disable the DConnect target observer to block normal processing
+ // of pending messages through the event queue.
+ IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kDConnectTargetID);
+
+ rv = IPC_SendMessage(mPeerID, kDConnectTargetID,
+ writer.GetBuffer(),
+ writer.GetSize());
+ LOG(("DConnectStub::CallMethod: IPC_SendMessage()=%08X\n", rv));
+ if (NS_FAILED(rv))
+ {
+ // INVOKE message wasn't delivered; clean up wrappers
+ dConnect->ReleaseWrappers(wrappers, mPeerID);
+ return rv;
+ }
+
+ // now, we wait for the method call to complete. during that time, it's
+ // possible that we'll receive other method call requests. we'll process
+ // those while waiting for out method call to complete. it's critical that
+ // we do so since those other method calls might need to complete before
+ // out method call can complete!
+
+ DConnectInvokeCompletion completion(mPeerID, &invoke);
+
+ do
+ {
+ rv = IPC_WaitMessage(IPC_SENDER_ANY, kDConnectTargetID,
+ &completion.GetSelector(), &completion,
+ DCON_WAIT_TIMEOUT);
+ LOG(("DConnectStub::CallMethod: IPC_WaitMessage()=%08X\n", rv));
+ if (NS_FAILED(rv))
+ {
+ // INVOKE message wasn't received; clean up wrappers
+ dConnect->ReleaseWrappers(wrappers, mPeerID);
+ return rv;
+ }
+ }
+ while (completion.IsPending());
+
+ ipcMessageReader reader(completion.Params(), completion.ParamsLen());
+
+ rv = completion.GetResult();
+ if (NS_SUCCEEDED(rv))
+ {
+ PRUint8 i;
+
+ // handle out-params and retvals: DCON_OP_INVOKE_REPLY has the data
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
+
+ if (paramInfo.IsOut() || paramInfo.IsRetval())
+ DeserializeResult(reader, paramInfo.GetType(), aParams[i]);
+ }
+
+ // fixup any interface pointers using a second pass so we can properly
+ // handle INTERFACE_IS referencing an IID that is an out param! This pass is
+ // also used to deserialize arrays (array data goes after all other params).
+ for (i=0; i<paramCount && NS_SUCCEEDED(rv); ++i)
+ {
+ const nsXPTParamInfo &paramInfo = aInfo->GetParam(i);
+ if ((paramInfo.IsOut() || paramInfo.IsRetval()) && aParams[i].val.p)
+ {
+ const nsXPTType &type = paramInfo.GetType();
+ if (type.IsInterfacePointer())
+ {
+ // grab the DConAddr value temporarily stored in the param, restore
+ // the pointer and free the temporarily allocated memory.
+ DConAddrPlusPtr *dptr = (DConAddrPlusPtr *)aParams[i].val.p;
+ PtrBits bits = dptr->addr;
+ aParams[i].val.p = dptr->p;
+ nsMemory::Free((void *)dptr);
+
+ // DeserializeInterfaceParamBits needs IID only if it's a remote object
+ nsID iid;
+ if (bits & PTRBITS_REMOTE_BIT)
+ rv = dConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
+ aMethodIndex, aParams, PR_FALSE,
+ iid);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsISupports *obj = nsnull;
+ rv = dConnect->DeserializeInterfaceParamBits(bits, mPeerID, iid, obj);
+ if (NS_SUCCEEDED(rv))
+ *(void **)aParams[i].val.p = obj;
+ }
+ }
+ else if (type.IsArray())
+ {
+ void *array = nsnull;
+ rv = DeserializeArrayParam(dConnect, reader, mPeerID, mIInfo,
+ aMethodIndex, *aInfo, aParams, PR_FALSE,
+ paramInfo, PR_TRUE, array);
+ if (NS_SUCCEEDED(rv))
+ *(void **)aParams[i].val.p = array;
+ }
+ }
+ }
+ }
+
+ if (completion.Reply()->flags & DCON_OP_FLAGS_REPLY_EXCEPTION)
+ {
+ LOG(("got nsIException instance, will create a stub\n"));
+
+ nsIException *xcpt = nsnull;
+ rv = dConnect->DeserializeException (reader, mPeerID, &xcpt);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = em->SetCurrentException(xcpt);
+ NS_IF_RELEASE(xcpt);
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to deserialize/set exception");
+ }
+
+ return NS_SUCCEEDED(rv) ? completion.GetResult() : rv;
+}
+
+//-----------------------------------------------------------------------------
+
+class DConnectSetupCompletion : public DConnectCompletion
+{
+public:
+ DConnectSetupCompletion(PRUint32 peer, const DConnectSetup *setup)
+ : DConnectCompletion(peer, DCON_OP_SETUP_REPLY, setup->request_index)
+ , mSetup(setup)
+ , mStatus(NS_OK)
+ {}
+
+ void OnResponseAvailable(PRUint32 sender, const DConnectOp *op, PRUint32 opLen)
+ {
+ if (op->opcode_major != DCON_OP_SETUP_REPLY)
+ {
+ NS_NOTREACHED("unexpected response");
+ mStatus = NS_ERROR_UNEXPECTED;
+ return;
+ }
+
+ if (opLen < sizeof(DConnectSetupReply))
+ {
+ NS_NOTREACHED("unexpected response size");
+ mStatus = NS_ERROR_UNEXPECTED;
+ return;
+ }
+
+ const DConnectSetupReply *reply = (const DConnectSetupReply *) op;
+
+ LOG(("got SETUP_REPLY: status=%x instance=0x%Lx\n", reply->status, reply->instance));
+
+ mStatus = reply->status;
+
+ if (NS_SUCCEEDED(reply->status))
+ {
+ // ensure ipcDConnectService is not deleted before we finish
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ nsresult rv;
+ if (dConnect)
+ rv = dConnect->CreateStub(mSetup->iid, sender, reply->instance,
+ getter_AddRefs(mStub));
+ else
+ rv = NS_ERROR_FAILURE;
+ if (NS_FAILED(rv))
+ mStatus = rv;
+ }
+
+ if (reply->flags & DCON_OP_FLAGS_REPLY_EXCEPTION)
+ {
+ const PRUint8 *params = ((const PRUint8 *) op) + sizeof (DConnectSetupReply);
+ const PRUint32 paramsLen = opLen - sizeof (DConnectSetupReply);
+
+ ipcMessageReader reader(params, paramsLen);
+
+ LOG(("got nsIException instance, will create a stub\n"));
+
+ nsresult rv;
+ nsCOMPtr <nsIExceptionService> es;
+ es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIExceptionManager> em;
+ rv = es->GetCurrentExceptionManager (getter_AddRefs(em));
+ if (NS_SUCCEEDED(rv))
+ {
+ // ensure ipcDConnectService is not deleted before we finish
+ nsRefPtr <ipcDConnectService> dConnect (ipcDConnectService::GetInstance());
+ if (dConnect)
+ {
+ nsIException *xcpt = nsnull;
+ rv = dConnect->DeserializeException (reader, sender, &xcpt);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = em->SetCurrentException(xcpt);
+ NS_IF_RELEASE(xcpt);
+ }
+ }
+ else
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to deserialize/set exception");
+ if (NS_FAILED(rv))
+ mStatus = rv;
+ }
+ }
+
+ nsresult GetStub(void **aInstancePtr)
+ {
+ if (NS_FAILED(mStatus))
+ return mStatus;
+
+ DConnectStub *stub = mStub;
+ NS_IF_ADDREF(stub);
+ *aInstancePtr = stub;
+ return NS_OK;
+ }
+
+private:
+ const DConnectSetup *mSetup;
+ nsresult mStatus;
+ nsRefPtr<DConnectStub> mStub;
+};
+
+// static
+nsresult
+SetupPeerInstance(PRUint32 aPeerID, DConnectSetup *aMsg, PRUint32 aMsgLen,
+ void **aInstancePtr)
+{
+ *aInstancePtr = nsnull;
+
+ aMsg->opcode_major = DCON_OP_SETUP;
+ aMsg->flags = 0;
+ aMsg->request_index = NewRequestIndex();
+
+ // temporarily disable the DConnect target observer to block normal processing
+ // of pending messages through the event queue.
+ IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kDConnectTargetID);
+
+ // send SETUP message, expect SETUP_REPLY
+
+ nsresult rv = IPC_SendMessage(aPeerID, kDConnectTargetID,
+ (const PRUint8 *) aMsg, aMsgLen);
+ if (NS_FAILED(rv))
+ return rv;
+
+ DConnectSetupCompletion completion(aPeerID, aMsg);
+
+ // need to allow messages from other clients to be processed immediately
+ // to avoid distributed dead locks. the completion's OnMessageAvailable
+ // will call our default OnMessageAvailable if it receives any message
+ // other than the one for which it is waiting.
+
+ do
+ {
+ rv = IPC_WaitMessage(IPC_SENDER_ANY, kDConnectTargetID,
+ &completion.GetSelector(), &completion,
+ DCON_WAIT_TIMEOUT);
+ if (NS_FAILED(rv))
+ break;
+
+ rv = completion.GetStub(aInstancePtr);
+ }
+ while (NS_SUCCEEDED(rv) && *aInstancePtr == nsnull);
+
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(DCONNECT_MULTITHREADED) && !defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+class DConnectWorker : public nsIRunnable
+{
+public:
+ // no reference counting
+ NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
+ NS_IMETHOD_(nsrefcnt) Release() { return 1; }
+ NS_IMETHOD QueryInterface(const nsIID &aIID, void **aInstancePtr);
+
+ NS_DECL_NSIRUNNABLE
+
+ DConnectWorker(ipcDConnectService *aDConnect) : mDConnect (aDConnect), mIsRunnable (PR_FALSE) {}
+ NS_HIDDEN_(nsresult) Init();
+ NS_HIDDEN_(void) Join() { mThread->Join(); };
+ NS_HIDDEN_(bool) IsRunning() { return mIsRunnable; };
+
+private:
+ nsCOMPtr <nsIThread> mThread;
+ ipcDConnectService *mDConnect;
+
+ // Indicate if thread might be quickly joined on shutdown.
+ volatile bool mIsRunnable;
+};
+
+NS_IMPL_QUERY_INTERFACE1(DConnectWorker, nsIRunnable)
+
+nsresult
+DConnectWorker::Init()
+{
+ return NS_NewThread(getter_AddRefs(mThread), this, 0, PR_JOINABLE_THREAD);
+}
+
+NS_IMETHODIMP
+DConnectWorker::Run()
+{
+ LOG(("DConnect Worker thread started.\n"));
+
+ mIsRunnable = PR_TRUE;
+
+ nsAutoMonitor mon(mDConnect->mPendingMon);
+
+ while (!mDConnect->mDisconnected)
+ {
+ DConnectRequest *request = mDConnect->mPendingQ.First();
+ if (!request)
+ {
+ mDConnect->mWaitingWorkers++;
+ {
+ // Note: we attempt to enter mWaitingWorkersMon from under mPendingMon
+ // here, but it should be safe because it's the only place where it
+ // happens. We could exit mPendingMon first, but we need to wait on it
+ // shorltly afterwards, which in turn will require us to enter it again
+ // just to exit immediately and start waiting. This seems to me a bit
+ // stupid (exit->enter->exit->wait).
+ nsAutoMonitor workersMon(mDConnect->mWaitingWorkersMon);
+ workersMon.NotifyAll();
+ }
+
+ nsresult rv = mon.Wait();
+ mDConnect->mWaitingWorkers--;
+
+ if (NS_FAILED(rv))
+ break;
+ }
+ else
+ {
+ LOG(("DConnect Worker thread got request.\n"));
+
+ // remove the request from the queue
+ mDConnect->mPendingQ.RemoveFirst();
+
+ PRBool pendingQEmpty = mDConnect->mPendingQ.IsEmpty();
+ mon.Exit();
+
+ if (pendingQEmpty)
+ {
+ nsAutoMonitor workersMon(mDConnect->mWaitingWorkersMon);
+ workersMon.NotifyAll();
+ }
+
+ // request is processed outside the queue monitor
+ mDConnect->OnIncomingRequest(request->peer, request->op, request->opLen);
+ delete request;
+
+ mon.Enter();
+ }
+ }
+
+ mIsRunnable = PR_FALSE;
+
+ LOG(("DConnect Worker thread stopped.\n"));
+ return NS_OK;
+}
+
+// called only on DConnect message thread
+nsresult
+ipcDConnectService::CreateWorker()
+{
+ DConnectWorker *worker = new DConnectWorker(this);
+ if (!worker)
+ return NS_ERROR_OUT_OF_MEMORY;
+ nsresult rv = worker->Init();
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoLock lock(mLock);
+ /* tracking an illegal join in Shutdown. */
+ NS_ASSERTION(!mDisconnected, "CreateWorker racing Shutdown");
+ if (!mWorkers.AppendElement(worker))
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (NS_FAILED(rv))
+ delete worker;
+ return rv;
+}
+
+#endif // defined(DCONNECT_MULTITHREADED) && !defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+//-----------------------------------------------------------------------------
+
+ipcDConnectService::ipcDConnectService()
+ : mLock(NULL)
+ , mStubLock(NULL)
+ , mDisconnected(PR_TRUE)
+ , mStubQILock(NULL)
+#if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+ , mhReqPool(NIL_RTREQPOOL)
+#endif
+{
+}
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+EnumerateInstanceMapAndDelete (const DConnectInstanceKey::Key &aKey,
+ DConnectInstance *aData,
+ void *userArg)
+{
+ // this method is to be called on ipcDConnectService shutdown only
+ // (after which no DConnectInstances may exist), so forcibly delete them
+ // disregarding the reference counter
+
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ aData->InterfaceInfo()->GetNameShared(&name);
+ LOG(("ipcDConnectService: WARNING: deleting unreleased "
+ "instance=%p iface=%p {%s}\n", aData, aData->RealInstance(), name));
+ }
+#endif
+
+ delete aData;
+ return PL_DHASH_NEXT;
+}
+
+ipcDConnectService::~ipcDConnectService()
+{
+ if (!mDisconnected)
+ Shutdown();
+
+ mInstance = nsnull;
+ PR_DestroyLock(mStubQILock);
+ PR_DestroyLock(mStubLock);
+ PR_DestroyLock(mLock);
+#if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+ RTReqPoolRelease(mhReqPool);
+ mhReqPool = NIL_RTREQPOOL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+ipcDConnectService::Init()
+{
+ nsresult rv;
+
+ LOG(("ipcDConnectService::Init.\n"));
+
+ rv = IPC_DefineTarget(kDConnectTargetID, this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = IPC_AddClientObserver(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mLock = PR_NewLock();
+ if (!mLock)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (!mInstances.Init())
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (!mInstanceSet.Init())
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mStubLock = PR_NewLock();
+ if (!mStubLock)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (!mStubs.Init())
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mIIM = do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mStubQILock = PR_NewLock();
+ if (!mStubQILock)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+ int vrc = RTReqPoolCreate(1024 /*cMaxThreads*/, 10*RT_MS_1SEC /*cMsMinIdle*/,
+ 8 /*cThreadsPushBackThreshold */, RT_MS_1SEC /* cMsMaxPushBack */,
+ "DCon", &mhReqPool);
+ if (RT_FAILURE(vrc))
+ {
+ mhReqPool = NIL_RTREQPOOL;
+ return NS_ERROR_FAILURE;
+ }
+
+ mDisconnected = PR_FALSE;
+
+# else
+
+ mPendingMon = nsAutoMonitor::NewMonitor("DConnect pendingQ monitor");
+ if (!mPendingMon)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mWaitingWorkers = 0;
+
+ mWaitingWorkersMon = nsAutoMonitor::NewMonitor("DConnect waiting workers monitor");
+ if (!mWaitingWorkersMon)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ /* The DConnectWorker::Run method checks the ipcDConnectService::mDisconnected.
+ * So mDisconnect must be set here to avoid an immediate exit of the worker thread.
+ */
+ mDisconnected = PR_FALSE;
+
+ // create a single worker thread
+ rv = CreateWorker();
+ if (NS_FAILED(rv))
+ {
+ mDisconnected = PR_TRUE;
+ return rv;
+ }
+
+# endif
+#else
+
+ mDisconnected = PR_FALSE;
+
+#endif
+
+ mInstance = this;
+
+ LOG(("ipcDConnectService::Init NS_OK.\n"));
+ return NS_OK;
+}
+
+void
+ipcDConnectService::Shutdown()
+{
+ {
+ // set the disconnected flag to make sensitive public methods
+ // unavailale from other (non worker) threads.
+ nsAutoLock lock(mLock);
+ mDisconnected = PR_TRUE;
+ }
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+# if defined(DCONNECT_STATS)
+ fprintf(stderr, "ipcDConnectService Stats\n");
+ fprintf(stderr,
+ " => number of worker threads: %llu (created %llu)\n"
+ " => requests processed: %llu\n"
+ " => avg requests process time: %llu ns\n"
+ " => avg requests waiting time: %llu ns\n",
+ RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_THREADS),
+ RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_THREADS_CREATED),
+ RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_REQUESTS_PROCESSED),
+ RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_NS_AVERAGE_REQ_PROCESSING),
+ RTReqPoolGetStat(mhReqPool, RTREQPOOLSTAT_NS_AVERAGE_REQ_QUEUED)
+ );
+# endif
+
+ RTReqPoolRelease(mhReqPool);
+ mhReqPool = NIL_RTREQPOOL;
+
+# else
+
+ {
+ // remove all pending messages and wake up all workers.
+ // mDisconnected is true here and they will terminate execution after
+ // processing the last request.
+ nsAutoMonitor mon(mPendingMon);
+ mPendingQ.DeleteAll();
+ mon.NotifyAll();
+ }
+
+#if defined(DCONNECT_STATS)
+ fprintf(stderr, "ipcDConnectService Stats\n");
+ fprintf(stderr, " => number of worker threads: %d\n", mWorkers.Count());
+ LOG(("ipcDConnectService Stats\n"));
+ LOG((" => number of worker threads: %d\n", mWorkers.Count()));
+#endif
+
+
+ // Iterate over currently running worker threads
+ // during VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS, join() those who
+ // exited a working loop and abandon ones which have not
+ // managed to do that when timeout occurred.
+ LOG(("Worker threads: %d\n", mWorkers.Count()));
+ uint64_t tsStart = RTTimeMilliTS();
+ while ((tsStart + VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS ) > RTTimeMilliTS() && mWorkers.Count() > 0)
+ {
+ // Some array elements might be deleted while iterating. Going from the last
+ // to the first array element (intentionally) in order to do not conflict with
+ // array indexing once element is deleted.
+ for (int i = mWorkers.Count() - 1; i >= 0; i--)
+ {
+ DConnectWorker *worker = NS_STATIC_CAST(DConnectWorker *, mWorkers[i]);
+ if (worker->IsRunning() == PR_FALSE)
+ {
+ LOG(("Worker %p joined.\n", worker));
+ worker->Join();
+ delete worker;
+ mWorkers.RemoveElementAt(i);
+ }
+ }
+
+ /* Double-ckeck if we already allowed to quit. */
+ if ((tsStart + VBOX_XPCOM_SHUTDOWN_TIMEOUT_MS ) < RTTimeMilliTS() || mWorkers.Count() == 0)
+ break;
+
+ // Relax a bit before the next round.
+ RTThreadSleep(10);
+ }
+
+ LOG(("There are %d thread(s) left.\n", mWorkers.Count()));
+
+ // If there are some running threads left, terminate the process.
+ if (mWorkers.Count() > 0)
+ exit(1);
+
+
+ nsAutoMonitor::DestroyMonitor(mWaitingWorkersMon);
+ nsAutoMonitor::DestroyMonitor(mPendingMon);
+
+# endif
+#endif
+
+ // make sure we have released all instances
+ mInstances.EnumerateRead(EnumerateInstanceMapAndDelete, nsnull);
+
+ mInstanceSet.Clear();
+ mInstances.Clear();
+
+ // clear the stub table
+ // (this will not release stubs -- it's the client's responsibility)
+ mStubs.Clear();
+}
+
+// this should be inlined
+nsresult
+ipcDConnectService::GetInterfaceInfo(const nsID &iid, nsIInterfaceInfo **result)
+{
+ return mIIM->GetInfoForIID(&iid, result);
+}
+
+// this is adapted from the version in xpcwrappednative.cpp
+nsresult
+ipcDConnectService::GetIIDForMethodParam(nsIInterfaceInfo *iinfo,
+ const nsXPTMethodInfo *methodInfo,
+ const nsXPTParamInfo &paramInfo,
+ const nsXPTType &type,
+ PRUint16 methodIndex,
+ nsXPTCMiniVariant *dispatchParams,
+ PRBool isXPTCVariantArray,
+ nsID &result)
+{
+ PRUint8 argnum, tag = type.TagPart();
+ nsresult rv;
+
+ if (tag == nsXPTType::T_INTERFACE)
+ {
+ rv = iinfo->GetIIDForParamNoAlloc(methodIndex, &paramInfo, &result);
+ }
+ else if (tag == nsXPTType::T_INTERFACE_IS)
+ {
+ rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, &paramInfo, &argnum);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsXPTParamInfo& arg_param = methodInfo->GetParam(argnum);
+ const nsXPTType& arg_type = arg_param.GetType();
+
+ // The xpidl compiler ensures this. We reaffirm it for safety.
+ if (!arg_type.IsPointer() || arg_type.TagPart() != nsXPTType::T_IID)
+ return NS_ERROR_UNEXPECTED;
+
+ nsID *p = (nsID *) GET_PARAM(dispatchParams, isXPTCVariantArray, argnum).val.p;
+ if (!p)
+ return NS_ERROR_UNEXPECTED;
+
+ result = *p;
+ }
+ else
+ rv = NS_ERROR_UNEXPECTED;
+ return rv;
+}
+
+nsresult
+ipcDConnectService::StoreInstance(DConnectInstance *wrapper)
+{
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ wrapper->InterfaceInfo()->GetNameShared(&name);
+ LOG(("ipcDConnectService::StoreInstance(): instance=%p iface=%p {%s}\n",
+ wrapper, wrapper->RealInstance(), name));
+ }
+#endif
+
+ nsresult rv = mInstanceSet.Put(wrapper);
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = mInstances.Put(wrapper->GetKey(), wrapper)
+ ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ if (NS_FAILED(rv))
+ mInstanceSet.Remove(wrapper);
+ }
+ return rv;
+}
+
+void
+ipcDConnectService::DeleteInstance(DConnectInstance *wrapper,
+ PRBool locked /* = PR_FALSE */)
+{
+ if (!locked)
+ PR_Lock(mLock);
+
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ wrapper->InterfaceInfo()->GetNameShared(&name);
+ LOG(("ipcDConnectService::DeleteInstance(): instance=%p iface=%p {%s}\n",
+ wrapper, wrapper->RealInstance(), name));
+ }
+#endif
+
+ mInstances.Remove(wrapper->GetKey());
+ mInstanceSet.Remove(wrapper);
+
+ if (!locked)
+ PR_Unlock(mLock);
+}
+
+PRBool
+ipcDConnectService::FindInstanceAndAddRef(PRUint32 peer,
+ const nsISupports *obj,
+ const nsIID *iid,
+ DConnectInstance **wrapper)
+{
+ PRBool result = mInstances.Get(DConnectInstanceKey::Key(peer, obj, iid), wrapper);
+ if (result)
+ (*wrapper)->AddRef();
+ return result;
+}
+
+PRBool
+ipcDConnectService::CheckInstanceAndAddRef(DConnectInstance *wrapper, PRUint32 peer)
+{
+ nsAutoLock lock (mLock);
+
+ if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer)
+ {
+ wrapper->AddRef();
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+nsresult
+ipcDConnectService::StoreStub(DConnectStub *stub)
+{
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ stub->GetInterfaceInfo(getter_AddRefs(iinfo));
+ iinfo->GetNameShared(&name);
+ LOG(("ipcDConnectService::StoreStub(): stub=%p instance=0x%Lx {%s}\n",
+ stub, stub->Instance(), name));
+ }
+#endif
+
+ return mStubs.Put(stub->GetKey(), stub)
+ ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+void
+ipcDConnectService::DeleteStub(DConnectStub *stub)
+{
+#ifdef IPC_LOGGING
+ if (IPC_LOG_ENABLED())
+ {
+ const char *name;
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ stub->GetInterfaceInfo(getter_AddRefs(iinfo));
+ iinfo->GetNameShared(&name);
+ LOG(("ipcDConnectService::DeleteStub(): stub=%p instance=0x%Lx {%s}\n",
+ stub, stub->Instance(), name));
+ }
+#endif
+
+ // this method is intended to be called only from DConnectStub::Release().
+ // the stub object is not deleted when removed from the table, because
+ // DConnectStub pointers are not owned by mStubs.
+ mStubs.Remove(stub->GetKey());
+}
+
+// not currently used
+#if 0
+PRBool
+ipcDConnectService::FindStubAndAddRef(PRUint32 peer, const DConAddr instance,
+ DConnectStub **stub)
+{
+ nsAutoLock stubLock (mStubLock);
+
+ PRBool result = mStubs.Get(DConnectStubKey::Key(peer, instance), stub);
+ if (result)
+ NS_ADDREF(*stub);
+ return result;
+}
+#endif
+
+NS_IMPL_THREADSAFE_ISUPPORTS3(ipcDConnectService, ipcIDConnectService,
+ ipcIMessageObserver,
+ ipcIClientObserver)
+
+NS_IMETHODIMP
+ipcDConnectService::CreateInstance(PRUint32 aPeerID,
+ const nsID &aCID,
+ const nsID &aIID,
+ void **aInstancePtr)
+{
+ DConnectSetupClassID msg;
+ msg.opcode_minor = DCON_OP_SETUP_NEW_INST_CLASSID;
+ msg.iid = aIID;
+ msg.classid = aCID;
+
+ return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr);
+}
+
+NS_IMETHODIMP
+ipcDConnectService::CreateInstanceByContractID(PRUint32 aPeerID,
+ const char *aContractID,
+ const nsID &aIID,
+ void **aInstancePtr)
+{
+ size_t slen = strlen(aContractID);
+ size_t size = sizeof(DConnectSetupContractID) + slen;
+
+ DConnectSetupContractID *msg =
+ (DConnectSetupContractID *) malloc(size);
+
+ msg->opcode_minor = DCON_OP_SETUP_NEW_INST_CONTRACTID;
+ msg->iid = aIID;
+ memcpy(&msg->contractid, aContractID, slen + 1);
+
+ nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr);
+
+ free(msg);
+ return rv;
+}
+
+NS_IMETHODIMP
+ipcDConnectService::GetService(PRUint32 aPeerID,
+ const nsID &aCID,
+ const nsID &aIID,
+ void **aInstancePtr)
+{
+ DConnectSetupClassID msg;
+ msg.opcode_minor = DCON_OP_SETUP_GET_SERV_CLASSID;
+ msg.iid = aIID;
+ msg.classid = aCID;
+
+ return SetupPeerInstance(aPeerID, &msg, sizeof(msg), aInstancePtr);
+}
+
+NS_IMETHODIMP
+ipcDConnectService::GetServiceByContractID(PRUint32 aPeerID,
+ const char *aContractID,
+ const nsID &aIID,
+ void **aInstancePtr)
+{
+ size_t slen = strlen(aContractID);
+ size_t size = sizeof(DConnectSetupContractID) + slen;
+
+ DConnectSetupContractID *msg =
+ (DConnectSetupContractID *) malloc(size);
+
+ msg->opcode_minor = DCON_OP_SETUP_GET_SERV_CONTRACTID;
+ msg->iid = aIID;
+ memcpy(&msg->contractid, aContractID, slen + 1);
+
+ nsresult rv = SetupPeerInstance(aPeerID, msg, size, aInstancePtr);
+
+ free(msg);
+ return rv;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+ipcDConnectService::OnMessageAvailable(PRUint32 aSenderID,
+ const nsID &aTarget,
+ const PRUint8 *aData,
+ PRUint32 aDataLen)
+{
+ if (mDisconnected)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ const DConnectOp *op = (const DConnectOp *) aData;
+
+ LOG (("ipcDConnectService::OnMessageAvailable: "
+ "senderID=%d, opcode_major=%d, index=%d\n",
+ aSenderID, op->opcode_major, op->request_index));
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+ void *pvDataDup = RTMemDup(aData, aDataLen);
+ if (RT_UNLIKELY(!pvDataDup))
+ return NS_ERROR_OUT_OF_MEMORY;
+ int rc = RTReqPoolCallVoidNoWait(mhReqPool, (PFNRT)ProcessMessageOnWorkerThread, 4,
+ this, aSenderID, pvDataDup, aDataLen);
+ if (RT_FAILURE(rc))
+ return NS_ERROR_FAILURE;
+
+# else
+
+ nsAutoMonitor mon(mPendingMon);
+ mPendingQ.Append(new DConnectRequest(aSenderID, op, aDataLen));
+ // notify a worker
+ mon.Notify();
+ mon.Exit();
+
+ // Yield the cpu so a worker can get a chance to start working without too much fuss.
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+ mon.Enter();
+ // examine the queue
+ if (mPendingQ.Count() > mWaitingWorkers)
+ {
+ // wait a little while to let the workers empty the queue.
+ mon.Exit();
+ {
+ PRUint32 ticks = PR_MillisecondsToInterval(PR_MIN(mWorkers.Count() / 20 + 1, 10));
+ nsAutoMonitor workersMon(mWaitingWorkersMon);
+ workersMon.Wait(ticks);
+ }
+ mon.Enter();
+ // examine the queue again
+ if (mPendingQ.Count() > mWaitingWorkers)
+ {
+ // we need one more worker
+ nsresult rv = CreateWorker();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create one more worker thread");
+ rv = rv;
+ }
+ }
+
+# endif
+#else
+
+ OnIncomingRequest(aSenderID, op, aDataLen);
+
+#endif
+
+ return NS_OK;
+}
+
+struct PruneInstanceMapForPeerArgs
+{
+ ipcDConnectService *that;
+ PRUint32 clientID;
+ nsVoidArray &wrappers;
+};
+
+PR_STATIC_CALLBACK(PLDHashOperator)
+PruneInstanceMapForPeer (const DConnectInstanceKey::Key &aKey,
+ DConnectInstance *aData,
+ void *userArg)
+{
+ PruneInstanceMapForPeerArgs *args = (PruneInstanceMapForPeerArgs *)userArg;
+ NS_ASSERTION(args, "PruneInstanceMapForPeerArgs is NULL");
+
+ if (args && args->clientID == aData->Peer())
+ {
+ nsrefcnt countIPC = aData->ReleaseIPC(PR_TRUE /* locked */);
+
+ LOG(("ipcDConnectService::PruneInstanceMapForPeer: "
+ "instance=%p: %d IPC refs to release\n",
+ aData, countIPC + 1));
+
+ // release all IPC instances of the "officially dead" client (see
+ // #OnRelease() to understand why it must be done under the lock). Note
+ // that due to true multithreading, late OnRelease() requests may still
+ // happen on other worker threads *after* OnClientStateChange() has been
+ // called, but it's OK because the instance will be removed from the map
+ // by the below code alreay and won't be deleted for the second time.
+ while (countIPC)
+ {
+ countIPC = aData->ReleaseIPC(PR_TRUE /* locked */);
+ aData->Release();
+ }
+
+ // collect the instance for the last release
+ // (we'll do it later outside the lock)
+ if (!args->wrappers.AppendElement(aData))
+ {
+ NS_NOTREACHED("Not enough memory");
+ // bad but what to do
+ aData->Release();
+ }
+ }
+ return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+ipcDConnectService::OnClientStateChange(PRUint32 aClientID,
+ PRUint32 aClientState)
+{
+ LOG(("ipcDConnectService::OnClientStateChange: aClientID=%d, aClientState=%d\n",
+ aClientID, aClientState));
+
+ if (aClientState == ipcIClientObserver::CLIENT_DOWN)
+ {
+ if (aClientID == IPC_SENDER_ANY)
+ {
+ // a special case: our IPC system is being shutdown, try to safely
+ // uninitialize everything...
+ Shutdown();
+ }
+ else
+ {
+ LOG(("ipcDConnectService::OnClientStateChange: "
+ "pruning all instances created for peer %d...\n", aClientID));
+
+ nsVoidArray wrappers;
+
+ {
+ nsAutoLock lock (mLock);
+
+ // make sure we have removed all instances from instance maps
+ PruneInstanceMapForPeerArgs args = { this, aClientID, wrappers };
+ mInstances.EnumerateRead(PruneInstanceMapForPeer, (void *)&args);
+ }
+
+ LOG(("ipcDConnectService::OnClientStateChange: "
+ "%d lost instances\n", wrappers.Count()));
+
+ // release all pending references left after PruneInstanceMapForPeer().
+ // this may call wrapper destructors so it's important to do that
+ // outside the lock because destructors will release the real objects
+ // which may need to make asynchronous use our service
+ for (PRInt32 i = 0; i < wrappers.Count(); ++i)
+ ((DConnectInstance *) wrappers[i])->Release();
+ }
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+#if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+/**
+ * Function called by the request thread pool to process a incoming request in
+ * the context of a worker thread.
+ */
+/* static */ DECLCALLBACK(void)
+ipcDConnectService::ProcessMessageOnWorkerThread(ipcDConnectService *aThis, PRUint32 aSenderID, void *aData, PRUint32 aDataLen)
+{
+ if (!aThis->mDisconnected)
+ aThis->OnIncomingRequest(aSenderID, (const DConnectOp *)aData, aDataLen);
+ RTMemFree(aData);
+}
+#endif
+
+void
+ipcDConnectService::OnIncomingRequest(PRUint32 peer, const DConnectOp *op, PRUint32 opLen)
+{
+ switch (op->opcode_major)
+ {
+ case DCON_OP_SETUP:
+ OnSetup(peer, (const DConnectSetup *) op, opLen);
+ break;
+ case DCON_OP_RELEASE:
+ OnRelease(peer, (const DConnectRelease *) op);
+ break;
+ case DCON_OP_INVOKE:
+ OnInvoke(peer, (const DConnectInvoke *) op, opLen);
+ break;
+ default:
+ NS_NOTREACHED("unknown opcode major");
+ }
+}
+
+void
+ipcDConnectService::OnSetup(PRUint32 peer, const DConnectSetup *setup, PRUint32 opLen)
+{
+ nsISupports *instance = nsnull;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ switch (setup->opcode_minor)
+ {
+ // CreateInstance
+ case DCON_OP_SETUP_NEW_INST_CLASSID:
+ {
+ const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup;
+
+ nsCOMPtr<nsIComponentManager> compMgr;
+ rv = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (NS_SUCCEEDED(rv))
+ rv = compMgr->CreateInstance(setupCI->classid, nsnull, setupCI->iid, (void **) &instance);
+
+ break;
+ }
+
+ // CreateInstanceByContractID
+ case DCON_OP_SETUP_NEW_INST_CONTRACTID:
+ {
+ const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup;
+
+ nsCOMPtr<nsIComponentManager> compMgr;
+ rv = NS_GetComponentManager(getter_AddRefs(compMgr));
+ if (NS_SUCCEEDED(rv))
+ rv = compMgr->CreateInstanceByContractID(setupCI->contractid, nsnull, setupCI->iid, (void **) &instance);
+
+ break;
+ }
+
+ // GetService
+ case DCON_OP_SETUP_GET_SERV_CLASSID:
+ {
+ const DConnectSetupClassID *setupCI = (const DConnectSetupClassID *) setup;
+
+ nsCOMPtr<nsIServiceManager> svcMgr;
+ rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
+ if (NS_SUCCEEDED(rv))
+ rv = svcMgr->GetService(setupCI->classid, setupCI->iid, (void **) &instance);
+ break;
+ }
+
+ // GetServiceByContractID
+ case DCON_OP_SETUP_GET_SERV_CONTRACTID:
+ {
+ const DConnectSetupContractID *setupCI = (const DConnectSetupContractID *) setup;
+
+ nsCOMPtr<nsIServiceManager> svcMgr;
+ rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
+ if (NS_SUCCEEDED(rv))
+ rv = svcMgr->GetServiceByContractID(setupCI->contractid, setupCI->iid, (void **) &instance);
+
+ break;
+ }
+
+ // QueryInterface
+ case DCON_OP_SETUP_QUERY_INTERFACE:
+ {
+ const DConnectSetupQueryInterface *setupQI = (const DConnectSetupQueryInterface *) setup;
+ DConnectInstance *QIinstance = (DConnectInstance *)setupQI->instance;
+
+ // make sure we've been sent a valid wrapper
+ if (!CheckInstanceAndAddRef(QIinstance, peer))
+ {
+ NS_NOTREACHED("instance wrapper not found");
+ rv = NS_ERROR_INVALID_ARG;
+ }
+ else
+ {
+ rv = QIinstance->RealInstance()->QueryInterface(setupQI->iid, (void **) &instance);
+ QIinstance->Release();
+ }
+ break;
+ }
+
+ default:
+ NS_NOTREACHED("unexpected minor opcode");
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ nsVoidArray wrappers;
+
+ // now, create instance wrapper, and store it in our instances set.
+ // this allows us to keep track of object references held on behalf of a
+ // particular peer. we can use this information to cleanup after a peer
+ // that disconnects without sending RELEASE messages for its objects.
+ DConnectInstance *wrapper = nsnull;
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ rv = GetInterfaceInfo(setup->iid, getter_AddRefs(iinfo));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoLock lock (mLock);
+
+ // first try to find an existing wrapper for the given object
+ if (!FindInstanceAndAddRef(peer, instance, &setup->iid, &wrapper))
+ {
+ wrapper = new DConnectInstance(peer, iinfo, instance);
+ if (!wrapper)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ else
+ {
+ rv = StoreInstance(wrapper);
+ if (NS_FAILED(rv))
+ {
+ delete wrapper;
+ wrapper = nsnull;
+ }
+ else
+ {
+ // reference the newly created wrapper
+ wrapper->AddRef();
+ }
+ }
+ }
+
+ if (wrapper)
+ {
+ // increase the second, IPC-only, reference counter (mandatory before
+ // trying wrappers.AppendElement() to make sure ReleaseIPC() will remove
+ // the wrapper from the instance map on failure)
+ wrapper->AddRefIPC();
+
+ if (!wrappers.AppendElement(wrapper))
+ {
+ wrapper->ReleaseIPC();
+ wrapper->Release();
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // wrapper remains referenced when passing it to the client
+ // (will be released upon DCON_OP_RELEASE)
+ }
+ }
+
+ NS_IF_RELEASE(instance);
+
+ nsCOMPtr <nsIException> exception;
+ PRBool got_exception = PR_FALSE;
+
+ if (rv != NS_OK)
+ {
+ // try to fetch an nsIException possibly set by one of the setup methods
+ nsresult rv2;
+ nsCOMPtr <nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rv2);
+ if (NS_SUCCEEDED(rv2))
+ {
+ nsCOMPtr <nsIExceptionManager> em;
+ rv2 = es->GetCurrentExceptionManager (getter_AddRefs (em));
+ if (NS_SUCCEEDED(rv2))
+ {
+ rv2 = em->GetCurrentException (getter_AddRefs (exception));
+ if (NS_SUCCEEDED(rv2))
+ {
+ LOG(("got nsIException instance, will serialize\n"));
+ got_exception = PR_TRUE;
+ }
+ }
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv2), "failed to get/serialize exception");
+ if (NS_FAILED(rv2))
+ rv = rv2;
+ }
+
+ ipcMessageWriter writer(64);
+
+ DConnectSetupReply msg;
+ msg.opcode_major = DCON_OP_SETUP_REPLY;
+ msg.opcode_minor = 0;
+ msg.flags = 0;
+ msg.request_index = setup->request_index;
+ msg.instance = (DConAddr)(uintptr_t)wrapper;
+ msg.status = rv;
+
+ if (got_exception)
+ msg.flags |= DCON_OP_FLAGS_REPLY_EXCEPTION;
+
+ writer.PutBytes(&msg, sizeof(msg));
+
+ if (got_exception)
+ {
+ rv = SerializeException(writer, peer, exception, wrappers);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get/serialize exception");
+ }
+
+ // fire off SETUP_REPLY, don't wait for a response
+ if (NS_FAILED(rv))
+ rv = IPC_SendMessage(peer, kDConnectTargetID,
+ (const PRUint8 *) &msg, sizeof(msg));
+ else
+ rv = IPC_SendMessage(peer, kDConnectTargetID,
+ writer.GetBuffer(), writer.GetSize());
+
+ if (NS_FAILED(rv))
+ {
+ LOG(("unable to send SETUP_REPLY: rv=%x\n", rv));
+ ReleaseWrappers(wrappers, peer);
+ }
+}
+
+void
+ipcDConnectService::OnRelease(PRUint32 peer, const DConnectRelease *release)
+{
+ LOG(("ipcDConnectService::OnRelease [peer=%u instance=0x%Lx]\n",
+ peer, release->instance));
+
+ DConnectInstance *wrapper = (DConnectInstance *)release->instance;
+
+ nsAutoLock lock (mLock);
+
+ // make sure we've been sent a valid wrapper from the same peer we created
+ // this wrapper for
+ if (mInstanceSet.Contains(wrapper) && wrapper->Peer() == peer)
+ {
+ // release the IPC reference from under the lock to ensure atomicity of
+ // the "check + possible delete" sequence ("delete" is remove this wrapper
+ // from the instance map when the IPC reference counter drops to zero)
+ wrapper->ReleaseIPC(PR_TRUE /* locked */);
+ // leave the lock before Release() because it may call the destructor
+ // which will release the real object which may need to make asynchronous
+ // use our service
+ lock.unlock();
+ wrapper->Release();
+ }
+ else
+ {
+ // it is possible that the client disconnection event handler has released
+ // all client instances before the DCON_OP_RELEASE message sent by the
+ // client gets processed here (because of true multithreading). Just log
+ // a debug warning
+ LOG(("ipcDConnectService::OnRelease: WARNING: "
+ "instance wrapper %p for peer %d not found", wrapper, peer));
+ }
+}
+
+void
+ipcDConnectService::OnInvoke(PRUint32 peer, const DConnectInvoke *invoke, PRUint32 opLen)
+{
+ LOG(("ipcDConnectService::OnInvoke [peer=%u instance=0x%Lx method=%u]\n",
+ peer, invoke->instance, invoke->method_index));
+
+ DConnectInstance *wrapper = (DConnectInstance *)invoke->instance;
+
+ ipcMessageReader reader((const PRUint8 *) (invoke + 1), opLen - sizeof(*invoke));
+
+ const nsXPTMethodInfo *methodInfo;
+ nsXPTCVariant *params = nsnull;
+ nsCOMPtr<nsIInterfaceInfo> iinfo = nsnull;
+ PRUint8 i, paramCount = 0, paramUsed = 0;
+ nsresult rv;
+
+ nsCOMPtr <nsIException> exception;
+ PRBool got_exception = PR_FALSE;
+
+ // make sure we've been sent a valid wrapper
+ if (!CheckInstanceAndAddRef(wrapper, peer))
+ {
+ NS_NOTREACHED("instance wrapper not found");
+ wrapper = nsnull;
+ rv = NS_ERROR_INVALID_ARG;
+ goto end;
+ }
+
+ iinfo = wrapper->InterfaceInfo();
+
+ rv = iinfo->GetMethodInfo(invoke->method_index, &methodInfo);
+ if (NS_FAILED(rv))
+ goto end;
+
+ paramCount = methodInfo->GetParamCount();
+
+ LOG((" iface=%p\n", wrapper->RealInstance()));
+ LOG((" name=%s\n", methodInfo->GetName()));
+ LOG((" param-count=%u\n", (PRUint32) paramCount));
+ LOG((" request-index=%d\n", (PRUint32) invoke->request_index));
+
+ params = new nsXPTCVariant[paramCount];
+ if (!params)
+ {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ goto end;
+ }
+
+ // setup |params| for xptcall
+
+ for (i=0; i<paramCount; ++i, ++paramUsed)
+ {
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
+
+ // XXX are inout params an issue?
+ // yes, we will need to do v.ptr = &v.val for them (DeserializeParam doesn't
+ // currently do that) to let the callee correctly pick it up and change.
+
+ if (paramInfo.IsIn() && !paramInfo.IsDipper())
+ rv = DeserializeParam(reader, paramInfo.GetType(), params[i]);
+ else
+ rv = SetupParam(paramInfo, params[i]);
+
+ if (NS_FAILED(rv))
+ goto end;
+ }
+
+ // fixup any interface pointers. we do this with a second pass so that
+ // we can properly handle INTERFACE_IS. This pass is also used to deserialize
+ // arrays (array data goes after all other params).
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
+ if (paramInfo.IsIn())
+ {
+ const nsXPTType &type = paramInfo.GetType();
+ if (type.IsInterfacePointer())
+ {
+ // grab the DConAddr value temporarily stored in the param
+#ifdef VBOX
+ PtrBits bits = params[i].val.u64;
+#else
+ PtrBits bits = (PtrBits)(uintptr_t) params[i].val.p;
+#endif
+
+ // DeserializeInterfaceParamBits needs IID only if it's a remote object
+ nsID iid;
+ if (bits & PTRBITS_REMOTE_BIT)
+ {
+ rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
+ invoke->method_index, params, PR_TRUE, iid);
+ if (NS_FAILED(rv))
+ goto end;
+ }
+
+ nsISupports *obj = nsnull;
+ rv = DeserializeInterfaceParamBits(bits, peer, iid, obj);
+ if (NS_FAILED(rv))
+ goto end;
+
+ params[i].val.p = obj;
+ // mark as interface to let FinishParam() release this param
+ params[i].SetValIsInterface();
+ }
+ else if (type.IsArray())
+ {
+ void *array = nsnull;
+ rv = DeserializeArrayParam(this, reader, peer, iinfo,
+ invoke->method_index, *methodInfo, params,
+ PR_TRUE, paramInfo, PR_FALSE, array);
+ if (NS_FAILED(rv))
+ goto end;
+
+ params[i].val.p = array;
+ // mark to let FinishParam() free this param
+ params[i].SetValIsAllocated();
+ }
+ }
+ }
+
+ rv = XPTC_InvokeByIndex(wrapper->RealInstance(),
+ invoke->method_index,
+ paramCount,
+ params);
+
+ if (rv != NS_OK)
+ {
+ // try to fetch an nsIException possibly set by the method
+ nsresult rv2;
+ nsCOMPtr <nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rv2);
+ if (NS_SUCCEEDED(rv2))
+ {
+ nsCOMPtr <nsIExceptionManager> em;
+ rv2 = es->GetCurrentExceptionManager (getter_AddRefs (em));
+ if (NS_SUCCEEDED(rv2))
+ {
+ rv2 = em->GetCurrentException (getter_AddRefs (exception));
+ if (NS_SUCCEEDED(rv2))
+ {
+ LOG(("got nsIException instance, will serialize\n"));
+ got_exception = PR_TRUE;
+ }
+ }
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv2), "failed to get/serialize exception");
+ if (NS_FAILED(rv2))
+ rv = rv2;
+ }
+
+end:
+ LOG(("sending INVOKE_REPLY: rv=%x\n", rv));
+
+ // balance CheckInstanceAndAddRef()
+ if (wrapper)
+ wrapper->Release();
+
+ ipcMessageWriter writer(64);
+
+ DConnectInvokeReply reply;
+ reply.opcode_major = DCON_OP_INVOKE_REPLY;
+ reply.opcode_minor = 0;
+ reply.flags = 0;
+ reply.request_index = invoke->request_index;
+ reply.result = rv;
+
+ if (got_exception)
+ reply.flags |= DCON_OP_FLAGS_REPLY_EXCEPTION;
+
+ writer.PutBytes(&reply, sizeof(reply));
+
+ nsVoidArray wrappers;
+
+ if (NS_SUCCEEDED(rv) && params)
+ {
+ // serialize out-params and retvals
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo paramInfo = methodInfo->GetParam(i);
+
+ if (paramInfo.IsRetval() || paramInfo.IsOut())
+ {
+ const nsXPTType &type = paramInfo.GetType();
+
+ if (type.IsInterfacePointer())
+ {
+ nsID iid;
+ rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
+ invoke->method_index, params, PR_TRUE, iid);
+ if (NS_SUCCEEDED(rv))
+ rv = SerializeInterfaceParam(writer, peer, iid,
+ (nsISupports *) params[i].val.p, wrappers);
+ }
+ else
+ rv = SerializeParam(writer, type, params[i]);
+
+ if (NS_FAILED(rv))
+ {
+ reply.result = rv;
+ break;
+ }
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // serialize output array parameters after everything else since the
+ // deserialization procedure will need to get a size_is value which may be
+ // stored in any preceeding or following param
+ for (i=0; i<paramCount; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
+
+ if (paramInfo.GetType().IsArray() &&
+ (paramInfo.IsRetval() || paramInfo.IsOut()))
+ {
+ rv = SerializeArrayParam(this, writer, peer, iinfo, invoke->method_index,
+ *methodInfo, params, PR_TRUE, paramInfo,
+ params[i].val.p, wrappers);
+ if (NS_FAILED(rv))
+ {
+ reply.result = rv;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (got_exception)
+ {
+ rv = SerializeException(writer, peer, exception, wrappers);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get/serialize exception");
+ }
+
+ if (NS_FAILED(rv))
+ rv = IPC_SendMessage(peer, kDConnectTargetID, (const PRUint8 *) &reply, sizeof(reply));
+ else
+ rv = IPC_SendMessage(peer, kDConnectTargetID, writer.GetBuffer(), writer.GetSize());
+ if (NS_FAILED(rv))
+ {
+ LOG(("unable to send INVOKE_REPLY: rv=%x\n", rv));
+ ReleaseWrappers(wrappers, peer);
+ }
+
+ if (params)
+ {
+ // free individual elements of arrays (note: before freeing arrays
+ // themselves in FinishParam())
+ for (i=0; i<paramUsed; ++i)
+ {
+ const nsXPTParamInfo &paramInfo = methodInfo->GetParam(i);
+ if (paramInfo.GetType().IsArray())
+ FinishArrayParam(iinfo, invoke->method_index, *methodInfo,
+ params, PR_TRUE, paramInfo, params[i]);
+ }
+
+ for (i=0; i<paramUsed; ++i)
+ FinishParam(params[i]);
+ delete[] params;
+ }
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h
new file mode 100644
index 00000000..517b2650
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/src/ipcDConnectService.h
@@ -0,0 +1,365 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ * Dmitry A. Kuminov <dmik@innotek.de>
+ *
+ * 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 ***** */
+
+// DConnect service is multithreaded by default...
+#if !defined(DCONNECT_SINGLETHREADED) && !defined(DCONNECT_MULTITHREADED)
+#define DCONNECT_MULTITHREADED
+# ifdef VBOX
+//# define DCONNECT_WITH_IPRT_REQ_POOL
+# endif
+#endif
+
+#include "ipcIDConnectService.h"
+#include "ipcdclient.h"
+
+#include "nsIInterfaceInfo.h"
+#include "nsIInterfaceInfoManager.h"
+
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsHashSets.h"
+#include "nsVoidArray.h"
+#include "nsAutoLock.h"
+#include "xptcall.h"
+#include "xptinfo.h"
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+# include <iprt/req.h>
+
+# else /* !DCONNECT_WITH_IPRT_REQ_POOL*/
+
+#include "ipcList.h"
+
+struct DConnectOp;
+
+struct DConnectRequest : public ipcListNode<DConnectRequest>
+{
+ DConnectRequest (PRUint32 aPeer, const DConnectOp *aOp, PRUint32 aOpLen)
+ : peer(aPeer)
+ , opLen(aOpLen)
+ {
+ op = (const DConnectOp *) malloc(aOpLen);
+ memcpy ((void *) op, aOp, aOpLen);
+ }
+ ~DConnectRequest() { free((void *) op); }
+
+ const PRUint32 peer;
+ const DConnectOp *op;
+ const PRUint32 opLen;
+};
+
+# endif // !DCONNECT_WITH_IPRT_REQ_POOL
+#endif // DCONNECT_MULTITHREADED
+
+class nsIException;
+class ipcMessageReader;
+class ipcMessageWriter;
+
+// a key class used to identify DConnectInstance objects stored in a hash table
+// by a composite of peer ID, XPCOM object pointer and IID this pointer represents
+
+class DConnectInstanceKey : public PLDHashEntryHdr
+{
+public:
+ struct Key
+ {
+ Key(PRUint32 aPeer, const nsISupports *aObj, const nsID *aIID)
+ : mPeer (aPeer), mObj (aObj), mIID (aIID) {}
+ const PRUint32 mPeer;
+ const nsISupports *mObj;
+ const nsIID *mIID;
+ };
+
+ typedef const Key &KeyType;
+ typedef const Key *KeyTypePointer;
+
+ DConnectInstanceKey(const Key *aKey) : mKey (*aKey) {}
+ DConnectInstanceKey(const DConnectInstanceKey &toCopy) : mKey (toCopy.mKey) {}
+ ~DConnectInstanceKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ KeyTypePointer GetKeyPointer() const { return &mKey; }
+
+ PRBool KeyEquals(KeyTypePointer aKey) const {
+ return mKey.mPeer == aKey->mPeer &&
+ mKey.mObj == aKey->mObj &&
+ mKey.mIID->Equals(*aKey->mIID);
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) {
+ return aKey->mPeer ^
+ (NS_PTR_TO_INT32(aKey->mObj) >> 2) ^
+ nsIDHashKey::HashKey(aKey->mIID);
+ }
+ enum { ALLOW_MEMMOVE = PR_TRUE };
+
+private:
+ const Key mKey;
+};
+
+class DConnectInstance;
+typedef nsDataHashtable<DConnectInstanceKey, DConnectInstance *> DConnectInstanceMap;
+
+// extend nsVoidHashSet for compatibility with some nsTHashtable methods
+class DConnectInstanceSet : public nsVoidHashSet
+{
+public:
+ PRBool Init(PRUint32 initSize = PL_DHASH_MIN_SIZE) {
+ return nsVoidHashSet::Init(initSize) == NS_OK;
+ }
+ void Clear() {
+ PL_DHashTableEnumerate(&mHashTable, PL_DHashStubEnumRemove, nsnull);
+ }
+};
+
+typedef PRUint64 DConAddr;
+
+// a key class used to identify DConnectStub objects stored in a hash table
+// by a composite of peer ID and DConAddr
+
+class DConnectStubKey : public PLDHashEntryHdr
+{
+public:
+ struct Key
+ {
+ Key(PRUint32 aPeer, const DConAddr aInstance)
+ : mPeer (aPeer), mInstance (aInstance) {}
+ const PRUint32 mPeer;
+ const DConAddr mInstance;
+ };
+
+ typedef const Key &KeyType;
+ typedef const Key *KeyTypePointer;
+
+ DConnectStubKey(const Key *aKey) : mKey (*aKey) {}
+ DConnectStubKey(const DConnectStubKey &toCopy) : mKey (toCopy.mKey) {}
+ ~DConnectStubKey() {}
+
+ KeyType GetKey() const { return mKey; }
+ KeyTypePointer GetKeyPointer() const { return &mKey; }
+
+ PRBool KeyEquals(KeyTypePointer aKey) const {
+ return mKey.mPeer == aKey->mPeer &&
+ mKey.mInstance == aKey->mInstance;
+ }
+
+ static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+ static PLDHashNumber HashKey(KeyTypePointer aKey) {
+ return aKey->mPeer ^
+ (NS_PTR_TO_INT32(aKey->mInstance) >> 2);
+ }
+ enum { ALLOW_MEMMOVE = PR_TRUE };
+
+private:
+ const Key mKey;
+};
+
+// used elsewhere like nsAtomTable to safely represent the integral value
+// of an address.
+typedef PRUint64 PtrBits;
+
+// bit flag that defines if a PtrBits value represents a remote object
+#define PTRBITS_REMOTE_BIT 0x1
+
+class DConnectStub;
+typedef nsDataHashtable<DConnectStubKey, DConnectStub *> DConnectStubMap;
+
+class ipcDConnectService : public ipcIDConnectService
+ , public ipcIMessageObserver
+ , public ipcIClientObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_IPCIDCONNECTSERVICE
+ NS_DECL_IPCIMESSAGEOBSERVER
+ NS_DECL_IPCICLIENTOBSERVER
+
+ ipcDConnectService();
+
+ NS_HIDDEN_(nsresult) Init();
+ NS_HIDDEN_(void) Shutdown();
+
+ NS_HIDDEN_(nsresult) GetInterfaceInfo(const nsID &iid, nsIInterfaceInfo **);
+ NS_HIDDEN_(nsresult) GetIIDForMethodParam(nsIInterfaceInfo *iinfo,
+ const nsXPTMethodInfo *methodInfo,
+ const nsXPTParamInfo &paramInfo,
+ const nsXPTType &type,
+ PRUint16 methodIndex,
+ nsXPTCMiniVariant *dispatchParams,
+ PRBool isXPTCVariantArray,
+ nsID &result);
+
+ NS_HIDDEN_(nsresult) SerializeInterfaceParam(ipcMessageWriter &writer,
+ PRUint32 peer, const nsID &iid,
+ nsISupports *obj,
+ nsVoidArray &wrappers);
+ NS_HIDDEN_(nsresult) DeserializeInterfaceParamBits(PtrBits bits, PRUint32 peer,
+ const nsID &iid,
+ nsISupports *&obj);
+
+ NS_HIDDEN_(nsresult) SerializeException(ipcMessageWriter &writer,
+ PRUint32 peer, nsIException *xcpt,
+ nsVoidArray &wrappers);
+ NS_HIDDEN_(nsresult) DeserializeException(ipcMessageReader &reader,
+ PRUint32 peer, nsIException **xcpt);
+
+ NS_HIDDEN_(void) ReleaseWrappers(nsVoidArray &wrappers, PRUint32 peer);
+
+ NS_HIDDEN_(nsresult) CreateStub(const nsID &, PRUint32, DConAddr, DConnectStub **);
+#if 0
+ NS_HIDDEN_(PRBool) FindStubAndAddRef(PRUint32, const DConAddr, DConnectStub **);
+#endif
+ // public only for DConnectStub::~DConnectStub()
+ NS_HIDDEN_(void) DeleteStub(DConnectStub *);
+
+ // public only for DConnectInstance::Release()
+ NS_HIDDEN_(void) DeleteInstance(DConnectInstance *, PRBool locked = PR_FALSE);
+ // public only for DConnectStub::CallMethod()
+ NS_HIDDEN_(PRBool) CheckInstanceAndAddRef(DConnectInstance *, PRUint32);
+
+ PRLock *StubLock() { return mStubLock; }
+ PRLock *StubQILock() { return mStubQILock; }
+
+ static nsRefPtr <ipcDConnectService> GetInstance() {
+ return nsRefPtr <ipcDConnectService> (mInstance);
+ }
+
+private:
+
+ NS_HIDDEN ~ipcDConnectService();
+
+ NS_HIDDEN_(nsresult) StoreInstance(DConnectInstance *);
+ NS_HIDDEN_(PRBool) FindInstanceAndAddRef(PRUint32,
+ const nsISupports *,
+ const nsIID *,
+ DConnectInstance **);
+
+ NS_HIDDEN_(nsresult) StoreStub(DConnectStub *);
+
+ NS_HIDDEN_(void) OnIncomingRequest(PRUint32 peer, const struct DConnectOp *op, PRUint32 opLen);
+
+ NS_HIDDEN_(void) OnSetup(PRUint32 peer, const struct DConnectSetup *, PRUint32 opLen);
+ NS_HIDDEN_(void) OnRelease(PRUint32 peer, const struct DConnectRelease *);
+ NS_HIDDEN_(void) OnInvoke(PRUint32 peer, const struct DConnectInvoke *, PRUint32 opLen);
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+ static DECLCALLBACK(void) ProcessMessageOnWorkerThread(ipcDConnectService *aThis, PRUint32 aSenderID, void *aData, PRUint32 aDataLen);
+# else
+ NS_HIDDEN_(nsresult) CreateWorker();
+# endif
+#endif
+
+private:
+ nsCOMPtr<nsIInterfaceInfoManager> mIIM;
+
+ // lock to protect access to instance sets and the disconnected flag
+ PRLock *mLock;
+
+ // table of local object instances allocated on behalf of a peer
+ // (keys are interface pointers of real objects these instances represent)
+ DConnectInstanceMap mInstances;
+ // hashset containing the same instances as above
+ // (used for quick parameter validity checks)
+ DConnectInstanceSet mInstanceSet;
+
+ // lock to protect access to mStubs and DConnectStub::mRefCntLevels
+ // (also guards every DConnectStub::Release call to provide atomicity)
+ PRLock *mStubLock;
+
+ // table of remote object stubs allocated to communicate with peer's instances
+ DConnectStubMap mStubs;
+
+ // this is true after IPC_Shutdown() has been called
+ PRBool mDisconnected;
+
+// member is never initialized or used, no point in wasting memory or making
+// someone believe it contains anything relevant
+#ifndef VBOX
+ // our IPC client ID
+ PRUint32 mSelfID;
+#endif
+
+ // global lock to protect access to protect DConnectStub::QueryInterface()
+ // (we cannot use mStubLock because it isn't supposed to be held long,
+ // like in case of an IPC call and such)
+ PRLock *mStubQILock;
+
+#if defined(DCONNECT_MULTITHREADED)
+# if defined(DCONNECT_WITH_IPRT_REQ_POOL)
+
+ /** Request pool. */
+ RTREQPOOL mhReqPool;
+
+# else
+
+ friend class DConnectWorker;
+
+ // pool of worker threads to serve incoming requests
+ nsVoidArray mWorkers;
+ // queue of pending requests
+ ipcList<DConnectRequest> mPendingQ;
+ // monitor to protect mPendingQ
+ PRMonitor *mPendingMon;
+ // number of waiting workers
+ PRUint32 mWaitingWorkers;
+ // monitor used to wait on changes in mWaitingWorkers.
+ PRMonitor *mWaitingWorkersMon;
+# endif
+#endif
+
+ // global ipcDConnectService instance for internal usage
+ static ipcDConnectService *mInstance;
+};
+
+#define IPC_DCONNECTSERVICE_CLASSNAME \
+ "ipcDConnectService"
+#define IPC_DCONNECTSERVICE_CONTRACTID \
+ "@mozilla.org/ipc/dconnect-service;1"
+#define IPC_DCONNECTSERVICE_CID \
+{ /* 63a5d9dc-4828-425a-bd50-bd10a4b26f2c */ \
+ 0x63a5d9dc, \
+ 0x4828, \
+ 0x425a, \
+ {0xbd, 0x50, 0xbd, 0x10, 0xa4, 0xb2, 0x6f, 0x2c} \
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in
new file mode 100644
index 00000000..2d511de2
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/Makefile.in
@@ -0,0 +1,33 @@
+# vim:set ts=8 sw=8 noet:
+
+DEPTH = ../../../../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = dconnect
+
+REQUIRES = ipcd \
+ nspr \
+ string \
+ xpcom \
+ $(NULL)
+
+CPPSRCS = \
+ TestDConnect.cpp \
+ $(NULL)
+
+SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX))
+
+include $(topsrcdir)/config/config.mk
+
+LIBS = \
+ $(EXTRA_DSO_LIBS) \
+ $(XPCOM_LIBS) \
+ $(NSPR_LIBS) \
+ $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js
new file mode 100644
index 00000000..8403fcdd
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestClient.js
@@ -0,0 +1,106 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * 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 ***** */
+
+/**
+ * This file contains code that mirrors the code in TestDConnect.cpp:DoTest
+ */
+
+const ipcIService = Components.interfaces.ipcIService;
+const ipcIDConnectService = Components.interfaces.ipcIDConnectService;
+const nsIFile = Components.interfaces.nsIFile;
+const nsILocalFile = Components.interfaces.nsILocalFile;
+const nsIEventQueueService = Components.interfaces.nsIEventQueueService;
+
+// XXX use directory service for this
+const TEST_PATH = "/tmp";
+
+var serverID = 0;
+
+function findServer()
+{
+ var ipc = Components.classes["@mozilla.org/ipc/service;1"].getService(ipcIService);
+ serverID = ipc.resolveClientName("test-server");
+}
+
+function doTest()
+{
+ var dcon = Components.classes["@mozilla.org/ipc/dconnect-service;1"]
+ .getService(ipcIDConnectService);
+
+ var file = dcon.createInstanceByContractID(serverID, "@mozilla.org/file/local;1", nsIFile);
+
+ var localFile = file.QueryInterface(nsILocalFile);
+
+ localFile.initWithPath(TEST_PATH);
+
+ if (file.path != TEST_PATH)
+ {
+ dump("*** path test failed [path=" + file.path + "]\n");
+ return;
+ }
+
+ dump("file exists: " + file.exists() + "\n");
+
+ var clone = file.clone();
+
+ const node = "hello.txt";
+ clone.append(node);
+ dump("files are equal: " + file.equals(clone) + "\n");
+
+ if (!clone.exists())
+ clone.create(nsIFile.NORMAL_FILE_TYPE, 0600);
+
+ clone.moveTo(null, "helloworld.txt");
+
+ var localObj = Components.classes["@mozilla.org/file/local;1"].createInstance(nsILocalFile);
+ localObj.initWithPath(TEST_PATH);
+ dump("file.equals(localObj) = " + file.equals(localObj) + "\n");
+ dump("localObj.equals(file) = " + localObj.equals(file) + "\n");
+}
+
+function setupEventQ()
+{
+ var eqs = Components.classes["@mozilla.org/event-queue-service;1"]
+ .getService(nsIEventQueueService);
+ eqs.createMonitoredThreadEventQueue();
+}
+
+setupEventQ();
+findServer();
+dump("\n---------------------------------------------------\n");
+doTest();
+dump("---------------------------------------------------\n\n");
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp
new file mode 100644
index 00000000..8eabe13c
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestDConnect.cpp
@@ -0,0 +1,268 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * 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 "ipcIService.h"
+#include "ipcIDConnectService.h"
+#include "ipcCID.h"
+
+#include "nsIEventQueueService.h"
+#include "nsIServiceManager.h"
+#include "nsIComponentRegistrar.h"
+
+#include "nsXPCOMCID.h"
+#include "nsILocalFile.h"
+
+#include "nsString.h"
+#include "prmem.h"
+
+#if defined( XP_WIN ) || defined( XP_OS2 )
+#define TEST_PATH "c:"
+#else
+#define TEST_PATH "/tmp"
+#endif
+
+#define RETURN_IF_FAILED(rv, step) \
+ PR_BEGIN_MACRO \
+ if (NS_FAILED(rv)) { \
+ printf("*** %s failed: rv=%x\n", step, rv); \
+ return rv;\
+ } \
+ PR_END_MACRO
+
+static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
+static nsIEventQueue* gEventQ = nsnull;
+static PRBool gKeepRunning = PR_TRUE;
+
+static ipcIService *gIpcServ = nsnull;
+
+static nsresult DoTest()
+{
+ nsresult rv;
+
+ nsCOMPtr<ipcIDConnectService> dcon = do_GetService("@mozilla.org/ipc/dconnect-service;1", &rv);
+ RETURN_IF_FAILED(rv, "getting dconnect service");
+
+ PRUint32 remoteClientID = 1;
+
+ nsCOMPtr<nsIFile> file;
+ rv = dcon->CreateInstanceByContractID(remoteClientID,
+ NS_LOCAL_FILE_CONTRACTID,
+ NS_GET_IID(nsIFile),
+ getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> sup = do_QueryInterface(file, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ printf("*** calling QueryInterface\n");
+ nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString path;
+ path.AssignLiteral(TEST_PATH);
+
+ printf("*** calling InitWithNativePath\n");
+ rv = localFile->InitWithPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString buf;
+ rv = file->GetPath(buf);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!buf.Equals(path))
+ {
+ NS_ConvertUTF16toUTF8 temp(buf);
+ printf("*** GetPath erroneously returned [%s]\n", temp.get());
+ return NS_ERROR_FAILURE;
+ }
+
+ PRBool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv))
+ {
+ printf("*** Exists test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+ printf("File exists? [%d]\n", exists);
+
+ nsCOMPtr<nsIFile> clone;
+ rv = file->Clone(getter_AddRefs(clone));
+ if (NS_FAILED(rv))
+ {
+ printf("*** Clone test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString node;
+ node.AssignLiteral("hello.txt");
+
+ rv = clone->Append(node);
+ if (NS_FAILED(rv))
+ {
+ printf("*** Append test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ PRBool match;
+ rv = file->Equals(clone, &match);
+ if (NS_FAILED(rv))
+ {
+ printf("*** Equals test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+ printf("Files are equals? [%d]\n", match);
+
+ // now test passing null for interface pointer
+
+ rv = clone->Exists(&exists);
+ if (NS_FAILED(rv))
+ {
+ printf("*** Exists test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+ if (!exists)
+ {
+ rv = clone->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ if (NS_FAILED(rv))
+ {
+ printf("*** Create test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ rv = clone->MoveTo(nsnull, NS_LITERAL_STRING("helloworld.txt"));
+ if (NS_FAILED(rv))
+ {
+ printf("*** MoveTo test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ // now test passing local objects to a remote object
+
+ nsCOMPtr<nsILocalFile> myLocalFile;
+ rv = NS_NewLocalFile(path, PR_TRUE, getter_AddRefs(myLocalFile));
+ if (NS_FAILED(rv))
+ {
+ printf("*** NS_NewLocalFile failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = file->Equals(myLocalFile, &match);
+ if (NS_FAILED(rv))
+ {
+ printf("*** second Equals test failed [rv=%x]\n", rv);
+ return NS_ERROR_FAILURE;
+ }
+ printf("Files are equals? [%d]\n", match);
+
+ printf("*** DoTest completed successfully :-)\n");
+ return NS_OK;
+}
+
+int main(int argc, char **argv)
+{
+ nsresult rv;
+
+ PRBool serverMode = PR_FALSE;
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "-server") == 0)
+ {
+ serverMode = PR_TRUE;
+ }
+ else
+ {
+ printf("usage: %s [-server]\n", argv[0]);
+ return -1;
+ }
+ }
+
+ {
+ nsCOMPtr<nsIServiceManager> servMan;
+ NS_InitXPCOM2(getter_AddRefs(servMan), nsnull, nsnull);
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
+ NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
+ if (registrar)
+ registrar->AutoRegister(nsnull);
+
+ // Create the Event Queue for this thread...
+ nsCOMPtr<nsIEventQueueService> eqs =
+ do_GetService(kEventQueueServiceCID, &rv);
+ RETURN_IF_FAILED(rv, "do_GetService(EventQueueService)");
+
+ rv = eqs->CreateMonitoredThreadEventQueue();
+ RETURN_IF_FAILED(rv, "CreateMonitoredThreadEventQueue");
+
+ rv = eqs->GetThreadEventQueue(NS_CURRENT_THREAD, &gEventQ);
+ RETURN_IF_FAILED(rv, "GetThreadEventQueue");
+
+ nsCOMPtr<ipcIService> ipcServ(do_GetService(IPC_SERVICE_CONTRACTID, &rv));
+ RETURN_IF_FAILED(rv, "do_GetService(ipcServ)");
+ NS_ADDREF(gIpcServ = ipcServ);
+
+ if (!serverMode)
+ {
+ rv = DoTest();
+ RETURN_IF_FAILED(rv, "DoTest()");
+ }
+ else
+ {
+ gIpcServ->AddName("DConnectServer");
+ }
+
+ PLEvent *ev;
+ while (gKeepRunning)
+ {
+ gEventQ->WaitForEvent(&ev);
+ gEventQ->HandleEvent(ev);
+ }
+
+ NS_RELEASE(gIpcServ);
+
+ printf("*** processing remaining events\n");
+
+ // process any remaining events
+ while (NS_SUCCEEDED(gEventQ->GetEvent(&ev)) && ev)
+ gEventQ->HandleEvent(ev);
+
+ printf("*** done\n");
+ } // this scopes the nsCOMPtrs
+
+ // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
+ rv = NS_ShutdownXPCOM(nsnull);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
+}
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js
new file mode 100644
index 00000000..a72e133b
--- /dev/null
+++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/dconnect/test/TestServer.js
@@ -0,0 +1,66 @@
+/* vim:set ts=2 sw=2 et cindent: */
+/* ***** 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 IPC.
+ *
+ * The Initial Developer of the Original Code is IBM Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Darin Fisher <darin@meer.net>
+ *
+ * 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 ***** */
+
+/**
+ * This file contains code for a "server" that does nothing more
+ * than set a name for itself on the IPC system so that the client
+ * can connect and control this process.
+ */
+
+const ipcIService = Components.interfaces.ipcIService;
+const nsIEventQueueService = Components.interfaces.nsIEventQueueService;
+const nsIEventQueue = Components.interfaces.nsIEventQueue;
+
+function registerServer()
+{
+ var ipc = Components.classes["@mozilla.org/ipc/service;1"].getService(ipcIService);
+ ipc.addName("test-server");
+}
+
+function runEventQ()
+{
+ var eqs = Components.classes["@mozilla.org/event-queue-service;1"]
+ .getService(nsIEventQueueService);
+ eqs.createMonitoredThreadEventQueue();
+ var queue = eqs.getSpecialEventQueue(eqs.CURRENT_THREAD_EVENT_QUEUE);
+
+ // this never returns
+ queue.eventLoop();
+}
+
+registerServer();
+runEventQ();