master
h 114 lines 2.81 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 COMImplClass.h
8
9 Abstract:
10
11 This file contains the definition for COMImplClass, a helper to forward calls from a COM class to an impl class.
12 // N.B. This class allows multiple calls to happen in parallel, and only blocks Disconnect() until there are either no more callers, or only calling thread is in a call to the underlying class.
13 // This is implemented that way so a caller can call Disconnect() from within a call to the COM class without causing a deadlock.
14
15 --*/
16
17 #pragma once
18
19 namespace wsl::windows::service::wslc {
20
21 template <typename TImpl, typename TPointer = TImpl*>
22 class COMImplClass
23 {
24 public:
25 void Initialize(TPointer impl)
26 {
27 std::unique_lock lock(m_lock);
28 m_impl = std::move(impl);
29 }
30
31 void Disconnect() noexcept
32 {
33 std::unique_lock lock(m_lock);
34
35 // Only continue if either:
36 // - There are no current callers
37 // - This thread is the only caller
38
39 m_cv.wait(lock, [this] {
40 return m_callers.empty() || m_callers.size() == 1 && *m_callers.begin() == std::this_thread::get_id();
41 });
42
43 m_impl = {};
44 }
45
46 protected:
47 template <typename... Args>
48 HRESULT CallImpl(void (TImpl::*routine)(Args... args), Args... args)
49 try
50 {
51 auto [lock, impl] = LockImpl();
52 ((*impl).*routine)(std::forward<Args>(args)...);
53
54 return S_OK;
55 }
56 CATCH_RETURN();
57
58 template <typename... Args>
59 HRESULT CallImpl(void (TImpl::*routine)(Args... args) const, Args... args)
60 try
61 {
62 auto [lock, impl] = LockImpl();
63 ((*impl).*routine)(std::forward<Args>(args)...);
64
65 return S_OK;
66 }
67 CATCH_RETURN();
68
69 auto GetPointer()
70 {
71 if constexpr (std::is_same_v<TPointer, TImpl*>)
72 {
73 return m_impl;
74 }
75 else
76 {
77 return m_impl.lock();
78 }
79 }
80
81 [[nodiscard]] auto LockImpl()
82 {
83 auto impl = [this] {
84 std::unique_lock lock{m_lock};
85
86 auto pointer = GetPointer();
87 THROW_HR_IF(RPC_E_DISCONNECTED, !pointer);
88
89 auto [_, inserted] = m_callers.insert(std::this_thread::get_id());
90 WI_ASSERT(inserted);
91
92 return pointer;
93 }();
94
95 auto release = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [this]() {
96 std::unique_lock lock{m_lock};
97
98 auto removed = m_callers.erase(std::this_thread::get_id());
99 WI_ASSERT(removed == 1);
100
101 m_cv.notify_one();
102 });
103
104 return std::make_pair(std::move(release), std::move(impl));
105 }
106
107 private:
108 std::mutex m_lock;
109 std::condition_variable m_cv;
110 _Guarded_by_(m_lock) std::unordered_set<std::thread::id> m_callers;
111 TPointer m_impl{};
112 };
113
114 } // namespace wsl::windows::service::wslc