Fixes a major bug that occurs when passing a remote object to a method call

on another remote object.  We now unwrap the local DConnectStub and send
the address of the remote object instead, which avoids the creation of an
unnecessary DConnectStub on the remote side.  Thanks to Frank Wiegerinck
for finding this bug.

This patch also makes it so that all of the IPC tests depend on no more
than XPCOM.  That way it is possible to build IPCDC and XPCOM standalone
without needing disable tests.

These changes do not affect the default build.
This commit is contained in:
darin%meer.net 2004-06-23 18:10:11 +00:00
Родитель fe8e4e2d2d
Коммит 7a96cb68e8
4 изменённых файлов: 217 добавлений и 109 удалений

Просмотреть файл

@ -71,6 +71,10 @@ static const nsID kDConnectTargetID = DCONNECT_IPC_TARGETID;
#define DCON_WAIT_TIMEOUT PR_INTERVAL_NO_TIMEOUT
// used elsewhere like nsAtomTable to safely represent the integral value
// of an address.
typedef unsigned long PtrBits;
//-----------------------------------------------------------------------------
//
@ -656,44 +660,6 @@ DeserializeResult(ipcMessageReader &reader, const nsXPTType &t, nsXPTCMiniVarian
return NS_OK;
}
static nsresult
SerializeInterfaceParam(ipcMessageWriter &writer,
PRUint32 peer, const nsID &iid,
const nsXPTType &type, const nsXPTCMiniVariant &v,
nsVoidArray &wrappers)
{
// 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.
nsCOMPtr<nsIInterfaceInfo> iinfo;
nsresult rv = gDConnect->GetInterfaceInfo(iid, getter_AddRefs(iinfo));
if (NS_FAILED(rv))
return rv;
DConnectInstance *wrapper = nsnull;
if (v.val.p)
{
wrapper = new DConnectInstance(peer, iinfo, (nsISupports *) v.val.p);
if (!wrapper)
return NS_ERROR_OUT_OF_MEMORY;
}
if (!wrappers.AppendElement(wrapper))
return NS_ERROR_OUT_OF_MEMORY;
rv = gDConnect->StoreInstance(wrapper);
if (NS_FAILED(rv))
return rv;
// send address of the instance wrapper
writer.PutBytes(&wrapper, sizeof(wrapper));
return NS_OK;
}
//-----------------------------------------------------------------------------
static PRUint32
@ -773,6 +739,15 @@ private:
//-----------------------------------------------------------------------------
#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
@ -798,6 +773,9 @@ public:
const nsXPTMethodInfo *aInfo,
nsXPTCMiniVariant *aParams);
DConAddr Instance() { return mInstance; }
PRUint32 PeerID() { return mPeerID; }
private:
NS_HIDDEN ~DConnectStub();
@ -844,6 +822,76 @@ CreateStub(const nsID &iid, PRUint32 peer, DConAddr instance, DConnectStub **res
return rv;
}
static nsresult
SerializeInterfaceParam(ipcMessageWriter &writer,
PRUint32 peer, const nsID &iid,
const nsXPTType &type, const nsXPTCMiniVariant &v,
nsVoidArray &wrappers)
{
// 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.
nsISupports *obj = (nsISupports *) v.val.p;
if (!obj)
{
// write null address
writer.PutBytes(&obj, sizeof(obj));
}
else
{
DConnectStub *stub = nsnull;
nsresult rv = obj->QueryInterface(kDConnectStubID, (void **) &stub);
if (NS_SUCCEEDED(rv) && (stub->PeerID() == peer))
{
void *p = stub->Instance();
writer.PutBytes(&p, sizeof(p));
}
else
{
// create instance wrapper
nsCOMPtr<nsIInterfaceInfo> iinfo;
rv = gDConnect->GetInterfaceInfo(iid, getter_AddRefs(iinfo));
if (NS_FAILED(rv))
return rv;
DConnectInstance *wrapper = nsnull;
wrapper = new DConnectInstance(peer, iinfo, obj);
if (!wrapper)
return NS_ERROR_OUT_OF_MEMORY;
if (!wrappers.AppendElement(wrapper))
{
delete wrapper;
return NS_ERROR_OUT_OF_MEMORY;
}
rv = gDConnect->StoreInstance(wrapper);
if (NS_FAILED(rv))
{
wrappers.RemoveElement(wrapper);
delete wrapper;
return rv;
}
// send address of the instance wrapper, and set the low bit
// to indicate that this is an instance wrapper.
PtrBits bits = ((PtrBits) wrapper) | 0x1;
writer.PutBytes(&bits, sizeof(bits));
}
NS_IF_RELEASE(stub);
}
return NS_OK;
}
DConnectStub::~DConnectStub()
{
// destroying stub, notify peer that we have released our reference
@ -920,6 +968,14 @@ DConnectStub::QueryInterface(const nsID &aIID, void **aInstancePtr)
return NS_OK;
}
// used to discover if this is a DConnectStub instance.
if (aIID.Equals(kDConnectStubID))
{
*aInstancePtr = this;
NS_ADDREF_THIS();
return NS_OK;
}
// it might seem attractive to check this stub first for parent
// interfaces, but we should instead always defer to the master
// since that ensures that the result of QI is independent of
@ -1091,16 +1147,29 @@ DConnectStub::CallMethod(PRUint16 aMethodIndex,
const nsXPTType &type = paramInfo.GetType();
if (type.IsInterfacePointer())
{
nsID iid;
rv = gDConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
aMethodIndex, i, aParams, iid);
if (NS_SUCCEEDED(rv))
PtrBits bits = (PtrBits) *((void **) aParams[i].val.p);
if (bits & 0x1)
{
DConnectStub *stub;
void **pptr = (void **) aParams[i].val.p;
rv = CreateStub(iid, mPeerID, (DConAddr) *pptr, &stub);
*((void **) aParams[i].val.p) = (void *) (bits & ~0x1);
nsID iid;
rv = gDConnect->GetIIDForMethodParam(mIInfo, aInfo, paramInfo, type,
aMethodIndex, i, aParams, iid);
if (NS_SUCCEEDED(rv))
*((nsISupports **) aParams[i].val.p) = stub;
{
DConnectStub *stub;
void **pptr = (void **) aParams[i].val.p;
rv = CreateStub(iid, mPeerID, (DConAddr) *pptr, &stub);
if (NS_SUCCEEDED(rv))
*((nsISupports **) aParams[i].val.p) = stub;
}
}
else
{
// pointer is to one of our instance wrappers.
DConnectInstance *wrapper = (DConnectInstance *) aParams[i].val.p;
*((void **) aParams[i].val.p) = wrapper->RealInstance();
}
}
}
@ -1595,19 +1664,34 @@ ipcDConnectService::OnInvoke(PRUint32 peer, const DConnectInvoke *invoke, PRUint
if (paramInfo.IsIn() && type.IsInterfacePointer())
{
nsID iid;
rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
invoke->method_index, i, params, iid);
if (NS_SUCCEEDED(rv))
PtrBits bits = (PtrBits) params[i].ptr;
if (bits & 0x1)
{
DConnectStub *stub;
rv = CreateStub(iid, peer, (DConAddr) params[i].ptr, &stub);
// pointer is to a remote object. we need to build a stub.
params[i].ptr = (void *) (bits & ~0x1);
nsID iid;
rv = GetIIDForMethodParam(iinfo, methodInfo, paramInfo, type,
invoke->method_index, i, params, iid);
if (NS_SUCCEEDED(rv))
{
params[i].val.p = params[i].ptr = stub;
params[i].SetValIsInterface();
DConnectStub *stub;
rv = CreateStub(iid, peer, (DConAddr) params[i].ptr, &stub);
if (NS_SUCCEEDED(rv))
{
params[i].val.p = params[i].ptr = stub;
params[i].SetValIsInterface();
}
}
}
else
{
// pointer is to one of our instance wrappers.
DConnectInstance *wrapper = (DConnectInstance *) params[i].ptr;
params[i].val.p = params[i].ptr = wrapper->RealInstance();
params[i].SetValIsInterface();
}
}
}

