Skip to content

Commit 41e6fe7

Browse files
Chris Guzak (WINDOWS)Copilot
andcommitted
Add get_only_safe_from_non_presenting_sta() to async operations
Add a peer to .get() on IAsyncAction, IAsyncOperation, IAsyncActionWithProgress, and IAsyncOperationWithProgress that skips the _DEBUG-only STA blocking assert. The existing .get() asserts !is_sta_thread() to guard against blocking UI threads. However, not all STAs are UI threads — some never present UI, haven't presented yet, or never will. The assert is also _DEBUG-only, making it invisible to codebases that don't build with _DEBUG (e.g. the Windows OS). The new method get_only_safe_from_non_presenting_sta() is functionally identical to .get() but omits the STA check. The intentionally long name communicates the risk to callers. Changes: - strings/base_coroutine_foundation.h: Add wait_get_bypass_sta_check() impl helper and get_only_safe_from_non_presenting_sta() for all 4 async consume templates - cppwinrt/code_writers.h: Add declaration to generated code for all 4 async types - test/test_nocoro: Add test calling the new method from an STA thread using a real WinRT async operation (PathIO::ReadTextAsync on C:\Windows\win.ini) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f23b41e commit 41e6fe7

File tree

4 files changed

+71
-0
lines changed

4 files changed

+71
-0
lines changed

cppwinrt/code_writers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,24 +1433,28 @@ namespace cppwinrt
14331433
else if (type_name == "Windows.Foundation.IAsyncAction")
14341434
{
14351435
w.write(R"( auto get() const;
1436+
auto get_only_safe_from_non_presenting_sta() const;
14361437
auto wait_for(Windows::Foundation::TimeSpan const& timeout) const;
14371438
)");
14381439
}
14391440
else if (type_name == "Windows.Foundation.IAsyncOperation`1")
14401441
{
14411442
w.write(R"( auto get() const;
1443+
auto get_only_safe_from_non_presenting_sta() const;
14421444
auto wait_for(Windows::Foundation::TimeSpan const& timeout) const;
14431445
)");
14441446
}
14451447
else if (type_name == "Windows.Foundation.IAsyncActionWithProgress`1")
14461448
{
14471449
w.write(R"( auto get() const;
1450+
auto get_only_safe_from_non_presenting_sta() const;
14481451
auto wait_for(Windows::Foundation::TimeSpan const& timeout) const;
14491452
)");
14501453
}
14511454
else if (type_name == "Windows.Foundation.IAsyncOperationWithProgress`2")
14521455
{
14531456
w.write(R"( auto get() const;
1457+
auto get_only_safe_from_non_presenting_sta() const;
14541458
auto wait_for(Windows::Foundation::TimeSpan const& timeout) const;
14551459
)");
14561460
}

strings/base_coroutine_foundation.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,19 @@ namespace winrt::impl
9999
return async.GetResults();
100100
}
101101

102+
template <typename Async>
103+
auto wait_get_bypass_sta_check(Async const& async)
104+
{
105+
auto status = async.Status();
106+
if (status == Windows::Foundation::AsyncStatus::Started)
107+
{
108+
status = wait_for_completed(async, 0xFFFFFFFF); // INFINITE
109+
}
110+
check_status_canceled(status);
111+
112+
return async.GetResults();
113+
}
114+
102115
#ifdef WINRT_IMPL_COROUTINES
103116
struct ignore_apartment_context {};
104117

