diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/libs/xpcom18a4/ipc/ipcd/extensions | |
parent | Initial commit. (diff) | |
download | virtualbox-upstream.tar.xz virtualbox-upstream.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
64 files changed, 10538 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in new file mode 100644 index 00000000..e258946b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.in @@ -0,0 +1,49 @@ +# 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 +DIRS = lock transmngr dconnect + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/Makefile.kup 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 ¶mInfo, + 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, ¶mInfo, 0, &sizeArg); + if (NS_FAILED(rv)) + return rv; + + // get the number of valid elements + PRUint8 lenArg; + rv = iinfo->GetLengthIsArgNumberForParam(methodIndex, ¶mInfo, 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, ¶mInfo, 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 ¶mInfo, + 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 ¶mInfo, + 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 ¶mInfo, + 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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo, + 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, ¶mInfo, &result); + } + else if (tag == nsXPTType::T_INTERFACE_IS) + { + rv = iinfo->GetInterfaceIsArgNumberForParam(methodIndex, ¶mInfo, &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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo = 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 ¶mInfo, + 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(); diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/Makefile.in new file mode 100644 index 00000000..4b94a31f --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/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/lock/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in new file mode 100644 index 00000000..9a08abdb --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/Makefile.in @@ -0,0 +1,58 @@ +# 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_lock + +EXPORTS = \ + ipcLockCID.h \ + $(NULL) + +XPIDLSRCS = \ + ipcILockService.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl new file mode 100644 index 00000000..1cbf8651 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcILockService.idl @@ -0,0 +1,65 @@ +/* ***** 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 ***** */ + +#include "nsISupports.idl" + +/** + * This service provides named interprocess locking. + */ +[scriptable, uuid(9f6dbe15-d851-4b00-912a-5ac0be88a409)] +interface ipcILockService : nsISupports +{ + /** + * Call this method to acquire a named interprocess lock. + * + * @param aLockName + * specifies the name of the lock + * @param aWaitIfBusy + * wait for the lock to become available; otherwise, fail if lock + * is already held by some other process. + */ + void acquireLock(in string aLockName, + in boolean aWaitIfBusy); + + /** + * Call this method to release a named lock. + * + * @param aLockName + * specifies the name of the lock + */ + void releaseLock(in string aLockName); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h new file mode 100644 index 00000000..ba756d9b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/public/ipcLockCID.h @@ -0,0 +1,53 @@ +/* ***** 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 ***** */ + +#ifndef ipcLockCID_h__ +#define ipcLockCID_h__ + +#define IPC_LOCKSERVICE_CLASSNAME \ + "ipcLockService" +#define IPC_LOCKSERVICE_CONTRACTID \ + "@mozilla.org/ipc/lock-service;1" +#define IPC_LOCKSERVICE_CID \ +{ /* d9e56bf8-e32e-4b6d-87f1-06d73b0ce7ca */ \ + 0xd9e56bf8, \ + 0xe32e, \ + 0x4b6d, \ + {0x87, 0xf1, 0x06, 0xd7, 0x3b, 0x0c, 0xe7, 0xca} \ +} + +#endif // !ipcLockCID_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in new file mode 100644 index 00000000..26d943a8 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/Makefile.in @@ -0,0 +1,69 @@ +# 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 = module + +MODULE = ipcd +LIBRARY_NAME = ipcdlock_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +FORCE_USE_PIC = 1 + +REQUIRES = \ + xpcom \ + string \ + $(NULL) + +CPPSRCS = \ + ipcLockProtocol.cpp \ + ipcLockService.cpp \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../../../shared/src \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp new file mode 100644 index 00000000..5a7e7755 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.cpp @@ -0,0 +1,87 @@ +/* ***** 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 ***** */ + +#include <stdlib.h> +#include <string.h> +#include "prlog.h" +#include "ipcLockProtocol.h" + +//----------------------------------------------------------------------------- + +static inline PRUint8 get_opcode(const PRUint8 *buf) +{ + return (buf[0] & 0x0f); +} + +static inline PRUint8 get_flags(const PRUint8 *buf) +{ + return (buf[0] & 0xf0) >> 4; +} + +static inline const char *get_key(const PRUint8 *buf) +{ + return ((const char *) buf) + 1; +} + +//----------------------------------------------------------------------------- + +PRUint8 * +IPC_FlattenLockMsg(const ipcLockMsg *msg, PRUint32 *bufLen) +{ + PRUint32 len = 1 // header byte + + strlen(msg->key) // key + + 1; // null terminator + + PRUint8 *buf = (PRUint8 *) ::operator new(len); + if (!buf) + return NULL; + + buf[0] = (msg->opcode | (msg->flags << 4)); + + memcpy(&buf[1], msg->key, len - 1); + *bufLen = len; + return buf; +} + +void +IPC_UnflattenLockMsg(const PRUint8 *buf, PRUint32 bufLen, ipcLockMsg *msg) +{ + PR_ASSERT(bufLen > 2); // malformed buffer otherwise + msg->opcode = get_opcode(buf); + msg->flags = get_flags(buf); + msg->key = get_key(buf); +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h new file mode 100644 index 00000000..b74b5cde --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockProtocol.h @@ -0,0 +1,98 @@ +/* ***** 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 ***** */ + +#ifndef ipcLockProtocol_h__ +#define ipcLockProtocol_h__ + +#include "prtypes.h" + +// +// ipc lock message format: +// +// +----------------------------------+ +// | opcode : 4 bits | +// +----------------------------------+ +// | flags : 4 bits | +// +----------------------------------+ +// | key : null terminated string | +// +----------------------------------+ +// + +// lock opcodes +#define IPC_LOCK_OP_ACQUIRE 1 +#define IPC_LOCK_OP_RELEASE 2 +#define IPC_LOCK_OP_STATUS_ACQUIRED 3 +#define IPC_LOCK_OP_STATUS_FAILED 4 +#define IPC_LOCK_OP_STATUS_BUSY 5 + +// lock flags +#define IPC_LOCK_FL_NONBLOCKING 1 + +// data structure for representing lock request message +struct ipcLockMsg +{ + PRUint8 opcode; + PRUint8 flags; + const char * key; +}; + +// +// flatten a lock message +// +// returns a malloc'd buffer containing the flattened message. on return, +// bufLen contains the length of the flattened message. +// +PRUint8 *IPC_FlattenLockMsg(const ipcLockMsg *msg, PRUint32 *bufLen); + +// +// unflatten a lock message. upon return, msg->key points into buf, so +// buf must not be deallocated until after msg is no longer needed. +// +void IPC_UnflattenLockMsg(const PRUint8 *buf, PRUint32 bufLen, ipcLockMsg *msg); + +// +// TargetID for message passing +// +#define IPC_LOCK_TARGETID \ +{ /* 703ada8a-2d38-4d5d-9d39-03d1ccceb567 */ \ + 0x703ada8a, \ + 0x2d38, \ + 0x4d5d, \ + {0x9d, 0x39, 0x03, 0xd1, 0xcc, 0xce, 0xb5, 0x67} \ +} + +#endif // !ipcLockProtocol_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp new file mode 100644 index 00000000..771aa787 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.cpp @@ -0,0 +1,168 @@ +/* vim:set ts=4 sw=4 sts=4 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 + * 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 ***** */ + +#include <stdlib.h> +#include "nsDependentString.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" +#include "ipcLockService.h" +#include "ipcLockProtocol.h" +#include "ipcLog.h" +#include "prthread.h" + +static const nsID kLockTargetID = IPC_LOCK_TARGETID; + +//----------------------------------------------------------------------------- + +struct ipcPendingLock +{ + const char *name; + nsresult status; + PRBool complete; +}; + +//----------------------------------------------------------------------------- + +nsresult +ipcLockService::Init() +{ + if (PR_NewThreadPrivateIndex(&mTPIndex, nsnull) != PR_SUCCESS) + return NS_ERROR_OUT_OF_MEMORY; + + // Configure OnMessageAvailable to be called on the IPC thread. This is + // done to allow us to proxy OnAcquireLockComplete events to the right + // thread immediately even if the main thread is blocked waiting to acquire + // some other lock synchronously. + + return IPC_DefineTarget(kLockTargetID, this, PR_FALSE); +} + +NS_IMPL_THREADSAFE_ISUPPORTS2(ipcLockService, ipcILockService, ipcIMessageObserver) + +NS_IMETHODIMP +ipcLockService::AcquireLock(const char *lockName, PRBool waitIfBusy) +{ + LOG(("ipcLockService::AcquireLock [lock=%s wait=%u]\n", lockName, waitIfBusy)); + + ipcLockMsg msg; + msg.opcode = IPC_LOCK_OP_ACQUIRE; + msg.flags = (waitIfBusy ? 0 : IPC_LOCK_FL_NONBLOCKING); + msg.key = lockName; + + PRUint32 bufLen; + nsAutoPtr<PRUint8> buf( IPC_FlattenLockMsg(&msg, &bufLen) ); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + ipcPendingLock pendingLock; + pendingLock.name = lockName; + pendingLock.status = 0xDEADBEEF; // something bogus + pendingLock.complete = PR_FALSE; + if (PR_SetThreadPrivate(mTPIndex, &pendingLock) != PR_SUCCESS) + return NS_ERROR_UNEXPECTED; + + // prevent our OnMessageAvailable from being called until we explicitly ask + // for it to be called via IPC_WaitMessage. + IPC_DISABLE_MESSAGE_OBSERVER_FOR_SCOPE(kLockTargetID); + + nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen); + if (NS_SUCCEEDED(rv)) { + do { + // block the calling thread until we get a response from the daemon + rv = IPC_WaitMessage(0, kLockTargetID, this, nsnull, PR_INTERVAL_NO_TIMEOUT); + } + while (NS_SUCCEEDED(rv) && !pendingLock.complete); + + if (NS_SUCCEEDED(rv)) + rv = pendingLock.status; + } + + // we could clear the TPD, but that isn't really necessary. + + return rv; +} + +NS_IMETHODIMP +ipcLockService::ReleaseLock(const char *lockName) +{ + LOG(("ipcLockService::ReleaseLock [lock=%s]\n", lockName)); + + ipcLockMsg msg; + msg.opcode = IPC_LOCK_OP_RELEASE; + msg.flags = 0; + msg.key = lockName; + + PRUint32 bufLen; + PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen); + if (!buf) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = IPC_SendMessage(0, kLockTargetID, buf, bufLen); + delete buf; + + if (NS_FAILED(rv)) + return rv; + + return NS_OK; +} + +// called on the same thread that called IPC_WaitMessage +NS_IMETHODIMP +ipcLockService::OnMessageAvailable(PRUint32 unused, const nsID &target, + const PRUint8 *data, PRUint32 dataLen) +{ + ipcLockMsg msg; + IPC_UnflattenLockMsg(data, dataLen, &msg); + + LOG(("ipcLockService::OnMessageAvailable [lock=%s opcode=%u]\n", msg.key, msg.opcode)); + + ipcPendingLock *pendingLock = (ipcPendingLock *) PR_GetThreadPrivate(mTPIndex); + if (strcmp(pendingLock->name, msg.key) == 0) { + pendingLock->complete = PR_TRUE; + if (msg.opcode == IPC_LOCK_OP_STATUS_ACQUIRED) + pendingLock->status = NS_OK; + else + pendingLock->status = NS_ERROR_FAILURE; + return NS_OK; + } + + LOG(("message does not match; waiting for another...\n")); + + // else, we got a message that another thread is waiting to receive. + return IPC_WAIT_NEXT_MESSAGE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h new file mode 100644 index 00000000..6014172b --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/ipcLockService.h @@ -0,0 +1,63 @@ +/* vim:set ts=4 sw=4 sts=4 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 + * 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 ***** */ + +#ifndef ipcLockService_h__ +#define ipcLockService_h__ + +#include "ipcILockService.h" +#include "ipcdclient.h" + +//----------------------------------------------------------------------------- + +class ipcLockService : public ipcILockService + , public ipcIMessageObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCILOCKSERVICE + NS_DECL_IPCIMESSAGEOBSERVER + + NS_HIDDEN_(nsresult) Init(); + +private: + PRUintn mTPIndex; +}; + +//----------------------------------------------------------------------------- + +#endif // !ipcLockService_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in new file mode 100644 index 00000000..b35db1bc --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/Makefile.in @@ -0,0 +1,92 @@ +# 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 = lockmodule +ifeq ($(OS_ARCH), OS2) +SHORT_LIBNAME = lockmod +endif +MODULE_NAME = ipcd + +FORCE_SHARED_LIB = 1 +NO_DIST_INSTALL = 1 +NO_INSTALL = 1 + +ifeq ($(OS_ARCH),Darwin) +NO_COMPONENT_LINK_MAP = 1 +MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = +endif + +# required for #include "nsID.h" +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = ipcLockModule.cpp + +LOCAL_INCLUDES = \ + -I$(srcdir)/.. \ + $(NULL) + +EXTRA_OBJS = ../ipcLockProtocol.$(OBJ_SUFFIX) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(NSPR_LIBS) \ + $(EXTRA_DSO_LIBS) \ + $(EXTRA_OBJS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +_IPC_FILES = \ + $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) \ + $(NULL) + +libs:: $(_IPC_FILES) + $(INSTALL) $^ $(DIST)/bin/ipc/modules + +install:: $(_IPC_FILES) + $(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/ipc/modules diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp new file mode 100644 index 00000000..151c0f89 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/src/module/ipcLockModule.cpp @@ -0,0 +1,337 @@ +/* ***** 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 ***** */ + +#include <stdlib.h> +#include <stdio.h> +#include "ipcModuleUtil.h" +#include "ipcLockProtocol.h" +#include "plhash.h" +#include "plstr.h" + +#ifdef DEBUG +#define LOG(args) printf args +#else +#define LOG(args) +#endif + +static const nsID kLockTargetID = IPC_LOCK_TARGETID; + +static void +ipcLockModule_Send(PRUint32 cid, const char *key, PRUint8 opcode) +{ + ipcLockMsg msg = { opcode, 0, key }; + PRUint32 bufLen; + PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen); + if (!buf) + return; + IPC_SendMsg(cid, kLockTargetID, buf, bufLen); + free(buf); +} + +//----------------------------------------------------------------------------- + +// +// gLockTable stores mapping from lock name to ipcLockContext +// +static PLHashTable *gLockTable = NULL; + +//----------------------------------------------------------------------------- + +struct ipcLockContext +{ + PRUint32 mOwnerID; // client ID of this lock's owner + struct ipcLockContext *mNextPending; // pointer to client next in line to + // acquire this lock. + + ipcLockContext(PRUint32 ownerID) + : mOwnerID(ownerID) + , mNextPending(NULL) {} +}; + +//----------------------------------------------------------------------------- + +PR_STATIC_CALLBACK(void *) +ipcLockModule_AllocTable(void *pool, PRSize size) +{ + return malloc(size); +} + +PR_STATIC_CALLBACK(void) +ipcLockModule_FreeTable(void *pool, void *item) +{ + free(item); +} + +PR_STATIC_CALLBACK(PLHashEntry *) +ipcLockModule_AllocEntry(void *pool, const void *key) +{ + return (PLHashEntry *) malloc(sizeof(PLHashEntry)); +} + +PR_STATIC_CALLBACK(void) +ipcLockModule_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag) +{ + PL_strfree((char *) he->key); + free(he); +} + +static const PLHashAllocOps ipcLockModule_AllocOps = { + ipcLockModule_AllocTable, + ipcLockModule_FreeTable, + ipcLockModule_AllocEntry, + ipcLockModule_FreeEntry +}; + +//----------------------------------------------------------------------------- + +static void +ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key) +{ + LOG(("$$$ acquiring lock [key=%s]\n", key)); + + if (!gLockTable) + return; + + ipcLockContext *ctx; + + ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key); + if (ctx) { + // + // lock is already acquired, add this client to the queue. make + // sure this client doesn't already own the lock or live on the queue. + // + while (ctx->mOwnerID != cid && ctx->mNextPending) + ctx = ctx->mNextPending; + if (ctx->mOwnerID != cid) { + // + // if nonblocking, then send busy status message. otherwise, + // proceed to add this client to the pending queue. + // + if (flags & IPC_LOCK_FL_NONBLOCKING) + ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_BUSY); + else + ctx->mNextPending = new ipcLockContext(cid); + } + } + else { + // + // ok, add this lock to the table, and notify client that it now owns + // the lock! + // + ctx = new ipcLockContext(cid); + if (!ctx) + return; + + PL_HashTableAdd(gLockTable, PL_strdup(key), ctx); + + ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_ACQUIRED); + } +} + +static PRBool +ipcLockModule_ReleaseLockHelper(PRUint32 cid, const char *key, ipcLockContext *ctx) +{ + LOG(("$$$ releasing lock [key=%s]\n", key)); + + PRBool removeEntry = PR_FALSE; + + // + // lock is already acquired _or_ maybe client is on the pending list. + // + if (ctx->mOwnerID == cid) { + if (ctx->mNextPending) { + // + // remove this element from the list. since this is the + // first element in the list, instead of removing it we + // shift the data from the next context into this one and + // delete the next context. + // + ipcLockContext *next = ctx->mNextPending; + ctx->mOwnerID = next->mOwnerID; + ctx->mNextPending = next->mNextPending; + delete next; + // + // notify client that it now owns the lock + // + ipcLockModule_Send(ctx->mOwnerID, key, IPC_LOCK_OP_STATUS_ACQUIRED); + } + else { + delete ctx; + removeEntry = PR_TRUE; + } + } + else { + ipcLockContext *prev; + for (;;) { + prev = ctx; + ctx = ctx->mNextPending; + if (!ctx) + break; + if (ctx->mOwnerID == cid) { + // remove ctx from list + prev->mNextPending = ctx->mNextPending; + delete ctx; + break; + } + } + } + + return removeEntry; +} + +static void +ipcLockModule_ReleaseLock(PRUint32 cid, const char *key) +{ + if (!gLockTable) + return; + + ipcLockContext *ctx; + + ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key); + if (ctx && ipcLockModule_ReleaseLockHelper(cid, key, ctx)) + PL_HashTableRemove(gLockTable, key); +} + +PR_STATIC_CALLBACK(PRIntn) +ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg) +{ + PRUint32 cid = *(PRUint32 *) arg; + + ipcLockContext *ctx = (ipcLockContext *) he->value; + if (ctx->mOwnerID != cid) + return HT_ENUMERATE_NEXT; + + LOG(("$$$ ipcLockModule_ReleaseByCID [cid=%u key=%s he=%p]\n", + cid, (char*)he->key, (void*)he)); + + if (ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key, ctx)) + return HT_ENUMERATE_REMOVE; + + return HT_ENUMERATE_NEXT; +} + +//----------------------------------------------------------------------------- + +static void +ipcLockModule_Init() +{ + LOG(("$$$ ipcLockModule_Init\n")); + + gLockTable = PL_NewHashTable(32, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + &ipcLockModule_AllocOps, + NULL); +} + +static void +ipcLockModule_Shutdown() +{ + LOG(("$$$ ipcLockModule_Shutdown\n")); + + if (gLockTable) { + // XXX walk table destroying all ipcLockContext objects + + PL_HashTableDestroy(gLockTable); + gLockTable = NULL; + } +} + +static void +ipcLockModule_HandleMsg(ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen) +{ + PRUint32 cid = IPC_GetClientID(client); + + LOG(("$$$ ipcLockModule_HandleMsg [cid=%u]\n", cid)); + + ipcLockMsg msg; + IPC_UnflattenLockMsg((const PRUint8 *) data, dataLen, &msg); + + switch (msg.opcode) { + case IPC_LOCK_OP_ACQUIRE: + ipcLockModule_AcquireLock(cid, msg.flags, msg.key); + break; + case IPC_LOCK_OP_RELEASE: + ipcLockModule_ReleaseLock(cid, msg.key); + break; + default: + PR_NOT_REACHED("invalid opcode"); + } +} + +static void +ipcLockModule_ClientUp(ipcClientHandle client) +{ + LOG(("$$$ ipcLockModule_ClientUp [%u]\n", IPC_GetClientID(client))); +} + +static void +ipcLockModule_ClientDown(ipcClientHandle client) +{ + PRUint32 cid = IPC_GetClientID(client); + + LOG(("$$$ ipcLockModule_ClientDown [%u]\n", cid)); + + // + // enumerate lock table, release any locks held by this client. + // + + PL_HashTableEnumerateEntries(gLockTable, ipcLockModule_ReleaseByCID, &cid); +} + +//----------------------------------------------------------------------------- + +static ipcModuleMethods gLockMethods = +{ + IPC_MODULE_METHODS_VERSION, + ipcLockModule_Init, + ipcLockModule_Shutdown, + ipcLockModule_HandleMsg, + ipcLockModule_ClientUp, + ipcLockModule_ClientDown +}; + +static ipcModuleEntry gLockModuleEntry[] = +{ + { IPC_LOCK_TARGETID, &gLockMethods } +}; + +IPC_IMPL_GETMODULES(ipcLockModule, gLockModuleEntry) diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in new file mode 100644 index 00000000..bcf9299a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/Makefile.in @@ -0,0 +1,67 @@ +# vim:set ts=8 sw=8 noet: +# ***** 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 ***** + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dconnect + +REQUIRES = ipcd \ + nspr \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + TestIPCLocks.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/lock/test/TestIPCLocks.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp new file mode 100644 index 00000000..f35e8434 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/lock/test/TestIPCLocks.cpp @@ -0,0 +1,244 @@ +/* vim:set ts=2 sw=2 sts=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 test program spawns N copies of itself, and each copy spawns M threads. +// Each thread acquires and releases a named, interprocess lock. +// Randomized delays are injected at various points to exercise the system, and +// help expose any race conditions that may exist. +// +// Usage: TestIPCLocks [-N] + +#include <stdlib.h> +#include <stdio.h> +#include "ipcILockService.h" +#include "ipcLockCID.h" +#include "nsIServiceManagerUtils.h" +#include "nsIEventQueueService.h" +#include "nsCOMPtr.h" +#include "nsXPCOM.h" +#include "prproces.h" +#include "prprf.h" + +#if defined(XP_UNIX) || defined(XP_OS2) || defined(XP_BEOS) +#include <unistd.h> +static unsigned GetPID() +{ + return (unsigned) getpid(); +} +#elif defined(XP_WIN) +#include <windows.h> +static unsigned GetPID() +{ + return (unsigned) GetCurrentProcessId(); +} +#else +static unsigned int GetPID() +{ + return 0; // implement me! +} +#endif + +static void LOG(const char *fmt, ... ) +{ + va_list ap; + va_start(ap, fmt); + PRUint32 nb = 0; + char buf[512]; + + nb = PR_snprintf(buf, sizeof(buf), "[%u:%p] ", GetPID(), PR_GetCurrentThread()); + + PR_vsnprintf(buf + nb, sizeof(buf) - nb, fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + + fwrite(buf, strlen(buf), 1, stdout); + fflush(stdout); + + va_end(ap); +} + +static void RandomSleep(PRUint32 fromMS, PRUint32 toMS) +{ + PRUint32 ms = fromMS + (PRUint32) ((toMS - fromMS) * ((double) rand() / RAND_MAX)); + //LOG("putting thread to sleep for %u ms\n", ms); + PR_Sleep(PR_MillisecondsToInterval(ms)); +} + +static ipcILockService *gLockService; + +PR_STATIC_CALLBACK(void) TestThread(void *arg) +{ + const char *lockName = (const char *) arg; + + LOG("entering TestThread [lock=%s]\n", lockName); + + nsresult rv; + + RandomSleep(1000, 1100); + + //LOG("done sleeping\n"); + + rv = gLockService->AcquireLock(lockName, PR_TRUE); + if (NS_SUCCEEDED(rv)) + { + //LOG("acquired lock \"%s\"\n", lockName); + RandomSleep(500, 1000); + //LOG("releasing lock \"%s\"\n", lockName); + rv = gLockService->ReleaseLock(lockName); + if (NS_FAILED(rv)) + { + LOG("failed to release lock [rv=%x]\n", rv); + NS_ERROR("failed to release lock"); + } + } + else + { + LOG("failed to acquire lock [rv=%x]\n", rv); + NS_NOTREACHED("failed to acquire lock"); + } + + LOG("exiting TestThread [lock=%s rv=%x]\n", lockName, rv); +} + +static const char *kLockNames[] = { + "foopy", + "test", + "1", + "xyz", + "moz4ever", + nsnull +}; + +static nsresult DoTest() +{ + nsresult rv; + + nsCOMPtr<nsIEventQueueService> eqs = + do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + rv = eqs->CreateMonitoredThreadEventQueue(); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIEventQueue> eq; + rv = eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eq)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<ipcILockService> lockService = + do_GetService(IPC_LOCKSERVICE_CONTRACTID); + + gLockService = lockService; + + PRThread *threads[10] = {0}; + int i = 0; + + for (const char **lockName = kLockNames; *lockName; ++lockName, ++i) + { + threads[i] = PR_CreateThread(PR_USER_THREAD, + TestThread, + (void *) *lockName, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); + } + + for (i=0; threads[i]; ++i) + { + PR_JoinThread(threads[i]); + threads[i] = nsnull; + } + + gLockService = nsnull; + + LOG("joined with all threads; exiting DoTest\n"); + return NS_OK; +} + +int main(int argc, char **argv) +{ + LOG("entering main\n"); + + int numProcs = 10; + + // if this is a child process, then just run the test + if (argc > 1) + { + if (strcmp(argv[1], "-child") == 0) + { + RandomSleep(1000, 1000); + LOG("running child test\n"); + NS_InitXPCOM2(nsnull, nsnull, nsnull); + DoTest(); + NS_ShutdownXPCOM(nsnull); + return 0; + } + else if (argv[1][0] == '-') + { + // argument is a number + numProcs = atoi(argv[1] + 1); + if (numProcs == 0) + { + printf("### usage: TestIPCLocks [-N]\n" + "where, N is the number of test processes to spawn.\n"); + return -1; + } + } + } + + LOG("sleeping for 1 second\n"); + PR_Sleep(PR_SecondsToInterval(1)); + + PRProcess **procs = (PRProcess **) malloc(sizeof(PRProcess*) * numProcs); + int i; + + // else, spawn the child processes + for (i=0; i<numProcs; ++i) + { + char *const argv[] = {"./TestIPCLocks", "-child", nsnull}; + LOG("spawning child test\n"); + procs[i] = PR_CreateProcess("./TestIPCLocks", argv, nsnull, nsnull); + } + + PRInt32 exitCode; + for (i=0; i<numProcs; ++i) + PR_WaitProcess(procs[i], &exitCode); + + return 0; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in new file mode 100644 index 00000000..5f80740d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/Makefile.in @@ -0,0 +1,52 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = common module public src + +ifdef ENABLE_TESTS +DIRS += test +endif + +include $(topsrcdir)/config/rules.mk + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore new file mode 100644 index 00000000..b292dd21 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/.cvsignore @@ -0,0 +1,9 @@ +Makefile +module.rc +module.res +tmModule.obj +transmngr_client.dll +transmngr_client.pdb +transmngr_client.exp +transmngr_client.lib +transmngr_client.ilk
\ No newline at end of file diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in new file mode 100644 index 00000000..87277839 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/Makefile.in @@ -0,0 +1,75 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = transmngr +LIBRARY_NAME = transmngr_client +EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 +MODULE_NAME = transmngr + +REQUIRES = ipcd \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = tmModule.cpp +EXPORTS = tmCID.h + +SHARED_LIBRARY_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)transmngr_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)transmngrcom_s.$(LIB_SUFFIX) \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../src \ + -I$(srcdir)/../common \ + $(NULL) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(EXTRA_DSO_LIBS) \ + $(MOZ_COMPONENT_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h new file mode 100644 index 00000000..8ca23b84 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmCID.h @@ -0,0 +1,53 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef tmCID_h__ +#define tmCID_h__ + +#define TRANSACTION_SERVICE_CLASSNAME \ + "tmTransactionService" +#define TRANSACTION_SERVICE_CONTRACTID \ + "@mozilla.org/transaction/service;1" +#define TRANSACTION_SERVICE_CID \ +{ /* 1403adf4-94d1-4c67-a8ae-d9f86972d378 */ \ + 0x1403adf4, \ + 0x94d1, \ + 0x4c67, \ + {0xa8, 0xae, 0xd9, 0xf8, 0x69, 0x72, 0xd3, 0x78} \ +} + +#endif // !tmCID_h__ diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp new file mode 100644 index 00000000..2853ba0a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/build/tmModule.cpp @@ -0,0 +1,71 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "nsICategoryManager.h" +#include "nsIGenericFactory.h" +#include "nsIServiceManager.h" +#include "tmCID.h" +#include "tmTransactionService.h" + +//----------------------------------------------------------------------------- +// Define the contructor function for the objects +// +// NOTE: This creates an instance of objects by using the default constructor +//----------------------------------------------------------------------------- +NS_GENERIC_FACTORY_CONSTRUCTOR(tmTransactionService) + +//----------------------------------------------------------------------------- +// Define a table of CIDs implemented by this module along with other +// information like the function to create an instance, contractid, and +// class name. +//----------------------------------------------------------------------------- +static const nsModuleComponentInfo components[] = { + { TRANSACTION_SERVICE_CLASSNAME, + TRANSACTION_SERVICE_CID, + TRANSACTION_SERVICE_CONTRACTID, + tmTransactionServiceConstructor }, + /* + tmTransactionServiceRegisterProc, + tmTransactionServiceUnregisterProc }, + */ +}; + +//----------------------------------------------------------------------------- +// Implement the NSGetModule() exported function for your module +// and the entire implementation of the module object. +//----------------------------------------------------------------------------- +NS_IMPL_NSGETMODULE(transmngr, components) diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore new file mode 100644 index 00000000..df8a43a2 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/.cvsignore @@ -0,0 +1,5 @@ +Makefile +tmTransaction.obj +tmVector.obj +transmgrcom_s.pdb +transmgrcom_s.lib diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in new file mode 100644 index 00000000..4521c39a --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/Makefile.in @@ -0,0 +1,61 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = transmgrcom_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +FORCE_USE_PIC = 1 + +REQUIRES = \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmTransaction.cpp \ + tmVector.cpp \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp new file mode 100644 index 00000000..5fb7e5bf --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.cpp @@ -0,0 +1,107 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include <stdlib.h> +#include "tmTransaction.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Constructor(s) & Destructor + +tmTransaction::~tmTransaction() { + if (mHeader) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree(mHeader); +#else + free(mHeader); +#endif +} + +// call only once per lifetime of object. does not reclaim the +// raw message, only sets it. +nsresult +tmTransaction::Init(PRUint32 aOwnerID, + PRInt32 aQueueID, + PRUint32 aAction, + PRInt32 aStatus, + const PRUint8 *aMessage, + PRUint32 aLength) { + nsresult rv = NS_OK; + tmHeader *header = nsnull; + + // indicates the message is the entire raw message + if (aQueueID == TM_INVALID_ID) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + header = (tmHeader*) RTMemAlloc(aLength); +#else + header = (tmHeader*) malloc(aLength); +#endif + if (header) { + mRawMessageLength = aLength; + memcpy(header, aMessage, aLength); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + else { // need to create the tmHeader and concat the message +#ifdef VBOX_USE_IPRT_IN_XPCOM + header = (tmHeader*) RTMemAlloc (sizeof(tmHeader) + aLength); +#else + header = (tmHeader*) malloc (sizeof(tmHeader) + aLength); +#endif + if (header) { + mRawMessageLength = sizeof(tmHeader) + aLength; + header->action = aAction; + header->queueID = aQueueID; + header->status = aStatus; + header->reserved = 0x00000000; + if (aLength > 0) // add the message if it exists + memcpy((header + 1), aMessage, aLength); + } + else + rv = NS_ERROR_OUT_OF_MEMORY; + } + + if (NS_SUCCEEDED(rv)) { + mOwnerID = aOwnerID; + mHeader = header; + } + + return rv; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h new file mode 100644 index 00000000..b86f20f0 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmTransaction.h @@ -0,0 +1,234 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransaction_H_ +#define _tmTransaction_H_ + +#include "tmUtils.h" + +////////////////////////////////////////////////////////////////////////////// +// +// Message format +// +// |------------------------------------|-- +// |QueueID | | +// |------------------------------------| | +// | Action - Post/Flush/Attach etc | |- this is the tmHeader struct +// |------------------------------------| | +// |Status | | +// |------------------------------------| | +// |Padding | | +// |------------------------------------|-- +// |Message Data (payload) | +// |------------------------------------| +// +// The Attach call is a special case in that it doesn't have a QueueID yet. A +// QueueID will be 0's. The message Data will be the Queue Name String which +// will be the profile name with a domain attached, a domain being +// [prefs|cookies|etc] +// +////////////////////////////////////////////////////////////////////////////// + +/** + * tmHeader contains various flags identifying + */ +struct tmHeader { + PRInt32 queueID; // will be the index of the queue in the TM, can be < 0 + PRUint32 action; // defined by tmUtils.h will be > 0 + PRInt32 status; // return values from methods, could be < 0 + PRUint32 reserved; // not currently used, maintaining word alignment +}; + +/** + * Using tmTransaction: + * + * After creating a tmTransaction either through new or as a member + * or local variable a process must call Init() with the proper set of + * arguements to initialize the state of the transaction. tmTransaction is + * set up to accept 3 types of initialization. + * + * 1) Raw message - All data is carried in the byte pointer aMessage, + * args 2,3,4 should be set to TM_NO_ID and aLength + * must be set to the full length of aMessage, including null + * termination if the payload is a null-term string and the size of the + * tmHeader struct preceeding the message. Currently this + * format is used at the IPC boundary, where we receive a byte pointer + * from the IPC Daemon. + * + * 2) Flags only - aQueueID, aAction and aStatus are all set. aMessage + * should be set to nsnull and aLength to 0. This format is used when + * sending reply messages (except for ATTACH_REPLY) and when the TS + * Transaction Service is sending "control" messages to the Manager - + * flush, detach, etc... + * + * 3) Flags and message - All arguements are set. The aMessage is only + * the message for the client app. aLength should be set to the length + * of aMessage and not include the length of the tmHeader struct. + * + * The only data member you can set post initialization is the QueueID. + * You should only call Init() once in the lifetime of a tmTransaction + * as it doesn't clean up the exisiting data before assigning the new + * data. Therefore it would leak most heinously if Init() were to be + * called twice. + * + * mOwnerID only has relevance on the IPC daemon side of things. The + * Transaction Service has no knowledge of this ID and makes no use + * of it. + */ +class tmTransaction +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor + + tmTransaction(): mHeader(nsnull), mRawMessageLength(0), mOwnerID(0) { } + + virtual ~tmTransaction(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + // Initializer //////////// + + /** + * Sets up the internal data of the transaction. Allows for 3 basic ways + * to call this function: No flags and just one big raw message, Just + * flags and no message, and finally flags and message. If the message + * exists it is copied into the transaction. + * + * @param aOwnerID is given to us by the IPC Daemon and is specific + * to that transport layer. It is only set when transactions + * are sent from the TM to the TS. + * + * @param aQueueID is the either the destination queue, or the queue from + * where this transaction is eminating + * + * @param aAction is the action that occured to generate this transaction + * + * @param aStatus is the success state of the action. + * + * @param aMessage can be a raw message including the 3 flags above or it + * can be just the "payload" of the transaction that the destination + * process is going deal with. + * + * @param aLength is the length of the message. If there is a null + * terminated string in the message make sure the length includes + * the null termination. + * + * @returns NS_OK if everything was successful + * @returns NS_ERROR_OUT_OF_MEMORY if allocation of space for the + * copy of the message fails. + */ + nsresult Init(PRUint32 aOwnerID, + PRInt32 aQueueID, + PRUint32 aAction, + PRInt32 aStatus, + const PRUint8 *aMessage, + PRUint32 aLength); + + // Data Accessors ///////// + + /** + * @returns a const byte pointer to the message + */ + const PRUint8* GetMessage() const { return (PRUint8*)(mHeader + 1); } + + /** + * @returns the length of the message + */ + PRUint32 GetMessageLength() const { + return (mRawMessageLength > sizeof(tmHeader)) ? + (mRawMessageLength - sizeof(tmHeader)) : 0; + } + + /** + * @returns a const pointer to the memory containing the + * flag information followed immediately by the message + * data. + */ + const PRUint8* GetRawMessage() const { return (PRUint8*) mHeader; } + + /** + * @returns the length of the flags and message combined + */ + PRUint32 GetRawMessageLength() const { return mRawMessageLength; } + + /** + * @returns the id of the destination or sending queue, depending on the + * direction of the transaction. + */ + PRInt32 GetQueueID() const { return mHeader->queueID; } + + /** + * @returns the action represented by this transaction + */ + PRUint32 GetAction() const { return mHeader->action; } + + /** + * @returns the success state, if applicable of the action leading + * up to this message + */ + PRInt32 GetStatus() const { return mHeader->status; } + + /** + * @returns the client ID (in IPC daemon terms) of the client who initiated + * the exchange that generated this transaction. + */ + PRUint32 GetOwnerID() const { return mOwnerID; } + + // Data Mutator /////////// + + /** + * Sets the ID of the destination or source queue. Init should have been + * called before the call to this function. + */ + void SetQueueID(PRInt32 aID) { mHeader->queueID = aID; } + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + tmHeader* mHeader; // points to beginning of entire message + PRUint32 mRawMessageLength; // length of entire message, incl tmHeader + PRUint32 mOwnerID; // client who sent this trans. - a IPC ClientID + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h new file mode 100644 index 00000000..f85184d3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmUtils.h @@ -0,0 +1,93 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmUtils_H_ +#define _tmUtils_H_ + +#include "nscore.h" +#include "nsError.h" +#include "nsID.h" +#include "prlog.h" +#include <stdio.h> + +// UUID used to identify the Transaction Module in both daemon and client +// not part of the XPCOM hooks, but rather a means of identifying +// modules withing the IPC daemon. +#define TRANSACTION_MODULE_ID \ +{ /* c3dfbcd5-f51d-420b-abf4-3bae445b96a9 */ \ + 0xc3dfbcd5, \ + 0xf51d, \ + 0x420b, \ + {0xab, 0xf4, 0x3b, 0xae, 0x44, 0x5b, 0x96, 0xa9} \ +} + +//static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +/////////////////////////////////////////////////////////////////////////////// +// match NS_ERROR_FOO error code formats +// +// only create new errors for those errors that are specific to TM + +#define NS_ERROR_MODULE_TM 27 /* XXX goes in nserror.h -- integrating with ns error codes */ + +#define TM_ERROR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 1) +#define TM_ERROR_WRONG_QUEUE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 2) +#define TM_ERROR_NOT_POSTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 3) +#define TM_ERROR_QUEUE_EXISTS NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 4) +#define TM_SUCCESS_DELETE_QUEUE NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_TM, 6) + + +// XXX clean up: +#define TM_INVALID_ID 0xFFFFFFFF +#define TM_INVALID 0xFFFFFFFF +#define TM_NO_ID 0xFFFFFFFE + +// Transaction Actions +enum { + TM_ATTACH = 0, + TM_ATTACH_REPLY, + TM_POST, + TM_POST_REPLY, + TM_NOTIFY, + TM_FLUSH, + TM_FLUSH_REPLY, + TM_DETACH, + TM_DETACH_REPLY +}; + +#endif + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp new file mode 100644 index 00000000..99213f27 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.cpp @@ -0,0 +1,179 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include <stdlib.h> +#include "tmVector.h" +#ifdef VBOX_USE_IPRT_IN_XPCOM +# include <iprt/mem.h> +#endif + +//////////////////////////////////////////////////////////////////////////// +// Constructor(s) & Destructor + +// can not be responsible for reclaiming memory pointed to by the void*s in +// the collection - how would we reclaim, don't know how they were allocated +tmVector::~tmVector() { + if (mElements) +#ifdef VBOX_USE_IPRT_IN_XPCOM + RTMemFree((void*)mElements); +#else + free((void*)mElements); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Public Member Functions + +nsresult +tmVector::Init() { + +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemAllocZ (mCapacity * sizeof(void*)); +#else + mElements = (void**) calloc (mCapacity, sizeof(void*)); +#endif + if (!mElements) + return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// mutators + +PRInt32 +tmVector::Append(void *aElement){ + PR_ASSERT(aElement); + + // make sure there is room + if (mNext == mCapacity) + if (NS_FAILED(Grow())) + return -1; + + // put the element in the array + mElements[mNext] = aElement; + mCount++; + + // encapsulates the index into a success value + return mNext++; // post increment. +} + +void +tmVector::Remove(void *aElement) { + PR_ASSERT(aElement); + + for (PRUint32 index = 0; index < mNext; index++) { + if (mElements[index] == aElement) { + mElements[index] = nsnull; + mCount--; + if (index == mNext-1) { // if we removed the last element + mNext--; + // don't test for success of the shrink + Shrink(); + } + } + } +} + +void +tmVector::RemoveAt(PRUint32 aIndex) { + PR_ASSERT(aIndex < mNext); + + // remove the element if it isn't already nsnull + if (mElements[aIndex] != nsnull) { + mElements[aIndex] = nsnull; + mCount--; + if (aIndex == mNext-1) { // if we removed the last element + mNext--; + // don't test for success of the shrink + Shrink(); + } + } +} + +//void* +//tmVector::operator[](int index) { +// if (index < mNext && index >= 0) +// return mElements[index]; +// return nsnull; +//} + +// Does not delete any of the data, merely removes references to them +void +tmVector::Clear(){ + memset(mElements, 0, mCapacity); + mCount = 0; + mNext = 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Protected Member Functions + +// increases the capacity by the growth increment +nsresult +tmVector::Grow() { + + PRUint32 newcap = mCapacity + GROWTH_INC; +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemRealloc(mElements, (newcap * sizeof(void*))); +#else + mElements = (void**) realloc(mElements, (newcap * sizeof(void*))); +#endif + if (mElements) { + mCapacity = newcap; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +// reduces the capacity by the growth increment. leaves room +// for one more add before needing to Grow(). +nsresult +tmVector::Shrink() { + + PRUint32 newcap = mCapacity - GROWTH_INC; + if (mNext < newcap) { +#ifdef VBOX_USE_IPRT_IN_XPCOM + mElements = (void**) RTMemRealloc(mElements, newcap * sizeof(void*)); +#else + mElements = (void**) realloc(mElements, newcap * sizeof(void*)); +#endif + if (!mElements) + return NS_ERROR_OUT_OF_MEMORY; + mCapacity = newcap; + } + return NS_OK; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h new file mode 100644 index 00000000..4ddb68ef --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/common/tmVector.h @@ -0,0 +1,160 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmVector_H_ +#define _tmVector_H_ + +#include "tmUtils.h" + +#define GROWTH_INC 5 + +/** + * A simple, clear, self-growing, collection of objects. typed independant + * basically a growing array. Useful in situations where you need an + * indexed collection but do not know the size in advance and need the + * ability for increase and decrease in size. Not optimized for anything + * in particular, or any size in particular. + * + * Is able to guarantee the index of an item will + * not change due to removals of a lower indexed item. The growing, + * and shrinking all happens to the end of the collection + * + * Does not backfill, adds to the end. At some point this should be + * changed to make best use of space. + */ +class tmVector +{ +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor + + /** + * Set some sane default values to set up the internal storage. Init() + * must be called after construction of the object to allcate the + * backing store. + */ + tmVector() : mNext(0), mCount(0), mCapacity(10), mElements(nsnull) {;} + + /** + * Reclaim the memory allocated in the Init() method. + */ + virtual ~tmVector(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Allocates the storage back-end + * + * @returns NS_OK if allocation succeeded + * @returns NS_ERROR_OUT_OF_MEMORY if the allocation failed + */ + nsresult Init(); + + // mutators + + /** + * @returns the index of the element added, if successful + * @returns -1 if an error occured during allocation of space + */ + PRInt32 Append(void *aElement); + + /** + * This does not collapse the collection, it leaves holes. Note, it also + * doesn't delete the element, it merely removes it from the collection + */ + void Remove(void *aElement); + + /** + * This does not collapse the collection, it leaves holes. Note, it also + * doesn't delete the element, it merely removes it from the collection + */ + void RemoveAt(PRUint32 aIndex); + + /** + * Does not call delete on the elements since we have no idea how to + * reclaim the memory. Sets all array slots to 0. + */ + void Clear(); + + /** + * @returns the element at the index indicated, including nsnull if the + * slot is empty. + */ + void* operator[](PRUint32 index) { + PR_ASSERT(index < mNext); + return mElements[index]; + } + + /** + * @returns the number of elements stored + */ + PRUint32 Count() { return mCount; } + + /** + * Meant to be used as the conditional in a loop. |index < size| should + * reach all elements of the collection and not run out of bounds. If + * slots 0,1,4,5,6 contain elements Size() will return 7, Count() will + * return 5. + * + * @returns the number of slots in the array taken, irrespective of + * holes in the collection. + */ + PRUint32 Size() { return mNext; } + +protected: + + nsresult Grow(); // mCapacity += GROWTH_INC - realloc()s + nsresult Shrink(); // mCapacity -= GROWTH_INC - dumb, free, malloc + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + // bookkeeping variables + PRUint32 mNext; // next element insertion slot (0 based) + PRUint32 mCount; // how many elements in the Vector (1 based) + PRUint32 mCapacity; // current capacity of the Vector (1 based) + + // the actual array of objects being stored + void **mElements; + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore new file mode 100644 index 00000000..ccfb2bc3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/.cvsignore @@ -0,0 +1,11 @@ +Makefile +module.rc +module.res +tmIPCModule.obj +tmQueue.obj +tmTransactionManager.obj +transmgr.dll +transmgr.exp +transmgr.ilk +transmgr.lib +transmgr.pdb diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in new file mode 100644 index 00000000..b8baa225 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/Makefile.in @@ -0,0 +1,100 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = transmgr +MODULE_NAME = ipcd + +FORCE_SHARED_LIB = 1 +NO_DIST_INSTALL = 1 +NO_INSTALL = 1 + +ifeq ($(OS_ARCH),Darwin) +NO_COMPONENT_LINK_MAP = 1 +MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = +endif + +REQUIRES = nspr \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmIPCModule.cpp \ + tmQueue.cpp \ + tmTransactionManager.cpp \ + $(NULL) + +EXPORTS = \ + tmIPCModule.h \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(srcdir)/../common \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +LIBS = \ + $(EXTRA_DSO_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +EXTRA_DSO_LDOPTS = \ + $(LIBS_DIR) \ + $(NSPR_LIBS) \ + $(DIST)/lib/$(LIB_PREFIX)transmgrcom_s.$(LIB_SUFFIX) \ + $(EXTRA_DSO_LIBS) \ + $(NULL) + +include $(topsrcdir)/config/rules.mk + +_IPC_FILES = \ + $(DLL_PREFIX)$(LIBRARY_NAME)$(DLL_SUFFIX) \ + $(NULL) + +libs:: $(_IPC_FILES) + $(INSTALL) $^ $(DIST)/bin/ipc/modules + +install:: $(_IPC_FILES) + $(SYSINSTALL) $(IFLAGS1) $^ $(DESTDIR)$(mozappdir)/ipc/modules diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp new file mode 100644 index 00000000..3982c93c --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.cpp @@ -0,0 +1,137 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "tmIPCModule.h" +#include "tmTransaction.h" +#include "tmTransactionManager.h" + +/////////////////////////////////////////////////////////////////////////////// +// IPC Daemon hookup stuff + +// functionpointer array giving access to this module from the IPC daemon +static ipcModuleMethods gTransMethods = +{ + IPC_MODULE_METHODS_VERSION, + tmIPCModule::Init, + tmIPCModule::Shutdown, + tmIPCModule::HandleMsg +}; + +static ipcModuleEntry gTransModuleEntry[] = +{ + { TRANSACTION_MODULE_ID, &gTransMethods } +}; + +IPC_IMPL_GETMODULES(TransactionModule, gTransModuleEntry) + +static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +/////////////////////////////////////////////////////////////////////////////// +// Define global variable + +tmTransactionManager *tmIPCModule::tm; + +/////////////////////////////////////////////////////////////////////////////// +// IPC Module API + +void +tmIPCModule::Init() { + if (!tm) + InitInternal(); +} + +void +tmIPCModule::Shutdown() { + if (tm) { + delete tm; + tm = nsnull; + } +} + +// straight pass-through, don't check args, let the TM do it. +void +tmIPCModule::HandleMsg(ipcClientHandle client, const nsID &target, + const void *data, PRUint32 dataLen) { + + // make sure the trans mngr is there + if (!tm && (InitInternal() < 0)) + return; + + // create the transaction + tmTransaction *trans = new tmTransaction(); + + // initialize it + if (trans) { + if(NS_SUCCEEDED(trans->Init(IPC_GetClientID(client), // who owns it + TM_INVALID_ID, // in data + TM_INVALID, // in data + TM_INVALID, // in data + (PRUint8 *)data, // raw message + dataLen))) { // length of message + // pass it on to the trans mngr + tm->HandleTransaction(trans); + } + else + delete trans; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// tmIPCModule API + +// straight pass-through, don't check args, let the TS & TM do it. +void +tmIPCModule::SendMsg(PRUint32 aDestClientIPCID, tmTransaction *aTransaction) { + + IPC_SendMsg(aDestClientIPCID, + kTransModuleID, + (void *)aTransaction->GetRawMessage(), + aTransaction->GetRawMessageLength()); +} + +/////////////////////////////////////////////////////////////////////////////// +// Protected Methods + +PRInt32 +tmIPCModule::InitInternal() { + + tm = new tmTransactionManager(); + if (tm) + return tm->Init(); + return -1; +} + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h new file mode 100644 index 00000000..d4623b1e --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmIPCModule.h @@ -0,0 +1,109 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmIPCModule_H_ +#define _tmIPCModule_H_ + +#include "ipcModuleUtil.h" +#include "tmUtils.h" + +// forward declarations +class tmTransaction; +class tmTransactionManager; + +/** + * Basically an interface between the tmTransactionManager and the IPC + * daemon. Does little else than format the data from one party into + * a format understandable to the other. + * + * The reason for this class is to try and abstract the transportation + * layer the transaction service uses. By using this class the Transaction + * Manager itself only needs to know that clients are identified by + * PRUint32 IDs. + */ +class tmIPCModule +{ +public: + + //////////////////////////////////////////////////////////////////////////// + // ipcModule API - called from IPC daemon + + /** + * Clean up the TM + */ + static void Shutdown(); + + /** + * Check the TM, create it if neccessary. + */ + static void Init(); + + /** + * Receives a message from the IPC daemon, creates a transaction and sends + * it to the TM to deal with. + */ + static void HandleMsg(ipcClientHandle client, + const nsID &target, + const void *data, + PRUint32 dataLen); + + //////////////////////////////////////////////////////////////////////////// + // tmIPCModule API - called from tmTransactionManager + + /** + * Sends the message to the IPC daemon to be deliverd to the client arg. + */ + static void SendMsg(PRUint32 aDestClientIPCID, tmTransaction *aTransaction); + +protected: + + /** + * tm should be null coming into this method. This does NOT delete tm + * first. + * + * @returns NS_OK if everything succeeds + * @returns -1 if initialization fails + */ + static PRInt32 InitInternal(); + + static tmTransactionManager *tm; + +}; + +#endif + + diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp new file mode 100644 index 00000000..5164446f --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.cpp @@ -0,0 +1,223 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "plstr.h" +#include "tmQueue.h" +#include "tmTransaction.h" +#include "tmTransactionManager.h" +#include "tmUtils.h" + +/////////////////////////////////////////////////////////////////////////////// +// Constructors & Destructor + +tmQueue::~tmQueue() { + + // empty the vectors + PRUint32 index = 0; + PRUint32 size = mTransactions.Size(); + for ( ; index < size ; index++) { + if (mTransactions[index]) + delete (tmTransaction *)mTransactions[index]; + } + + // don't need to delete the mListeners because + // we just insert PRUint32s, no allocation + + mTM = nsnull; + mID = 0; + if (mName) + PL_strfree(mName); +} + +/////////////////////////////////////////////////////////////////////////////// +// Public Methods + +PRInt32 +tmQueue::Init(const char* aName, PRUint32 aID, tmTransactionManager *aTM) { + PR_ASSERT(mTM == nsnull); + + if (NS_SUCCEEDED(mTransactions.Init()) && + NS_SUCCEEDED(mListeners.Init()) && + ((mName = PL_strdup(aName)) != nsnull) ) { + mTM = aTM; + mID = aID; + return NS_OK; + } + return -1; +} + +PRInt32 +tmQueue::AttachClient(PRUint32 aClientID) { + + PRInt32 status = NS_OK; // success of adding client + + if (!IsAttached(aClientID)) { + // add the client to the listener list -- null safe call + status = mListeners.Append((void*) aClientID); + } + else + status = -2; + + // create & init a reply transaction + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, // owner's ipc ID + mID, // client gets our ID + TM_ATTACH_REPLY, // action + status, // success of the add + (PRUint8*)mName, // client matches name to ID + PL_strlen(mName)+1))) { + // send the reply + mTM->SendTransaction(aClientID, &trans); + } + + // if we successfully added the client - send all current transactions + if (status >= 0) { // append returns the index of the added element + + PRUint32 size = mTransactions.Size(); + for (PRUint32 index = 0; index < size; index++) { + if (mTransactions[index]) + mTM->SendTransaction(aClientID, (tmTransaction*) mTransactions[index]); + } + } + return status; +} + +PRInt32 +tmQueue::DetachClient(PRUint32 aClientID) { + + PRUint32 size = mListeners.Size(); + PRUint32 id = 0; + PRInt32 status = -1; + + for (PRUint32 index = 0; index < size; index++) { + id = (PRUint32)NS_PTR_TO_INT32(mListeners[index]); + if(id == aClientID) { + mListeners.RemoveAt(index); + status = NS_OK; + break; + } + } + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, + mID, + TM_DETACH_REPLY, + status, + nsnull, + 0))) { + // send the reply + mTM->SendTransaction(aClientID, &trans); + } + + // if we've removed all the listeners, remove the queue. + if (mListeners.Size() == 0) + return TM_SUCCESS_DELETE_QUEUE; + return status; +} + +void +tmQueue::FlushQueue(PRUint32 aClientID) { + + if(!IsAttached(aClientID)) + return; + + PRUint32 size = mTransactions.Size(); + for (PRUint32 index = 0; index < size; index++) + if (mTransactions[index]) + delete (tmTransaction*)mTransactions[index]; + + mTransactions.Clear(); + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(aClientID, + mID, + TM_FLUSH_REPLY, + NS_OK, + nsnull, + 0))) { + mTM->SendTransaction(aClientID, &trans); + } +} + +PRInt32 +tmQueue::PostTransaction(tmTransaction *aTrans) { + + PRInt32 status = -1; + PRUint32 ownerID = aTrans->GetOwnerID(); + + // if we are attached, have the right queue and have successfully + // appended the transaction to the queue, send the transaction + // to all the listeners. + + if (IsAttached(ownerID) && aTrans->GetQueueID() == mID) + status = mTransactions.Append(aTrans); + + if (status >= 0) { + // send the transaction to all members of mListeners except the owner + PRUint32 size = mListeners.Size(); + PRUint32 id = 0; + for (PRUint32 index = 0; index < size; index++) { + id = (PRUint32)NS_PTR_TO_INT32(mListeners[index]); + if (ownerID != id) + mTM->SendTransaction(id, aTrans); + } + } + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(ownerID, + mID, + TM_POST_REPLY, + status, + nsnull, + 0))) { + // send the reply + mTM->SendTransaction(ownerID, &trans); + } + return status; +} + +PRBool +tmQueue::IsAttached(PRUint32 aClientID) { + // XXX could be an issue if the aClientID is 0 and there + // is a "hole" in the mListeners vector. - may NEED to store PRUint32*s + PRUint32 size = mListeners.Size(); + for (PRUint32 index = 0; index < size; index++) { + if (aClientID == (PRUint32)NS_PTR_TO_INT32(mListeners[index])) + return PR_TRUE; + } + return PR_FALSE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h new file mode 100644 index 00000000..1c680084 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmQueue.h @@ -0,0 +1,186 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmQueue_H_ +#define _tmQueue_H_ + +#include "tmUtils.h" +#include "tmVector.h" + +class tmClient; +class tmTransaction; +class tmTransactionManager; + +/** + * This class isn't so much a queue as it is storage for transactions. It + * is set up to recieve and store transactions in a growing collection + * (using tmVectors). Different messages can be recieved from the + * Transaction Manager(TM) the queue belongs to which can add and remove + * listeners, empty the queue (flush), and add messages to the queue. + * + * See the documentation in tmTransactionService.h for details on the + * messages you can send to and recieve from the queues in the TM + */ +class tmQueue +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor & Destructor + + /** + * Set the internal state to default values. Init() must be called + * after construction to allocate the storage and set the name and ID. + */ + tmQueue(): mID(0), mName(nsnull), mTM(nsnull) { } + + /** + * Reclaim the memory allocated in Init(). Destroys the transactions in + * the transaction storage and the ids in the listener storage + */ + virtual ~tmQueue(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Initialize internal storage vectors and set the name of the queue + * and the pointer to the TM container. + * + * @returns NS_OK if everything succeeds + * @returns -1 if initialization fails + */ + PRInt32 Init(const char* aName, PRUint32 aID, tmTransactionManager *aTM); + + // Queue Operations + + /** + * Adds the clientID to the list of queue listeners. A reply is created + * and sent to the client. The reply contains both the name of the + * queue and the id, so the client can match the id to the name and + * then use the id in all further communications to the queue. All + * current transactions in the queue are then sent to the client. + * + * If the client was already attached the reply is sent, but not the + * outstanding transactions, the assumption being made that all + * transactions have already been sent to the client. + * + * The reply is sent for all cases, with the return value in the status + * field. + * + * @returns >= 0 if the client was attached successfully + * @returns -1 if the client was not attached + * @returns -2 if the client was already attached + */ + PRInt32 AttachClient(PRUint32 aClientID); + + /** + * Removes the client from the list of queue listeners. A reply is created + * and sent to the client to indicate the success of the removal. + * + * The reply is sent for all cases, with the status field set to either + * -1 or NS_OK. + * + * @returns NS_OK on success + * @returns -1 if client is not attached to this queue + * @returns TM_SUCCESS_DELETE_QUEUE if there are no more listeners, + * instructing the Transaction Mangaer to delete the queue. + */ + PRInt32 DetachClient(PRUint32 aClientID); + + /** + * Removes all the transactions being held in the queue. + * A reply is created and sent to the client to indicate the + * completion of the operation. + */ + void FlushQueue(PRUint32 aClientID); + + /** + * Places the transaction passed in on the queue. Takes ownership of the + * transaction, deletes it in the destructor. A reply is created and + * sent to the client to indicate the success of the posting of the + * transaction. + * + * The reply is sent for all cases, with the status field containing the + * return value. + * + * @returns >= 0 if the message was posted properly. + * @returns -1 if the client posting is not attached to this queue, + * if the transaction has been posted to the wrong queue or + * if an error occured when trying to add the post to the + * internal storage. + */ + PRInt32 PostTransaction(tmTransaction *aTrans); + + // Accessors + + /** + * @returns the ID of the queue + */ + PRUint32 GetID() const { return mID; } + + /** + * @returns the name of the queue + */ + const char* GetName() const { return mName; } + +protected: + + /** + * Helper method to determine if the client has already attached. + * + * @returns PR_TRUE if the client is attached to the queue. + * @returns PR_FALSE if the client is not attached to the queue. + */ + PRBool IsAttached(PRUint32 aClientID); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + // storage + tmVector mTransactions; // transactions that have been posted + tmVector mListeners; // programs listening to this queue + + // bookkeeping + PRUint32 mID; // a number linked to the name in the mTM + char *mName; // format: [namespace][domainname(ie prefs)] + tmTransactionManager *mTM; // the container that holds the queue + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp new file mode 100644 index 00000000..9d6cc98e --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.cpp @@ -0,0 +1,162 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "plstr.h" +#include <stdlib.h> +#include "tmQueue.h" +#include "tmTransactionManager.h" +#include "tmTransaction.h" +#include "tmUtils.h" + +/////////////////////////////////////////////////////////////////////////////// +// Constructors & Destructor & Initializer + +tmTransactionManager::~tmTransactionManager() { + + PRUint32 size = mQueues.Size(); + tmQueue *queue = nsnull; + for (PRUint32 index = 0; index < size; index++) { + queue = (tmQueue *)mQueues[index]; + if (queue) { + delete queue; + } + } +} + +PRInt32 +tmTransactionManager::Init() { + return mQueues.Init(); +} + +/////////////////////////////////////////////////////////////////////////////// +// public transaction module methods + +void +tmTransactionManager::HandleTransaction(tmTransaction *aTrans) { + + PRUint32 action = aTrans->GetAction(); + PRUint32 ownerID = aTrans->GetOwnerID(); + tmQueue *queue = nsnull; + + // get the right queue -- attaches do it differently + if (action == TM_ATTACH) { + const char *name = (char*) aTrans->GetMessage(); // is qName for Attaches + queue = GetQueue(name); + if (!queue) { + PRInt32 index = AddQueue(name); + if (index >= 0) + queue = GetQueue(index); // GetQueue may return nsnull + } + } + else // all other trans should have a valid queue ID already + queue = GetQueue(aTrans->GetQueueID()); + + if (queue) { + // All possible actions should have a case, default is not valid + // delete trans when done with them, let the queue own the trans + // that are posted to them. + PRInt32 result = 0; + switch (action) { + case TM_ATTACH: + queue->AttachClient(ownerID); + break; + case TM_POST: + result = queue->PostTransaction(aTrans); + if (result >= 0) // post failed, aTrans cached in a tmQueue + return; + break; + case TM_FLUSH: + queue->FlushQueue(ownerID); + break; + case TM_DETACH: + if (queue->DetachClient(ownerID) == TM_SUCCESS_DELETE_QUEUE) { + // the last client has been removed, remove the queue + RemoveQueue(aTrans->GetQueueID()); // this _could_ be out of bounds + } + break; + default: + PR_NOT_REACHED("bad action in the transaction"); + } + } + delete aTrans; +} + +/////////////////////////////////////////////////////////////////////////////// +// Protected member functions + +// +// Queue Handling +// + +tmQueue* +tmTransactionManager::GetQueue(const char *aQueueName) { + + PRUint32 size = mQueues.Size(); + tmQueue *queue = nsnull; + for (PRUint32 index = 0; index < size; index++) { + queue = (tmQueue*) mQueues[index]; + if (queue && strcmp(queue->GetName(), aQueueName) == 0) + return queue; + } + return nsnull; +} + +// if successful the nsresult contains the index of the added queue +PRInt32 +tmTransactionManager::AddQueue(const char *aQueueName) { + + tmQueue* queue = new tmQueue(); + if (!queue) + return -1; + PRInt32 index = mQueues.Append(queue); + if (index < 0) + delete queue; + else + queue->Init(aQueueName, index, this); + return index; +} + +void +tmTransactionManager::RemoveQueue(PRUint32 aQueueID) { + PR_ASSERT(aQueueID <= mQueues.Size()); + + tmQueue *queue = (tmQueue*)mQueues[aQueueID]; + if (queue) { + mQueues.RemoveAt(aQueueID); + delete queue; + } +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h new file mode 100644 index 00000000..a03ba528 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/module/tmTransactionManager.h @@ -0,0 +1,147 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransactionManager_H_ +#define _tmTransactionManager_H_ + +#include "plhash.h" +#include "tmUtils.h" +#include "tmVector.h" +#include "tmIPCModule.h" + +// forward declarations +class tmQueue; +class tmClient; +class tmTransaction; + +/** + * This class manages the flow of messages from the IPC daemon (coming to + * it through the tmIPCModule) that ultimately come from a Transaction + * Service (TS) in a mozilla based client somewhere. The message is + * delivered to the proper queue, where it is dealt with. + * + * New queues get created here as clients request them. + */ +class tmTransactionManager +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor(s) & Destructor & Initializer + + /** + * reclaim the memory allcoated during initialization + */ + virtual ~tmTransactionManager(); + + /** + * Set up the storage of the queues - initialize the vector + * + * @returns NS_OK if successful + * @returns -1 if initialization fails + */ + PRInt32 Init(); + + //////////////////////////////////////////////////////////////////////////// + // Public Member Functions + + /** + * Called from the tmIPCModule. Decide where to send the message and + * dispatch it. + */ + void HandleTransaction(tmTransaction *aTrans); + + /** + * Called by the queues when they need to get a message back out to a + * client. + */ + void SendTransaction(PRUint32 aDestClientIPCID, tmTransaction *aTrans) { + PR_ASSERT(aTrans); + tmIPCModule::SendMsg(aDestClientIPCID, aTrans); + } + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Functions + + // Queue management + + /** + * @returns the queue indexed by the ID passed in, which could be nsnull + */ + tmQueue* GetQueue(PRUint32 aQueueID) { + return (tmQueue*) mQueues[aQueueID]; + } + + /** + * @returns the queue with the name passed in + * @returns nsnull if there is no queue with that name + */ + tmQueue* GetQueue(const char *aQueueName); + + /** + * If all is successful a new queue with the name provided will be created, + * and added to the collection of queues. It will be initialized and ready + * to have transactions added. + * + * This doesn't check for the existance of a queue with this name. IF + * there is already a queue with this name then you will + * get that when using GetQueue(qName) and never get the new queue + * created here. A call to GetQueue(qID) will be able to get at the new + * queue, however you had better cache the ID. + * + * @returns -1 if the queue can't be created, or is not added + * @returns >= 0 if the queue was added successfully + */ + PRInt32 AddQueue(const char *aQueueType); + + /** + */ + void RemoveQueue(PRUint32 aQueueID); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + tmVector mQueues; + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in new file mode 100644 index 00000000..65b126e9 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/Makefile.in @@ -0,0 +1,53 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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_transmngr + +XPIDLSRCS = \ + ipcITransactionService.idl \ + ipcITransactionObserver.idl \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl new file mode 100644 index 00000000..c7df965d --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionObserver.idl @@ -0,0 +1,100 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "nsISupports.idl" + +[scriptable, uuid(656c0a6a-5cb3-45ec-8cb6-e7678897f937)] +interface ipcITransactionObserver : nsISupports +{ + /** + * This gets called when a Transaction has been sent from the + * TransactionManager. If the data passed in needs to be stored + * for longer than the life of the method the observer needs + * to make a copy. + * + * @param aQueueID + * The queue from which the transaction originated + * + * @param aData + * The data to be sent. + * + * @param aDataLen + * The length of the data argument + */ + void onTransactionAvailable(in unsigned long aQueueID, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); + + /** + * Called after an application sends an Attach message to the + * Transaction Manager. + * + * @param aQueueID + * The client has been attached to the queue with this ID + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onAttachReply(in unsigned long aQueueID, in unsigned long aStatus); + + /** + * Called after an application sends a Detach message. Indicates + * to the client that no more messages will be coming from the + * the TM to this client. Also, no messages posted from this + * client to the indicated queue will be accepted. + * + * @param aQueueID + * The client has been detached from the queue with this ID + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onDetachReply(in unsigned long aQueueID, in unsigned long aStatus); + + /** + * The reply from the TM indicating all messages have been removed + * from the queue indicated. + * + * @param aQueueID + * The queue that has been flushed. + * + * @param aStatus + * The status of the operation, as defined in tmUtils.h + */ + void onFlushReply(in unsigned long aQueueID, in unsigned long aStatus); +}; diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl new file mode 100644 index 00000000..0bcbc453 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl @@ -0,0 +1,239 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +// from tmTransactionManager.h +// +// XXX documentation needs work +////////////////////////////////////////////////////////////////////////////// +// Overview of TransactionManager IPC Module +// +// Classes: +// tmIPCModule - From the tmTransactionManager's point of view, this +// is a proxy for the IPC daemon itself. The reverse is true +// from the daemon's point of view. This is an interface for the +// Transaction system to work with the IPC daemon as its transport +// layer. +// tmTransactionManager (TM) - Manages the different queues. Maintains +// the queues neccessary for different clients. Receives messages +// from the tmIPCModule and passes message to the IPC daemon through +// the tmIPCModule. +// tmQueue - this class manages the transactions for the different areas +// of the profiles being shared. Broken down by functional area there +// will be a queue for prefs, cookies etc, but not for profileA and +// profileB, and not for pref_delete, pref_create, pref_change etc... +// tmTransaction - the actual transaction being shared with the different +// tmClients. It contains the type of transaction, which will equate with +// a type of queue in existance, the owner of the transaction (an IPC daemon ID) +// and the actual text message to be shared. +// +////////////////////////////////////////////////////////////////////////////// + +/// XXX some docs that need to be put somewhere: +// +// from tmqueue.cpp +// Docs - note that the status of the TM_ATTACH_REPLY is only for checking +// for TM_ERROR_FAILURE. Other numbers have no importance +// success of the status means the NS_ERROR_GET_CODE(status) will +// yield the index of the listener. +// +// move to documentation page - from tmqueue.h +// +// a queue is specific to profile +// +// UUID going out from the module is a handler in the client +// (will go to the XPCOM service impling that UUID) +// -- does it make sense to have different UUIDs for cookies/prefs/etc +// + +#include "nsISupports.idl" + +interface ipcITransactionObserver; + +[scriptable, uuid(15561efb-8c58-4a47-813a-fa91cf730895)] +interface ipcITransactionService : nsISupports +{ + /** + * Connects the application to the transaction manager, defines the + * namespace and initializes internal storage + * + * @param aNamespace + * A string defining the scope of the transaction domains. It is + * used internally to seperate process listening to the same domain + * (ie. preferences) but for two different namespaces (ie. profile1 vs + * profile2). + * + * @returns NS_OK if all memory allocated properly and the IPC service was + * reached and attached to successfully. + * + * @returns an NS_ERROR_<foo> code specific to the failure otherwise + */ + void init(in ACString aNamespace); + + /** + * Links the observer passed in with the domain specified. This will allow + * the observer to post transactions dealing with this domain as well as + * receive transactions posted by other applications observing this + * domain. + * + * Return codes for this method confer information about the success of + * this call, not of the actual attaching of the observer to the domain. + * (except the TM_ERROR code - which means the observer can not attach) + * If the attach is successful the observer will have its OnAttachReply + * method called before this method returns. + * + * Note: This call is synchronous and will not return until the call to + * OnAttachReply is made. + * + * @param aDomainName + * the name of the domain, in the current namespace, to listen for + * transactions from. i.e. cookies + * + * @param aObserver + * this will be used to notify the application when transactions + * and messages come in. + * + * @param aLockingCall + * Have the Transaction Sevice acquire a lock based on the domain + * before attaching. This should be used when persistant storage + * is being used to prevent data corruption. + * + * @returns NS_OK if the attach message was sent to the Transaction Manager. + * + * @returns an NS_ERROR_<foo> code specific to the failure otherwise + * + * @returns TM_ERROR_QUEUE_EXISTS if the queue already exists which means + * someone has already attached to it. + */ + void attach(in ACString aDomainName, + in ipcITransactionObserver aObserver, + in PRBool aLockingCall); + + /** + * Sends a detach message to the Transaction Manager to unlink the observer + * associated with the domain passed in. + * + * As in attach, return codes do not indicate success of detachment. The + * observer will have it's OnDetach method called if it is successfully + * detached. + * + * Note: This call is an asynchronous call. + * + * @param aDomainName + * the domain, in the current namespace, from which the client + * should be removed. + * + * @returns NS_OK if the detach message is sent to the Transaction Manager + * + * @returns NS_ERROR_FAILURE is something goes wrong + * + * @returns NS_ERRROR_UNEXPECTD if the domain does not have an observer + * attached + */ + void detach(in ACString aDomainName); + + /** + * Sends a flush message to the Transaction Manager to remove all + * transactions for the domain. After this call there will be no + * transactions in the Transaction Manager for the namespace/domain + * pairing. It is up to the application to coordinate the flushing + * of the Transaction Manager with the writing of data to files, + * if needed. + * + * Note: This call is synchronous and will not return until the call to + * OnFlushReply is made. + * + * @param aDomainName + * The domain, in the current namespace, to flush. + * + * @param aLockingCall + * Have the Transaction Sevice acquire a lock based on the domain + * before flushing. This should be used when persistant storage + * is being used to prevent data corruption. + * + * @returns NS_OK if the flush message is sent to the Transaction Manager + * + * @returns NS_ERROR_FAILURE is something goes wrong + * + * @returns NS_ERRROR_UNEXPECTD if the domain does not have an observer + * attached + */ + void flush(in ACString aDomainName, in PRBool aLockingCall); + + /** + * Send the data to the Transaction Manager to be broadcast to any + * applications that have registered as observers of this particular + * namespace/domain pairing. + * + * If this domain is not being observed (attach has not been called for + * this domain) the message is queued until the attach is made and then + * the message is sent to the Transaction Manager with the proper domain + * information. + * + * XXXjg - this may not be neccessary with the synch attach call. + * + * Note: This call is an asynchronous call. + * + * @param aDomainName + * the domain, in the current namespace, to which the data will be + * sent. + * + * @param aData + * The actual data to be sent. + * + * @param aDataLen + * The length of the data argument + */ + void postTransaction(in ACString aDomainName, + [array, const, size_is(aDataLen)] + in octet aData, + in unsigned long aDataLen); +}; + +%{C++ +// singleton implementing ipcITransactionService +#define IPC_TRANSACTIONSERVICE_CLASSNAME \ + "tmTransactionService" +#define IPC_TRANSACTIONSERVICE_CONTRACTID \ + "@mozilla.org/ipc/transaction-service;1" +#define IPC_TRANSACTIONSERVICE_CID \ +{ /* 1403adf4-94d1-4c67-a8ae-d9f86972d378 */ \ + 0x1403adf4, \ + 0x94d1, \ + 0x4c67, \ + {0xa8, 0xae, 0xd9, 0xf8, 0x69, 0x72, 0xd3, 0x78} \ +} +%} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore new file mode 100644 index 00000000..7726b497 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/.cvsignore @@ -0,0 +1,4 @@ +Makefile +tmTransactionService.obj +transmgr_s.lib +transmgr_s.pdb
\ No newline at end of file diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in new file mode 100644 index 00000000..6510e489 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/Makefile.in @@ -0,0 +1,64 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = transmgr_s +FORCE_STATIC_LIB = 1 +MODULE_NAME = ipcd + +REQUIRES = string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmTransactionService.cpp \ + $(NULL) + +include $(topsrcdir)/config/config.mk + +LOCAL_INCLUDES = \ + -I$(srcdir)/../common \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp new file mode 100644 index 00000000..e53d6aa7 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.cpp @@ -0,0 +1,504 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#include "nsCOMPtr.h" +#include "nsIServiceManager.h" +#include "nsReadableUtils.h" +#include "plstr.h" +#include "ipcITransactionObserver.h" +#include "tmTransaction.h" +#include "tmTransactionService.h" +#include "tmUtils.h" + +static const nsID kTransModuleID = TRANSACTION_MODULE_ID; + +struct tm_waiting_msg { + tmTransaction trans; // a transaction waiting to be sent to a queue + char* domainName; // the short queue name + + ~tm_waiting_msg(); +}; + +tm_waiting_msg::~tm_waiting_msg() { + if (domainName) + PL_strfree(domainName); +} + +struct tm_queue_mapping { + PRInt32 queueID; // the ID in the TM + char* domainName; // used by the consumers of this service + char* joinedQueueName; // used by the service -- namespace + domain name + + ~tm_queue_mapping(); +}; + +tm_queue_mapping::~tm_queue_mapping() { + if (domainName) + PL_strfree(domainName); + if (joinedQueueName) + PL_strfree(joinedQueueName); +} + +////////////////////////////////////////////////////////////////////////////// +// Constructor and Destructor + +tmTransactionService::~tmTransactionService() { + + // just destroy this, it contains 2 pointers it doesn't own. + if (mObservers) + PL_HashTableDestroy(mObservers); + + PRUint32 index = 0; + PRUint32 size = mWaitingMessages.Size(); + tm_waiting_msg *msg = nsnull; + for ( ; index < size; index ++) { + msg = (tm_waiting_msg*) mWaitingMessages[index]; + delete msg; + } + + size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap) + delete qmap; + } +} + +////////////////////////////////////////////////////////////////////////////// +// ISupports + +NS_IMPL_ISUPPORTS2(tmTransactionService, + ipcITransactionService, + ipcIMessageObserver) + +////////////////////////////////////////////////////////////////////////////// +// ipcITransactionService + +NS_IMETHODIMP +tmTransactionService::Init(const nsACString & aNamespace) { + + nsresult rv; + + rv = IPC_DefineTarget(kTransModuleID, this, PR_TRUE); + if (NS_FAILED(rv)) + return rv; + + // get the lock service + lockService = do_GetService("@mozilla.org/ipc/lock-service;1"); + if (!lockService) + return NS_ERROR_FAILURE; + + // create some internal storage + mObservers = PL_NewHashTable(20, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, 0, 0); + if (!mObservers) + return NS_ERROR_FAILURE; + + // init some internal storage + mQueueMaps.Init(); + mWaitingMessages.Init(); + + // store the namespace + mNamespace.Assign(aNamespace); + return NS_OK; +} + +NS_IMETHODIMP +tmTransactionService::Attach(const nsACString & aDomainName, + ipcITransactionObserver *aObserver, + PRBool aLockingCall) { + + // if the queue already exists, then someone else is attached to it. must + // return an error here. Only one module attached to a queue per app. + if (GetQueueID(aDomainName) != TM_NO_ID) + return TM_ERROR_QUEUE_EXISTS; + + // create the full queue name: namespace + queue + nsCString jQName; + jQName.Assign(mNamespace); + jQName.Append(aDomainName); + + // this char* has two homes, make sure it gets PL_free()ed properly + char* joinedQueueName = ToNewCString(jQName); + if (!joinedQueueName) + return NS_ERROR_OUT_OF_MEMORY; + + // link the observer to the joinedqueuename. home #1 for joinedQueueName + // these currently don't get removed until the destructor on this is called. + PL_HashTableAdd(mObservers, joinedQueueName, aObserver); + + // store the domainName and JoinedQueueName, create a place to store the ID + tm_queue_mapping *qm = new tm_queue_mapping(); + if (!qm) + return NS_ERROR_OUT_OF_MEMORY; + qm->queueID = TM_NO_ID; // initially no ID for the queue + qm->joinedQueueName = joinedQueueName; // home #2, owner of joinedQueueName + qm->domainName = ToNewCString(aDomainName); + if (!qm->domainName) { + PL_HashTableRemove(mObservers, joinedQueueName); + delete qm; + return NS_ERROR_OUT_OF_MEMORY; + } + mQueueMaps.Append(qm); + + nsresult rv = NS_ERROR_FAILURE; + tmTransaction trans; + + // acquire a lock if neccessary + if (aLockingCall) + lockService->AcquireLock(joinedQueueName, PR_TRUE); + // XXX need to handle lock failures + + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + TM_NO_ID, // qID gets returned to us + TM_ATTACH, // action + NS_OK, // default status + (PRUint8 *)joinedQueueName, // qName gets copied + PL_strlen(joinedQueueName)+1))) { // message length + // send the attach msg + SendMessage(&trans, PR_TRUE); // synchronous + rv = NS_OK; + } + + // drop the lock if neccessary + if (aLockingCall) + lockService->ReleaseLock(joinedQueueName); + + return rv; +} + +// actual removal of the observer takes place when we get the detach reply +NS_IMETHODIMP +tmTransactionService::Detach(const nsACString & aDomainName) { + + // asynchronous detach + return SendDetachOrFlush(GetQueueID(aDomainName), TM_DETACH, PR_FALSE); + +} + +NS_IMETHODIMP +tmTransactionService::Flush(const nsACString & aDomainName, + PRBool aLockingCall) { + // acquire a lock if neccessary + if (aLockingCall) + lockService->AcquireLock(GetJoinedQueueName(aDomainName), PR_TRUE); + + // synchronous flush + nsresult rv = SendDetachOrFlush(GetQueueID(aDomainName), TM_FLUSH, PR_TRUE); + + // drop the lock if neccessary + if (aLockingCall) + lockService->ReleaseLock(GetJoinedQueueName(aDomainName)); + + return rv; + +} + +NS_IMETHODIMP +tmTransactionService::PostTransaction(const nsACString & aDomainName, + const PRUint8 *aData, + PRUint32 aDataLen) { + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + GetQueueID(aDomainName), // qID returned to us + TM_POST, // action + NS_OK, // default status + aData, // message data + aDataLen))) { // message length + if (trans.GetQueueID() == TM_NO_ID) { + // stack it and pack it + tm_waiting_msg *msg = new tm_waiting_msg(); + if (!msg) + return NS_ERROR_OUT_OF_MEMORY; + msg->trans = trans; + msg->domainName = ToNewCString(aDomainName); + if (!msg->domainName) { + delete msg; + return NS_ERROR_OUT_OF_MEMORY; + } + mWaitingMessages.Append(msg); + } + else { + // send it + SendMessage(&trans, PR_FALSE); + } + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +////////////////////////////////////////////////////////////////////////////// +// ipcIMessageObserver + +NS_IMETHODIMP +tmTransactionService::OnMessageAvailable(const PRUint32 aSenderID, + const nsID & aTarget, + const PRUint8 *aData, + PRUint32 aDataLength) { + + nsresult rv = NS_ERROR_OUT_OF_MEMORY; // prime the return value + + tmTransaction *trans = new tmTransaction(); + if (trans) { + rv = trans->Init(0, // no IPC client ID + TM_INVALID_ID, // in aData + TM_INVALID_ID, // in aData + TM_INVALID_ID, // in aData + aData, // message data + aDataLength); // message length + + if (NS_SUCCEEDED(rv)) { + switch(trans->GetAction()) { + case TM_ATTACH_REPLY: + OnAttachReply(trans); + break; + case TM_POST_REPLY: + // OnPostReply() would be called here + // isn't neccessary at the current time 2/19/03 + break; + case TM_DETACH_REPLY: + OnDetachReply(trans); + break; + case TM_FLUSH_REPLY: + OnFlushReply(trans); + break; + case TM_POST: + OnPost(trans); + break; + default: // error, should not happen + NS_NOTREACHED("Recieved a TM reply outside of mapped messages"); + break; + } + } + delete trans; + } + return rv; +} + +////////////////////////////////////////////////////////////////////////////// +// Protected Member Functions + +void +tmTransactionService::SendMessage(tmTransaction *aTrans, PRBool aSync) { + + NS_ASSERTION(aTrans, "tmTransactionService::SendMessage called with null transaction"); + + IPC_SendMessage(0, kTransModuleID, + aTrans->GetRawMessage(), + aTrans->GetRawMessageLength()); + if (aSync) + IPC_WaitMessage(0, kTransModuleID, nsnull, nsnull, PR_INTERVAL_NO_TIMEOUT); +} + +void +tmTransactionService::OnAttachReply(tmTransaction *aTrans) { + + // if we attached, store the queue's ID + if (aTrans->GetStatus() >= 0) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && + PL_strcmp(qmap->joinedQueueName, (char*) aTrans->GetMessage()) == 0) { + + // set the ID in the mapping + qmap->queueID = aTrans->GetQueueID(); + // send any stored messges to the queue + DispatchStoredMessages(qmap); + } + } + } + + // notify the observer we have attached (or didn't) + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + (char*)aTrans->GetMessage()); + if (observer) + observer->OnAttachReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnDetachReply(tmTransaction *aTrans) { + + tm_queue_mapping *qmap = GetQueueMap(aTrans->GetQueueID()); + + // get the observer before we release the hashtable entry + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + qmap->joinedQueueName); + + // if it was removed, clean up + if (aTrans->GetStatus() >= 0) { + + // remove the link between observer and queue + PL_HashTableRemove(mObservers, qmap->joinedQueueName); + + // remove the mapping of queue names and id + mQueueMaps.Remove(qmap); + delete qmap; + } + + + // notify the observer -- could be didn't detach + if (observer) + observer->OnDetachReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnFlushReply(tmTransaction *aTrans) { + + ipcITransactionObserver *observer = + (ipcITransactionObserver *)PL_HashTableLookup(mObservers, + GetJoinedQueueName(aTrans->GetQueueID())); + if (observer) + observer->OnFlushReply(aTrans->GetQueueID(), aTrans->GetStatus()); +} + +void +tmTransactionService::OnPost(tmTransaction *aTrans) { + + ipcITransactionObserver *observer = + (ipcITransactionObserver*) PL_HashTableLookup(mObservers, + GetJoinedQueueName(aTrans->GetQueueID())); + if (observer) + observer->OnTransactionAvailable(aTrans->GetQueueID(), + aTrans->GetMessage(), + aTrans->GetMessageLength()); +} + +void +tmTransactionService::DispatchStoredMessages(tm_queue_mapping *aQMapping) { + + PRUint32 size = mWaitingMessages.Size(); + tm_waiting_msg *msg = nsnull; + for (PRUint32 index = 0; index < size; index ++) { + msg = (tm_waiting_msg*) mWaitingMessages[index]; + // if the message is waiting on the queue passed in + if (msg && strcmp(aQMapping->domainName, msg->domainName) == 0) { + + // found a match, send it and remove + msg->trans.SetQueueID(aQMapping->queueID); + SendMessage(&(msg->trans), PR_FALSE); + + // clean up + mWaitingMessages.Remove(msg); + delete msg; + } + } +} + +// searches against the short queue name +PRInt32 +tmTransactionService::GetQueueID(const nsACString & aDomainName) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && aDomainName.Equals(qmap->domainName)) + return qmap->queueID; + } + return TM_NO_ID; +} + +char* +tmTransactionService::GetJoinedQueueName(PRUint32 aQueueID) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && qmap->queueID == aQueueID) + return qmap->joinedQueueName; + } + return nsnull; +} + +char* +tmTransactionService::GetJoinedQueueName(const nsACString & aDomainName) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && aDomainName.Equals(qmap->domainName)) + return qmap->joinedQueueName; + } + return nsnull; +} + +tm_queue_mapping* +tmTransactionService::GetQueueMap(PRUint32 aQueueID) { + + PRUint32 size = mQueueMaps.Size(); + tm_queue_mapping *qmap = nsnull; + for (PRUint32 index = 0; index < size; index++) { + qmap = (tm_queue_mapping*) mQueueMaps[index]; + if (qmap && qmap->queueID == aQueueID) + return qmap; + } + return nsnull; +} + +nsresult +tmTransactionService::SendDetachOrFlush(PRUint32 aQueueID, + PRUint32 aAction, + PRBool aSync) { + + // if the queue isn't attached to, just return + if (aQueueID == TM_NO_ID) + return NS_ERROR_UNEXPECTED; + + tmTransaction trans; + if (NS_SUCCEEDED(trans.Init(0, // no IPC client + aQueueID, // qID to detach from + aAction, // action + NS_OK, // default status + nsnull, // no message + 0))) { // no message + // send it + SendMessage(&trans, aSync); + return NS_OK; + } + return NS_ERROR_FAILURE; +} diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h new file mode 100644 index 00000000..cd9310fe --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/src/tmTransactionService.h @@ -0,0 +1,196 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +#ifndef _tmTransactionService_H_ +#define _tmTransactionService_H_ + +#include "ipcdclient.h" +#include "ipcILockService.h" +#include "ipcIMessageObserver.h" +#include "ipcITransactionService.h" +#include "nsString.h" +#include "nsVoidArray.h" +#include "plhash.h" +#include "tmTransaction.h" +#include "tmVector.h" + +struct tm_queue_mapping; + +/** + * The tmTransactionService shares packets of information + * (transactions) with other Gecko based applications interested in the same + * namespace and domain. An application registers with the Transaction Service + * for a particular namespace and domain and then can post transactions to the + * service and receive transactions from the service. + * + * For applications using the Transaction Service to share changes in state that + * get reflected in files on disk there are certain pattersn to follow to ensure + * data loss does not occur. + * + * Startup: XXX docs needed + * + * Shutdown/writing to disk: XXX docs needed + * + * + */ +class tmTransactionService : public ipcITransactionService, + public ipcIMessageObserver +{ + +public: + + //////////////////////////////////////////////////////////////////////////// + // Constructor & Destructor + tmTransactionService() : mObservers(0) {}; + + /** + * Reclaim all the memory allocated: PL_hashtable, tmVectors + */ + virtual ~tmTransactionService(); + + //////////////////////////////////////////////////////////////////////////// + // Interface Declarations + + // for API docs, see the respective *.idl files + NS_DECL_ISUPPORTS + NS_DECL_IPCITRANSACTIONSERVICE + NS_DECL_IPCIMESSAGEOBSERVER + +protected: + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Functions + + /** + * Pulls the raw message out of the transaction and sends it to the IPC + * service to be delivered to the TM. + * + * @param aTrans + * The transaction to send to the TM + * + * @param aSync + * If TRUE, calling thread will be blocked until a reply is + * received. + */ + void SendMessage(tmTransaction *aTrans, PRBool aSync); + + // handlers for reply messages from TransactionManager + + /** + * Pulls the queueID out of the ATTACH_REPLY message and stores it in the + * proper tm_queue_mapping object. Calls DispatchStoredMessages() to make + * sure we send any messages that have been waiting on the ATTACH_REPLY. + * Also calls the OnAttachReply() method for the observer of the queue. + */ + void OnAttachReply(tmTransaction *aTrans); + + /** + * Removes the tm_queue_mapping object and calls the OnDetachReply() method + * on the observer of the queue detached. + */ + void OnDetachReply(tmTransaction *aTrans); + + /** + * Calls the OnFlushReply method of the observer of the queue. + */ + void OnFlushReply(tmTransaction *aTrans); + + /** + * Calls the OnPost method of the observer of the queue. + */ + void OnPost(tmTransaction *aTrans); + + // other helper functions + + /** + * Cycle through the collection of transactions waiting to go out and + * send any that are waiting on an ATTACH_REPLY from the queue + * specified by the tm_queue_mapping passed in. + */ + void DispatchStoredMessages(tm_queue_mapping *aQMapping); + + // helper methods for accessing the void arrays + + /** + * @returns the ID corresponding to the domain name passed in + * @returns TM_NO_ID if the name is not found. + */ + PRInt32 GetQueueID(const nsACString & aDomainName); + + /** + * @returns the joined queue name - namespace + domain + * (prefs, cookies etc) corresponding to the ID passed in. + * @returns nsnull if the ID is not found. + */ + char* GetJoinedQueueName(PRUint32 aQueueID); + + /** + * @returns the joined queue name - namespace + domain + * (prefs, cookies etc) corresponding to the ID passed in. + * @returns nsnull if the ID is not found. + */ + char* GetJoinedQueueName(const nsACString & aDomainName); + + /** + * @returns the tm_queue_mapping object that contains the ID passed in. + * @returns nsnull if the ID is not found. + */ + tm_queue_mapping* GetQueueMap(PRUint32 aQueueID); + + /** + * Helper method for Detach and Flush requests. + */ + nsresult SendDetachOrFlush(PRUint32 aQueueID, + PRUint32 aAction, + PRBool aSync); + + //////////////////////////////////////////////////////////////////////////// + // Protected Member Variables + + nsCString mNamespace; // limit domains to the namespace + PLHashTable *mObservers; // maps qName -> ipcITransactionObserver + + tmVector mQueueMaps; // queue - name - domain mappings + tmVector mWaitingMessages; // messages sent before ATTACH_REPLY + + nsCOMPtr<ipcILockService> lockService; // cache the lock service + +private: + +}; + +#endif diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore new file mode 100644 index 00000000..e3c610be --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/.cvsignore @@ -0,0 +1,6 @@ +Makefile +tmModuleTest.exe +tmModuleTest.ilk +tmModuleTest.pdb +tmModuleTest.obj +tmModuleTest diff --git a/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in new file mode 100644 index 00000000..385751a3 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/Makefile.in @@ -0,0 +1,68 @@ +# ***** 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 Transaction Manager. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corp. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# John Gaunt <jgaunt@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 = tm_test + +REQUIRES = ipcd \ + nspr \ + string \ + xpcom \ + $(NULL) + +CPPSRCS = \ + tmModuleTest.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/transmngr/test/tmModuleTest.cpp b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/tmModuleTest.cpp new file mode 100644 index 00000000..5d1dccd4 --- /dev/null +++ b/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/test/tmModuleTest.cpp @@ -0,0 +1,323 @@ +/* ***** 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 Transaction Manager. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * John Gaunt <jgaunt@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 ***** */ + +// transaction manager includes +#include "ipcITransactionService.h" +#include "ipcITransactionObserver.h" + +// ipc daemon includes +#include "ipcIService.h" + +// core & xpcom ns includes +#include "nsDebug.h" +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsIComponentRegistrar.h" +#include "nsString.h" + +// nspr includes +#include "prmem.h" +#include "plgetopt.h" +#include "nspr.h" +#include "prlog.h" + +////////////////////////////////////////////////////////////////////////////// +// Testing/Debug/Logging BEGIN + +const int NameSize = 1024; + +/* command line options */ +PRIntn optDebug = 0; +char optMode = 's'; +char *profileName = new char[NameSize]; +char *queueName = new char[NameSize]; + +char *data = new char[NameSize]; +PRUint32 dataLen = 10; // includes the null terminator for "test data" + +// Testing/Debug/Logging END +////////////////////////////////////////////////////////////////////////////// + +#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 PRInt32 gMsgCount = 0; +static ipcIService *gIpcServ = nsnull; +static ipcITransactionService *gTransServ = nsnull; + +//----------------------------------------------------------------------------- + +class myTransactionObserver : public ipcITransactionObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IPCITRANSACTIONOBSERVER + + myTransactionObserver() { } +}; + +NS_IMPL_ISUPPORTS1(myTransactionObserver, ipcITransactionObserver) + +NS_IMETHODIMP myTransactionObserver::OnTransactionAvailable(PRUint32 aQueueID, const PRUint8 *aData, PRUint32 aDataLen) +{ + printf("tmModuleTest: myTransactionObserver::OnTransactionAvailable [%s]\n", aData); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnAttachReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnAttachReply [%d]\n", aStatus); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnDetachReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnDetachReply [%d]\n", aStatus); + return NS_OK; +} + +NS_IMETHODIMP myTransactionObserver::OnFlushReply(PRUint32 aQueueID, PRUint32 aStatus) +{ + printf("tmModuleTest: myTransactionObserver::OnFlushReply [%d]\n", aStatus); + return NS_OK; +} + + +//----------------------------------------------------------------------------- + +int main(PRInt32 argc, char *argv[]) +{ + nsresult rv; + + // default string values + strcpy(profileName, "defaultProfile"); + strcpy(queueName, "defaultQueue"); + strcpy(data, "test data"); + + { // scope the command line option gathering (needed for some reason) + + // Get command line options + PLOptStatus os; + PLOptState *opt = PL_CreateOptState(argc, argv, "bdfhlp:q:"); + + while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) + { + if (PL_OPT_BAD == os) continue; + switch (opt->option) + { + case 'b': /* broadcast a bunch of messages */ + printf("tmModuleTest: broadcaster\n"); + optMode = 'b'; + break; + case 'd': /* debug mode */ + printf("tmModuleTest: debugging baby\n"); + optDebug = 1; + break; + case 'f': /* broadcast and flush */ + printf("tmModuleTest: flusher\n"); + optMode = 'f'; + break; + case 'h': /* broadcast and detach */ + printf("tmModuleTest: hit-n-run\n"); + optMode = 'h'; + break; + case 'l': /* don't broadcast, just listen */ + printf("tmModuleTest: listener\n"); + optMode = 'l'; + break; + case 'p': /* set the profile name */ + strcpy(profileName, opt->value); + printf("tmModuleTest: profilename:%s\n",profileName); + break; + case 'q': /* set the queue name */ + strcpy(queueName, opt->value); + printf("tmModuleTest: queuename:%s\n",queueName); + break; + default: + printf("tmModuleTest: default\n"); + break; + } + } + PL_DestroyOptState(opt); + } // scope the command line option gathering (needed for some reason) + + { // scope the nsCOMPtrs + + printf("tmModuleTest: Starting xpcom\n"); + + // xpcom startup stuff + 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"); + + // Need to make sure the ipc system has been started + printf("tmModuleTest: getting ipc service\n"); + nsCOMPtr<ipcIService> ipcServ(do_GetService("@mozilla.org/ipc/service;1", &rv)); + RETURN_IF_FAILED(rv, "do_GetService(ipcServ)"); + NS_ADDREF(gIpcServ = ipcServ); + + // Get the transaction service + printf("tmModuleTest: getting transaction service\n"); + nsCOMPtr<ipcITransactionService> transServ(do_GetService("@mozilla.org/ipc/transaction-service;1", &rv)); + RETURN_IF_FAILED(rv, "do_GetService(transServ)"); + NS_ADDREF(gTransServ = transServ); + + // transaction specifc startup stuff, done for all cases + + nsCOMPtr<ipcITransactionObserver> observ = new myTransactionObserver(); + + // initialize the transaction service with a specific profile + gTransServ->Init(nsDependentCString(profileName)); + printf("tmModuleTest: using profileName [%s]\n", profileName); + + // attach to the queue in the transaction manager + gTransServ->Attach(nsDependentCString(queueName), observ, PR_TRUE); + printf("tmModuleTest: observing queue [%s]\n", queueName); + + + // run specific patterns based on the mode + int i = 0; // wasn't working inside the cases + switch (optMode) + { + case 's': + printf("tmModuleTest: start standard\n"); + // post a couple events + for (; i < 5 ; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end standard\n"); + break; + case 'b': + printf("tmModuleTest: start broadcast\n"); + // post a BUNCH of messages + for (; i < 50; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end broadcast\n"); + break; + case 'f': + printf("tmModuleTest: start flush\n"); + // post a couple events + for (; i < 5; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // flush the queue + gTransServ->Flush(nsDependentCString(queueName), PR_TRUE); + // post a couple events + for (i=0; i < 8; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + // detach + gTransServ->Detach(nsDependentCString(queueName)); + printf("tmModuleTest: end flush\n"); + break; + case 'h': + printf("tmModuleTest: start hit-n-run\n"); + // post a couple events + for (; i < 5; i++) { + gTransServ->PostTransaction(nsDependentCString(queueName), (PRUint8 *)data, dataLen); + } + // detach + gTransServ->Detach(nsDependentCString(queueName)); + printf("tmModuleTest: end hit-n-run\n"); + break; + case 'l': + printf("tmModuleTest: start listener\n"); + // listen for events + while (gKeepRunning) + gEventQ->ProcessPendingEvents(); + printf("tmModuleTest: end listener\n"); + break; + default : + printf("tmModuleTest: start & end default\n"); + break; + } + + // shutdown process + + NS_RELEASE(gTransServ); + NS_RELEASE(gIpcServ); + + printf("tmModuleTest: processing remaining events\n"); + + // process any remaining events + PLEvent *ev; + while (NS_SUCCEEDED(gEventQ->GetEvent(&ev)) && ev) + gEventQ->HandleEvent(ev); + + printf("tmModuleTest: done\n"); + } // this scopes the nsCOMPtrs + + // helps with shutdown on some cases + PR_Sleep(PR_SecondsToInterval(4)); + + // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM + rv = NS_ShutdownXPCOM(nsnull); + NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed"); + + return 0; +} |