Просмотреть файл

@ -13,7 +13,6 @@ REQUIRES = ipcd \
nspr \
string \
xpcom \
necko \
$(NULL)
CPPSRCS = \
@ -26,7 +25,6 @@ include $(topsrcdir)/config/config.mk
LIBS = \
$(EXTRA_DSO_LIBS) \
$(MOZ_JS_LIBS) \
$(XPCOM_LIBS) \
$(NSPR_LIBS) \
$(NULL)

Просмотреть файл

@ -6,12 +6,18 @@
#include "nsIServiceManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIURL.h"
#include "nsNetCID.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)) { \
@ -21,7 +27,6 @@
PR_END_MACRO
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
static nsIEventQueue* gEventQ = nsnull;
static PRBool gKeepRunning = PR_TRUE;
@ -36,85 +41,91 @@ static nsresult DoTest()
PRUint32 remoteClientID = 1;
nsCOMPtr<nsIURL> url;
rv = dcon->CreateInstance(remoteClientID, kStandardURLCID, NS_GET_IID(nsIURL), getter_AddRefs(url));
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(url, &rv);
nsCOMPtr<nsISupports> sup = do_QueryInterface(file, &rv);
NS_ENSURE_SUCCESS(rv, rv);
printf("*** calling QueryInterface\n");
nsCOMPtr<nsIURI> uri = do_QueryInterface(url, &rv);
nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(file, &rv);
NS_ENSURE_SUCCESS(rv, rv);
NS_NAMED_LITERAL_CSTRING(spec, "http://www.mozilla.org/");
nsAutoString path;
path.AssignLiteral(TEST_PATH);
printf("*** calling SetSpec\n");
rv = uri->SetSpec(spec);
printf("*** calling InitWithNativePath\n");
rv = localFile->InitWithPath(path);
NS_ENSURE_SUCCESS(rv, rv);
nsCString buf;
rv = uri->GetSpec(buf);
nsAutoString buf;
rv = file->GetPath(buf);
NS_ENSURE_SUCCESS(rv, rv);
if (!buf.Equals(spec))
if (!buf.Equals(path))
{
printf("*** GetSpec erroneously returned [%s]\n", buf.get());
NS_ConvertUTF16toUTF8 temp(buf);
printf("*** GetPath erroneously returned [%s]\n", temp.get());
return NS_ERROR_FAILURE;
}
PRBool match;
rv = uri->SchemeIs("http", &match);
if (NS_FAILED(rv) || !match)
PRBool exists;
rv = file->Exists(&exists);
if (NS_FAILED(rv))
{
printf("*** SchemeIs test failed [rv=%x]\n", rv);
printf("*** Exists test failed [rv=%x]\n", rv);
return NS_ERROR_FAILURE;
}
printf("File exists? [%d]\n", exists);
nsCString resolvedSpec;
rv = uri->Resolve(NS_LITERAL_CSTRING("index.html"), resolvedSpec);
if (NS_FAILED(rv) || !resolvedSpec.EqualsLiteral("http://www.mozilla.org/index.html"))
{
printf("*** Resolve test failed [rv=%x result=%s]\n", rv, resolvedSpec.get());
return NS_ERROR_FAILURE;
}
PRInt32 port;
rv = uri->GetPort(&port);
if (NS_FAILED(rv) || port != -1)
{
printf("*** GetPort test failed [rv=%x port=%d]\n", rv, port);
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> clonedURI;
rv = uri->Clone(getter_AddRefs(clonedURI));
if (NS_FAILED(rv) || !clonedURI)
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;
}
rv = clonedURI->GetSpec(buf);
if (NS_FAILED(rv) || !buf.Equals(spec))
nsAutoString node;
node.AssignLiteral("hello.txt");
rv = clone->Append(node);
if (NS_FAILED(rv))
{
printf("*** Clone test (2) failed [rv=%x buf=%s]\n", rv, buf.get());
printf("*** Append test failed [rv=%x]\n", rv);
return NS_ERROR_FAILURE;
}
url = do_QueryInterface(clonedURI, &rv);
PRBool match;
rv = file->Equals(clone, &match);
if (NS_FAILED(rv))
{
printf("*** QI test failed [rv=%x]\n", rv);
return rv;
printf("*** Equals test failed [rv=%x]\n", rv);
return NS_ERROR_FAILURE;
}
printf("Files are equals? [%d]\n", match);
// 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 = url->SetFilePath(NS_LITERAL_CSTRING("/foo/bar/x.y.html?what"));
rv = file->Equals(myLocalFile, &match);
if (NS_FAILED(rv))
{
printf("*** SetFilePath test failed [rv=%x]\n", rv);
return 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;
@ -124,6 +135,20 @@ 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);
@ -147,13 +172,15 @@ int main(int argc, char **argv)
RETURN_IF_FAILED(rv, "do_GetService(ipcServ)");
NS_ADDREF(gIpcServ = ipcServ);
if (argc > 1) {
printf("*** using client name [%s]\n", argv[1]);
gIpcServ->AddName(argv[1]);
if (!serverMode)
{
rv = DoTest();
RETURN_IF_FAILED(rv, "DoTest()");
}
else
{
gIpcServ->AddName("DConnectServer");
}
rv = DoTest();
RETURN_IF_FAILED(rv, "DoTest()");
PLEvent *ev;
while (gKeepRunning)

Просмотреть файл

@ -62,7 +62,6 @@ include $(topsrcdir)/config/config.mk
LIBS = \
$(EXTRA_DSO_LIBS) \
$(MOZ_JS_LIBS) \
$(XPCOM_LIBS) \
$(NSPR_LIBS) \
$(NULL)