Skip to content

Commit 0c8f4e1

Browse files
authored
Extend weak reference support to classic COM (#1104)
1 parent b401564 commit 0c8f4e1

File tree

2 files changed

+71
-32
lines changed

2 files changed

+71
-32
lines changed

strings/base_implements.h

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,7 @@ namespace winrt::impl
10631063

10641064
using is_agile = std::negation<std::disjunction<std::is_same<non_agile, I>...>>;
10651065
using is_inspectable = std::disjunction<std::is_base_of<Windows::Foundation::IInspectable, I>...>;
1066-
using is_weak_ref_source = std::conjunction<is_inspectable, std::negation<std::disjunction<std::is_same<no_weak_ref, I>...>>>;
1066+
using is_weak_ref_source = std::negation<std::disjunction<std::is_same<no_weak_ref, I>...>>;
10671067
using use_module_lock = std::negation<std::disjunction<std::is_same<no_module_lock, I>...>>;
10681068
using weak_ref_t = impl::weak_ref<is_agile::value, use_module_lock::value>;
10691069

@@ -1125,57 +1125,64 @@ namespace winrt::impl
11251125

11261126
impl::IWeakReferenceSource* make_weak_ref() noexcept
11271127
{
1128-
static_assert(is_weak_ref_source::value, "This is only for weak ref support.");
1129-
uintptr_t count_or_pointer = m_references.load(std::memory_order_relaxed);
1130-
1131-
if (is_weak_ref(count_or_pointer))
1128+
if constexpr (is_weak_ref_source::value)
11321129
{
1133-
return decode_weak_ref(count_or_pointer)->get_source();
1134-
}
1135-
1136-
com_ptr<weak_ref_t> weak_ref;
1137-
*weak_ref.put() = new (std::nothrow) weak_ref_t(get_unknown(), static_cast<uint32_t>(count_or_pointer));
1130+
uintptr_t count_or_pointer = m_references.load(std::memory_order_relaxed);
11381131

1139-
if (!weak_ref)
1140-
{
1141-
return nullptr;
1142-
}
1132+
if (is_weak_ref(count_or_pointer))
1133+
{
1134+
return decode_weak_ref(count_or_pointer)->get_source();
1135+
}
11431136

1144-
uintptr_t const encoding = encode_weak_ref(weak_ref.get());
1137+
com_ptr<weak_ref_t> weak_ref;
1138+
*weak_ref.put() = new (std::nothrow) weak_ref_t(get_unknown(), static_cast<uint32_t>(count_or_pointer));
11451139

1146-
for (;;)
1147-
{
1148-
if (m_references.compare_exchange_weak(count_or_pointer, encoding, std::memory_order_acq_rel, std::memory_order_relaxed))
1140+
if (!weak_ref)
11491141
{
1150-
impl::IWeakReferenceSource* result = weak_ref->get_source();
1151-
detach_abi(weak_ref);
1152-
return result;
1142+
return nullptr;
11531143
}
11541144

1155-
if (is_weak_ref(count_or_pointer))
1145+
uintptr_t const encoding = encode_weak_ref(weak_ref.get());
1146+
1147+
for (;;)
11561148
{
1157-
return decode_weak_ref(count_or_pointer)->get_source();
1158-
}
1149+
if (m_references.compare_exchange_weak(count_or_pointer, encoding, std::memory_order_acq_rel, std::memory_order_relaxed))
1150+
{
1151+
impl::IWeakReferenceSource* result = weak_ref->get_source();
1152+
detach_abi(weak_ref);
1153+
return result;
1154+
}
1155+
1156+
if (is_weak_ref(count_or_pointer))
1157+
{
1158+
return decode_weak_ref(count_or_pointer)->get_source();
1159+
}
11591160

1160-
weak_ref->set_strong(static_cast<uint32_t>(count_or_pointer));
1161+
weak_ref->set_strong(static_cast<uint32_t>(count_or_pointer));
1162+
}
1163+
}
1164+
else
1165+
{
1166+
static_assert(is_weak_ref_source::value, "Weak references are not supported because no_weak_ref was specified.");
1167+
return nullptr;
11611168
}
11621169
}
11631170

11641171
static bool is_weak_ref(intptr_t const value) noexcept
11651172
{
1166-
static_assert(is_weak_ref_source::value, "This is only for weak ref support.");
1173+
static_assert(is_weak_ref_source::value, "Weak references are not supported because no_weak_ref was specified.");
11671174
return value < 0;
11681175
}
11691176