@@ -220,6 +233,11 @@ namespace winrt::impl
220233
impl::wait_get(static_cast<Windows::Foundation::IAsyncAction const&>(static_cast<D const&>(*this)));
221234
}
222235
template <typename D>
236+
auto consume_Windows_Foundation_IAsyncAction<D>::get_only_safe_from_non_presenting_sta() const
237+
{
238+
impl::wait_get_bypass_sta_check(static_cast<Windows::Foundation::IAsyncAction const&>(static_cast<D const&>(*this)));
239+
}
240+
template <typename D>
223241
auto consume_Windows_Foundation_IAsyncAction<D>::wait_for(Windows::Foundation::TimeSpan const& timeout) const
224242
{
225243
return impl::wait_for(static_cast<Windows::Foundation::IAsyncAction const&>(static_cast<D const&>(*this)), timeout);
@@ -231,6 +249,11 @@ namespace winrt::impl
231249
return impl::wait_get(static_cast<Windows::Foundation::IAsyncOperation<TResult> const&>(static_cast<D const&>(*this)));
232250
}
233251
template <typename D, typename TResult>
252+
auto consume_Windows_Foundation_IAsyncOperation<D, TResult>::get_only_safe_from_non_presenting_sta() const
253+
{
254+
return impl::wait_get_bypass_sta_check(static_cast<Windows::Foundation::IAsyncOperation<TResult> const&>(static_cast<D const&>(*this)));
255+
}
256+
template <typename D, typename TResult>
234257
auto consume_Windows_Foundation_IAsyncOperation<D, TResult>::wait_for(Windows::Foundation::TimeSpan const& timeout) const
235258
{
236259
return impl::wait_for(static_cast<Windows::Foundation::IAsyncOperation<TResult> const&>(static_cast<D const&>(*this)), timeout);
@@ -242,6 +265,11 @@ namespace winrt::impl
242265
impl::wait_get(static_cast<Windows::Foundation::IAsyncActionWithProgress<TProgress> const&>(static_cast<D const&>(*this)));
243266
}
244267
template <typename D, typename TProgress>
268+
auto consume_Windows_Foundation_IAsyncActionWithProgress<D, TProgress>::get_only_safe_from_non_presenting_sta() const
269+
{
270+
impl::wait_get_bypass_sta_check(static_cast<Windows::Foundation::IAsyncActionWithProgress<TProgress> const&>(static_cast<D const&>(*this)));
271+
}
272+
template <typename D, typename TProgress>
245273
auto consume_Windows_Foundation_IAsyncActionWithProgress<D, TProgress>::wait_for(Windows::Foundation::TimeSpan const& timeout) const
246274
{
247275
return impl::wait_for(static_cast<Windows::Foundation::IAsyncActionWithProgress<TProgress> const&>(static_cast<D const&>(*this)), timeout);
@@ -253,6 +281,11 @@ namespace winrt::impl
253281
return impl::wait_get(static_cast<Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> const&>(static_cast<D const&>(*this)));
254282
}
255283
template <typename D, typename TResult, typename TProgress>
284+
auto consume_Windows_Foundation_IAsyncOperationWithProgress<D, TResult, TProgress>::get_only_safe_from_non_presenting_sta() const
285+
{
286+
return impl::wait_get_bypass_sta_check(static_cast<Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> const&>(static_cast<D const&>(*this)));
287+
}
288+
template <typename D, typename TResult, typename TProgress>
256289
auto consume_Windows_Foundation_IAsyncOperationWithProgress<D, TResult, TProgress>::wait_for(Windows::Foundation::TimeSpan const& timeout) const
257290
{
258291
return impl::wait_for(static_cast<Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> const&>(static_cast<D const&>(*this)), timeout);

test/test_nocoro/get.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using namespace winrt;
44
using namespace Windows::Foundation;
5+
using namespace Windows::Storage;
56

67
template<typename TResult>
78
struct async_completion_source : implements<async_completion_source<TResult>, IAsyncOperation<TResult>, IAsyncInfo>
@@ -72,3 +73,35 @@ TEST_CASE("get")
7273

7374
REQUIRE(acs.as<IAsyncOperation<uint32_t>>().get() == 0xDEADBEEF);
7475
}
76+
77+
TEST_CASE("get_only_safe_from_non_presenting_sta")
78+
{
79+
// Call a real WinRT async operation from an STA thread.
80+
// This is the scenario the new API is designed for: an STA that is not
81+
// presenting UI, where a synchronous blocking wait is safe.
82+
std::exception_ptr failure{};
83+
std::thread sta_thread([&failure]
84+
{
85+
try
86+
{
87+
winrt::init_apartment(winrt::apartment_type::single_threaded);
88+
89+
auto content = PathIO::ReadTextAsync(L"C:\\Windows\\win.ini").get_only_safe_from_non_presenting_sta();
90+
91+
REQUIRE(content.size() > 0);
92+
93+
winrt::uninit_apartment();
94+
}
95+
catch (...)
96+
{
97+
failure = std::current_exception();
98+
}
99+
});
100+
101+
sta_thread.join();
102+
103+
if (failure)
104+
{
105+
std::rethrow_exception(failure);
106+
}
107+
}

test/test_nocoro/pch.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
#include "catch.hpp"
44
#include "winrt/Windows.Foundation.h"
5+
#include "winrt/Windows.Storage.h"
56

67
using namespace std::literals;

0 commit comments

Comments
 (0)