|
1 | 1 |
|
2 | 2 | namespace winrt::impl |
3 | 3 | { |
| 4 | + inline auto submit_threadpool_callback(void(__stdcall* callback)(void*, void* context), void* context) |
| 5 | + { |
| 6 | + if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, context, nullptr)) |
| 7 | + { |
| 8 | + throw_last_error(); |
| 9 | + } |
| 10 | + } |
| 11 | + |
4 | 12 | inline void __stdcall resume_background_callback(void*, void* context) noexcept |
5 | 13 | { |
6 | 14 | std::experimental::coroutine_handle<>::from_address(context)(); |
7 | 15 | }; |
8 | 16 |
|
9 | 17 | inline auto resume_background(std::experimental::coroutine_handle<> handle) |
10 | 18 | { |
11 | | - if (!WINRT_IMPL_TrySubmitThreadpoolCallback(resume_background_callback, handle.address(), nullptr)) |
12 | | - { |
13 | | - throw_last_error(); |
14 | | - } |
| 19 | + submit_threadpool_callback(resume_background_callback, handle.address()); |
15 | 20 | } |
16 | 21 |
|
17 | | - inline bool is_sta() noexcept |
| 22 | + inline std::pair<int32_t, int32_t> get_apartment_type() noexcept |
18 | 23 | { |
19 | 24 | int32_t aptType; |
20 | 25 | int32_t aptTypeQualifier; |
21 | | - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); |
| 26 | + if (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) |
| 27 | + { |
| 28 | + return { aptType, aptTypeQualifier }; |
| 29 | + } |
| 30 | + else |
| 31 | + { |
| 32 | + return { 1 /* APTTYPE_MTA */, 1 /* APTTYPEQUALIFIER_IMPLICIT_MTA */ }; |
| 33 | + } |
22 | 34 | } |
23 | 35 |
|
24 | | - inline bool requires_apartment_context() noexcept |
| 36 | + inline bool is_sta_thread() noexcept |
25 | 37 | { |
26 | | - int32_t aptType; |
27 | | - int32_t aptTypeQualifier; |
28 | | - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 2 /*APTTYPE_NA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); |
| 38 | + auto type = get_apartment_type(); |
| 39 | + switch (type.first) |
| 40 | + { |
| 41 | + case 0: /* APTTYPE_STA */ |
| 42 | + case 3: /* APTTYPE_MAINSTA */ |
| 43 | + return true; |
| 44 | + case 2: /* APTTYPE_NA */ |
| 45 | + return type.second == 3 /* APTTYPEQUALIFIER_NA_ON_STA */ || |
| 46 | + type.second == 5 /* APTTYPEQUALIFIER_NA_ON_MAINSTA */; |
| 47 | + } |
| 48 | + return false; |
29 | 49 | } |
30 | 50 |
|
31 | | - inline auto apartment_context() |
| 51 | + struct resume_apartment_context |
32 | 52 | { |
33 | | - return requires_apartment_context() ? capture<IContextCallback>(WINRT_IMPL_CoGetObjectContext) : nullptr; |
34 | | - } |
| 53 | + com_ptr<IContextCallback> m_context = try_capture<IContextCallback>(WINRT_IMPL_CoGetObjectContext); |
| 54 | + int32_t m_context_type = get_apartment_type().first; |
| 55 | + }; |
35 | 56 |
|
36 | 57 | inline int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept |
37 | 58 | { |
38 | 59 | std::experimental::coroutine_handle<>::from_address(args->data)(); |
39 | 60 | return 0; |
40 | 61 | }; |
41 | 62 |
|
42 | | - inline auto resume_apartment(com_ptr<IContextCallback> const& context, std::experimental::coroutine_handle<> handle) |
| 63 | + inline void resume_apartment_sync(com_ptr<IContextCallback> const& context, std::experimental::coroutine_handle<> handle) |
| 64 | + { |
| 65 | + com_callback_args args{}; |
| 66 | + args.data = handle.address(); |
| 67 | + |
| 68 | + check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of<ICallbackWithNoReentrancyToApplicationSTA>(), 5, nullptr)); |
| 69 | + } |
| 70 | + |
| 71 | + inline void resume_apartment_on_threadpool(com_ptr<IContextCallback> const& context, std::experimental::coroutine_handle<> handle) |
43 | 72 | { |
44 | | - if (context) |
| 73 | + struct threadpool_resume |
45 | 74 | { |
46 | | - com_callback_args args{}; |
47 | | - args.data = handle.address(); |
| 75 | + threadpool_resume(com_ptr<IContextCallback> const& context, std::experimental::coroutine_handle<> handle) : |
| 76 | + m_context(context), m_handle(handle) { } |
| 77 | + com_ptr<IContextCallback> m_context; |
| 78 | + std::experimental::coroutine_handle<> m_handle; |
| 79 | + }; |
| 80 | + auto state = std::make_unique<threadpool_resume>(context, handle); |
| 81 | + submit_threadpool_callback([](void*, void* p) |
| 82 | + { |
| 83 | + std::unique_ptr<threadpool_resume> state{ static_cast<threadpool_resume*>(p) }; |
| 84 | + resume_apartment_sync(state->m_context, state->m_handle); |
| 85 | + }, state.get()); |
| 86 | + state.release(); |
| 87 | + } |
48 | 88 |
|
49 | | - check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of<ICallbackWithNoReentrancyToApplicationSTA>(), 5, nullptr)); |
| 89 | + inline auto resume_apartment(resume_apartment_context const& context, std::experimental::coroutine_handle<> handle) |
| 90 | + { |
| 91 | + if ((context.m_context == nullptr) || (context.m_context == try_capture<IContextCallback>(WINRT_IMPL_CoGetObjectContext))) |
| 92 | + { |
| 93 | + handle(); |
| 94 | + } |
| 95 | + else if (context.m_context_type == 1 /* APTTYPE_MTA */) |
| 96 | + { |
| 97 | + resume_background(handle); |
| 98 | + } |
| 99 | + else if ((context.m_context_type == 2 /* APTTYPE_NTA */) && is_sta_thread()) |
| 100 | + { |
| 101 | + resume_apartment_on_threadpool(context.m_context, handle); |
50 | 102 | } |
51 | 103 | else |
52 | 104 | { |
53 | | - if (requires_apartment_context()) |
54 | | - { |
55 | | - resume_background(handle); |
56 | | - } |
57 | | - else |
58 | | - { |
59 | | - handle(); |
60 | | - } |
| 105 | + resume_apartment_sync(context.m_context, handle); |
61 | 106 | } |
62 | 107 | } |
63 | 108 |
|
@@ -294,7 +339,7 @@ WINRT_EXPORT namespace winrt |
294 | 339 | impl::resume_apartment(context, handle); |
295 | 340 | } |
296 | 341 |
|
297 | | - com_ptr<impl::IContextCallback> context = impl::apartment_context(); |
| 342 | + impl::resume_apartment_context context; |
298 | 343 | }; |
299 | 344 |
|
300 | 345 | [[nodiscard]] inline auto resume_after(Windows::Foundation::TimeSpan duration) noexcept |
|
0 commit comments