From 2f0e714a2779b4ccca397cad67835afe64b94cfa Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Wed, 28 Jun 2006 21:25:14 +0100 Subject: [PATCH] ole32: When marshaling a proxy make sure to maintain an external reference on the stub object so that the first proxy can be released. Implement external refcount sharing between a proxy and the marshaled proxy. Extend the marshaling of a proxy test to show that an external reference is always kept on the stub object. --- dlls/ole32/marshal.c | 46 ++++++++++++++++++++++++++++++++++---- dlls/ole32/tests/marshal.c | 40 +++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index 12e886d9efb..152647bca4b 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -379,11 +379,49 @@ static HRESULT WINAPI Proxy_MarshalInterface( if (SUCCEEDED(hr)) { STDOBJREF stdobjref = ifproxy->stdobjref; - /* FIXME: optimization - share out proxy's public references if possible - * instead of making new proxy do a roundtrip through the server */ - stdobjref.cPublicRefs = 0; /* InterlockedDecrement(&This->stdobjref.cPublicRefs) >= 0 ? 1 : 0 */ + ULONG cPublicRefs = ifproxy->refs; + ULONG cPublicRefsOld; - hr = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), NULL); + /* optimization - share out proxy's public references if possible + * instead of making new proxy do a roundtrip through the server */ + do + { + ULONG cPublicRefsNew; + cPublicRefsOld = cPublicRefs; + stdobjref.cPublicRefs = cPublicRefs / 2; + cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs; + cPublicRefs = InterlockedCompareExchange( + (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld); + } while (cPublicRefs != cPublicRefsOld); + + if (!stdobjref.cPublicRefs) + { + IRemUnknown *remunk; + hr = proxy_manager_get_remunknown(This, &remunk); + if (hr == S_OK) + { + HRESULT hrref = S_OK; + REMINTERFACEREF rif; + rif.ipid = ifproxy->stdobjref.ipid; + rif.cPublicRefs = NORMALEXTREFS; + rif.cPrivateRefs = 0; + hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref); + if (hr == S_OK && hrref == S_OK) + stdobjref.cPublicRefs = rif.cPublicRefs; + else + ERR("IRemUnknown_RemAddRef returned with 0x%08lx, hrref = 0x%08lx\n", hr, hrref); + } + } + + if (SUCCEEDED(hr)) + { + TRACE("writing stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n", + stdobjref.flags, stdobjref.cPublicRefs, + wine_dbgstr_longlong(stdobjref.oxid), + wine_dbgstr_longlong(stdobjref.oid), + debugstr_guid(&stdobjref.ipid)); + hr = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), NULL); + } } else { diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index 24f92685614..102be874dd3 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -434,6 +434,10 @@ static void test_interthread_marshal_and_unmarshal(void) end_host_object(tid, thread); } +/* the number of external references that Wine's proxy manager normally gives + * out, so we can test the border case of running out of references */ +#define NORMALEXTREFS 5 + /* tests success case of an interthread marshal and then marshaling the proxy */ static void test_proxy_marshal_and_unmarshal(void) { @@ -443,6 +447,7 @@ static void test_proxy_marshal_and_unmarshal(void) IUnknown *pProxy2 = NULL; DWORD tid; HANDLE thread; + int i; cLocks = 0; @@ -465,14 +470,26 @@ static void test_proxy_marshal_and_unmarshal(void) ok_more_than_one_lock(); + /* marshal 5 more times to exhaust the normal external references of 5 */ + for (i = 0; i < NORMALEXTREFS; i++) + { + hr = CoMarshalInterface(pStream, &IID_IClassFactory, pProxy, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); + ok_ole_success(hr, CoMarshalInterface); + } + + ok_more_than_one_lock(); + + /* release the original proxy to test that we successfully keep the + * original object alive */ + IUnknown_Release(pProxy); + IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); - /* unmarshal the second proxy to the object */ hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); ok_ole_success(hr, CoUnmarshalInterface); - IStream_Release(pStream); + + ok_more_than_one_lock(); /* now the proxies should be as follows: - * pProxy -> &Test_ClassFactory * pProxy2 -> &Test_ClassFactory * they should NOT be as follows: * pProxy -> &Test_ClassFactory @@ -480,16 +497,21 @@ static void test_proxy_marshal_and_unmarshal(void) * the above can only really be tested by looking in +ole traces */ - ok_more_than_one_lock(); - - IUnknown_Release(pProxy); - - ok_more_than_one_lock(); - IUnknown_Release(pProxy2); + /* unmarshal all of the proxies to check that the object stub still exists */ + for (i = 0; i < NORMALEXTREFS; i++) + { + hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); + ok_ole_success(hr, CoUnmarshalInterface); + + IUnknown_Release(pProxy2); + } + ok_no_locks(); + IStream_Release(pStream); + end_host_object(tid, thread); }