From: Vibhav Pant vibhavp@gmail.com
--- dlls/vccorlib140/tests/vccorlib.c | 14 ++-- dlls/vccorlib140/vccorlib.c | 126 +++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 11 deletions(-)
diff --git a/dlls/vccorlib140/tests/vccorlib.c b/dlls/vccorlib140/tests/vccorlib.c index b0391957a30..9de2b0ef7ca 100644 --- a/dlls/vccorlib140/tests/vccorlib.c +++ b/dlls/vccorlib140/tests/vccorlib.c @@ -382,7 +382,7 @@ static void test_refcount_(int line, void *obj, LONG val) ok_(__FILE__, line)(count == val, "got refcount %lu != %lu\n", count, val); }
-struct weakref +struct control_block { IWeakReference IWeakReference_iface; LONG ref_weak; @@ -399,7 +399,7 @@ struct unknown_impl { IUnknown IUnknown_iface; ULONG strong_ref_free_val; /* Should be a negative value */ - struct weakref *weakref; + struct control_block *weakref; };
static struct unknown_impl *impl_from_IUnknown(IUnknown *iface) @@ -439,7 +439,7 @@ static ULONG WINAPI unknown_Release(IUnknown *iface)
if (!ref) { - struct weakref *weak = impl->weakref; + struct control_block *weak = impl->weakref; BOOL is_inline = weak->is_inline; IUnknown *out = NULL; LONG count; @@ -506,7 +506,7 @@ static void test_AllocateWithWeakRef_inline(void)
/* Test inline allocation. */ object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), sizeof(struct unknown_impl)); - todo_wine ok(object != NULL, "got object %p\n", object); + ok(object != NULL, "got object %p\n", object); if (!object) { skip("AllocateWithWeakRef returned NULL\n"); @@ -524,8 +524,8 @@ static void test_AllocateWithWeakRef_inline(void) &object->IUnknown_iface); ok(object->weakref->unknown == 0, "got unknown %d\n", object->weakref->unknown); /* The object is allocate within the weakref. */ - ok((char *)object->weakref == ((char *)object - sizeof(struct weakref)), "got %p != %p\n", object->weakref, - (char *)object - sizeof(struct weakref)); + ok((char *)object->weakref == ((char *)object - sizeof(struct control_block)), "got %p != %p\n", object->weakref, + (char *)object - sizeof(struct control_block));
hr = IWeakReference_Resolve(weakref, &IID_IAgileObject, (IInspectable **)&out); ok(hr == S_OK, "got hr %#lx\n", hr); @@ -563,7 +563,7 @@ static void test_AllocateWithWeakRef(void)
/* Test non-inline allocation. */ object = pAllocateWithWeakRef(offsetof(struct unknown_impl, weakref), INLINE_MAX + 1); - todo_wine ok(object != NULL, "got object %p\n", object); + ok(object != NULL, "got object %p\n", object); if (!object) { skip("AllocateWithWeakRef returned NULL\n"); diff --git a/dlls/vccorlib140/vccorlib.c b/dlls/vccorlib140/vccorlib.c index a169d210ba8..e225e7ce5a7 100644 --- a/dlls/vccorlib140/vccorlib.c +++ b/dlls/vccorlib140/vccorlib.c @@ -17,7 +17,13 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + +#include <stdbool.h> + +#include "initguid.h" #include "roapi.h" +#include "weakreference.h" #include "winstring.h" #include "wine/asm.h" #include "wine/debug.h" @@ -95,15 +101,127 @@ void __cdecl Free(void *addr) free(addr); }
+struct control_block +{ + IWeakReference IWeakReference_iface; + LONG ref_weak; + LONG ref_strong; + IUnknown *object; + bool is_inline; + UINT16 unknown; +#ifdef _WIN32 + char _padding[4]; +#endif +}; + +static inline struct control_block *impl_from_IWeakReference(IWeakReference *iface) +{ + return CONTAINING_RECORD(iface, struct control_block, IWeakReference_iface); +} + +static HRESULT WINAPI control_block_QueryInterface(IWeakReference *iface, const GUID *iid, void **out) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + + TRACE("(%p, %s, %p)", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IWeakReference)) + { + IWeakReference_AddRef((*out = &impl->IWeakReference_iface)); + return S_OK; + } + + *out = NULL; + ERR("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI control_block_AddRef(IWeakReference *iface) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + + TRACE("(%p)\n", iface); + + return InterlockedIncrement(&impl->ref_weak); +} + +static ULONG WINAPI control_block_Release(IWeakReference *iface) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + ULONG ref = InterlockedDecrement(&impl->ref_weak); + + TRACE("(%p)\n", iface); + + if (!ref) Free(impl); + return ref; +} + +static HRESULT WINAPI control_block_Resolve(IWeakReference *iface, const GUID *iid, IInspectable **out) +{ + struct control_block *impl = impl_from_IWeakReference(iface); + HRESULT hr; + LONG ref; + + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + do + { + ref = ReadNoFence(&impl->ref_strong); + if (ref <= 0) + return S_OK; + } while (ref != InterlockedCompareExchange(&impl->ref_strong, ref + 1, ref)); + + hr = IUnknown_QueryInterface(impl->object, iid, (void **)out); + IUnknown_Release(impl->object); + return hr; +} + +static const IWeakReferenceVtbl control_block_vtbl = +{ + control_block_QueryInterface, + control_block_AddRef, + control_block_Release, + control_block_Resolve, +}; + void *__cdecl AllocateWithWeakRef(ptrdiff_t offset, size_t size) { - FIXME("(%Iu, %Iu) stub\n", offset, size); + const size_t inline_max = sizeof(void *) == 8 ? 128 : 64; + struct control_block *weakref; + void *object; + + TRACE("(%Iu, %Iu)\n", offset, size); + + if (size > inline_max) + { + weakref = Allocate(sizeof(*weakref)); + object = Allocate(size); + weakref->is_inline = FALSE; + } + else /* Perform an inline allocation */ + { + weakref = Allocate(sizeof(*weakref) + size); + object = (char *)weakref + sizeof(*weakref); + weakref->is_inline = TRUE; + } + + *(struct control_block **)((char *)object + offset) = weakref; + weakref->IWeakReference_iface.lpVtbl = &control_block_vtbl; + weakref->object = object; + weakref->ref_strong = weakref->ref_weak = 1; + weakref->unknown = 0;
- return NULL; + return weakref->object; }
DEFINE_THISCALL_WRAPPER(control_block_ReleaseTarget, 4) -void __thiscall control_block_ReleaseTarget(void *weakref) +void __thiscall control_block_ReleaseTarget(struct control_block *weakref) { - FIXME("(%p) stub\n", weakref); + void *object; + + TRACE("(%p)\n", weakref); + + if (weakref->is_inline || ReadNoFence(&weakref->ref_strong) >= 0) return; + if ((object = InterlockedCompareExchangePointer((void *)&weakref->object, NULL, weakref->object))) + Free(object); }