11701177
static weak_ref_t* decode_weak_ref(uintptr_t const value) noexcept
11711178
{
1172-
static_assert(is_weak_ref_source::value, "This is only for weak ref support.");
1179+
static_assert(is_weak_ref_source::value, "Weak references are not supported because no_weak_ref was specified.");
11731180
return reinterpret_cast<weak_ref_t*>(value << 1);
11741181
}
11751182

11761183
static uintptr_t encode_weak_ref(weak_ref_t* value) noexcept
11771184
{
1178-
static_assert(is_weak_ref_source::value, "This is only for weak ref support.");
1185+
static_assert(is_weak_ref_source::value, "Weak references are not supported because no_weak_ref was specified.");
11791186
constexpr uintptr_t pointer_flag = static_cast<uintptr_t>(1) << ((sizeof(uintptr_t) * 8) - 1);
11801187
WINRT_ASSERT((reinterpret_cast<uintptr_t>(value) & 1) == 0);
11811188
return (reinterpret_cast<uintptr_t>(value) >> 1) | pointer_flag;

test/old_tests/UnitTests/weak.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace
2222
}
2323
};
2424

25-
struct NoWeak : implements<NoWeak, ::IUnknown>
25+
struct WeakClassicCom : implements<WeakClassicCom, ::IUnknown>
2626
{
2727
};
2828

@@ -161,6 +161,26 @@ TEST_CASE("weak,source")
161161
REQUIRE(b.ToString() == L"Weak");
162162
}
163163

164+
SECTION("classic-com")
165+
{
166+
com_ptr<::IUnknown> a = make<WeakClassicCom>();
167+
168+
weak_ref<::IUnknown> w = a;
169+
com_ptr<::IUnknown> b = w.get();
170+
REQUIRE(b == a);
171+
172+
// still one outstanding reference
173+
b = nullptr;
174+
b = w.get();
175+
REQUIRE(b != nullptr);
176+
177+
// no outstanding references
178+
a = nullptr;
179+
b = nullptr;
180+
b = w.get();
181+
REQUIRE(b == nullptr);
182+
}
183+
164184
// Verify that deduction guides work.
165185
static_assert(std::is_same_v<weak_ref<IStringable>, decltype(weak_ref(IStringable()))>);
166186
static_assert(std::is_same_v<weak_ref<Uri>, decltype(weak_ref(std::declval<Uri>()))>);
@@ -206,12 +226,24 @@ TEST_CASE("weak,QI")
206226
REQUIRE(ref.as<::IUnknown>() != object.as<::IUnknown>());
207227
}
208228

209-
SECTION("no-weak")
229+
SECTION("weak-classic-com")
210230
{
211-
com_ptr<::IUnknown> object = make<NoWeak>();
231+
com_ptr<::IUnknown> object = make<WeakClassicCom>();
212232
REQUIRE(!object.try_as<Windows::Foundation::IInspectable>());
213-
REQUIRE(!object.try_as<winrt::impl::IWeakReferenceSource>());
233+
REQUIRE(object.try_as<winrt::impl::IWeakReferenceSource>());
214234
REQUIRE(!object.try_as<winrt::impl::IWeakReference>());
235+
236+
com_ptr<winrt::impl::IWeakReferenceSource> source = object.as<winrt::impl::IWeakReferenceSource>();
237+
REQUIRE(!source.try_as<winrt::impl::IWeakReference>());
238+
REQUIRE(source.try_as<winrt::impl::IWeakReferenceSource>());
239+
REQUIRE(object.as<::IUnknown>() == source.as<::IUnknown>());
240+
241+
com_ptr<winrt::impl::IWeakReference> ref;
242+
REQUIRE(S_OK == source->GetWeakReference(ref.put()));
243+
REQUIRE(!ref.try_as<winrt::impl::IWeakReferenceSource>());
244+
REQUIRE(!ref.try_as<Windows::Foundation::IInspectable>());
245+
REQUIRE(ref.as<winrt::impl::IWeakReference>() == ref);
246+
REQUIRE(ref.as<::IUnknown>() != object.as<::IUnknown>());
215247
}
216248

217249
SECTION("factory")

0 commit comments

Comments
 (0)