]>
Commit | Line | Data |
---|---|---|
e5ddd4ce | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
e5ddd4ce AR |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | #ifndef SQUID_SRC_BASE_ASYNCCALLBACKS_H | |
10 | #define SQUID_SRC_BASE_ASYNCCALLBACKS_H | |
11 | ||
12 | #include "base/AsyncCall.h" | |
13 | #include "base/AsyncJobCalls.h" | |
14 | #include "base/TypeTraits.h" | |
15 | ||
16 | /// access to a callback result carried by an asynchronous CallDialer | |
17 | template <typename AnswerT> | |
18 | class WithAnswer | |
19 | { | |
20 | public: | |
21 | using Answer = AnswerT; | |
22 | ||
23 | virtual ~WithAnswer() = default; | |
24 | ||
25 | /// callback results setter | |
26 | virtual Answer &answer() = 0; | |
27 | }; | |
28 | ||
29 | /// a smart AsyncCall pointer for delivery of future results | |
30 | template <typename Answer> | |
31 | class AsyncCallback | |
32 | { | |
33 | public: | |
34 | // all generated copying/moving functions are correct | |
35 | AsyncCallback() = default; | |
36 | ||
37 | template <class Call> | |
38 | explicit AsyncCallback(const RefCount<Call> &call): | |
39 | call_(call), | |
40 | answer_(&(call->dialer.answer())) | |
41 | { | |
42 | } | |
43 | ||
44 | Answer &answer() | |
45 | { | |
46 | assert(answer_); | |
47 | return *answer_; | |
48 | } | |
49 | ||
50 | /// make this smart pointer nil | |
51 | /// \return the AsyncCall pointer we used to manage before this call | |
52 | AsyncCall::Pointer release() | |
53 | { | |
54 | answer_ = nullptr; | |
55 | const auto call = call_; | |
56 | call_ = nullptr; | |
57 | return call; | |
58 | } | |
59 | ||
60 | /// whether the callback has been set but not released | |
61 | explicit operator bool() const { return answer_; } | |
62 | ||
63 | /* methods for decaying into an AsyncCall pointer w/o access to answer */ | |
64 | operator const AsyncCall::Pointer &() const { return call_; } | |
65 | const AsyncCall &operator *() const { return call_.operator*(); } | |
66 | const AsyncCall *operator ->() const { return call_.operator->(); } | |
67 | ||
68 | private: | |
69 | /// callback carrying the answer | |
70 | AsyncCall::Pointer call_; | |
71 | ||
72 | /// (future) answer inside this->call, obtained when it was still possible | |
73 | /// to reach it without dynamic casts and virtual methods | |
74 | Answer *answer_ = nullptr; | |
75 | }; | |
76 | ||
77 | /// CallDialer for single-parameter callback functions | |
78 | template <typename Argument1> | |
79 | class UnaryFunCallbackDialer: | |
80 | public CallDialer, | |
81 | public WithAnswer<Argument1> | |
82 | { | |
83 | public: | |
84 | // stand-alone function that receives our answer | |
85 | using Handler = void (Argument1 &); | |
86 | ||
87 | explicit UnaryFunCallbackDialer(Handler * const aHandler): handler(aHandler) {} | |
337b9aa4 | 88 | ~UnaryFunCallbackDialer() override = default; |
e5ddd4ce AR |
89 | |
90 | /* CallDialer API */ | |
91 | bool canDial(AsyncCall &) { return bool(handler); } | |
92 | void dial(AsyncCall &) { handler(arg1); } | |
337b9aa4 | 93 | void print(std::ostream &os) const final { os << '(' << arg1 << ')'; } |
e5ddd4ce AR |
94 | |
95 | /* WithAnswer API */ | |
337b9aa4 | 96 | Argument1 &answer() final { return arg1; } |
e5ddd4ce AR |
97 | |
98 | private: | |
99 | Handler *handler; ///< the function to call | |
100 | Argument1 arg1; ///< actual call parameter | |
101 | }; | |
102 | ||
103 | /// CallDialer for single-parameter callback methods of cbdata-protected classes | |
104 | /// that are not AsyncJobs (use UnaryJobCallbackDialer for the latter). | |
105 | template <class Destination, typename Argument1> | |
106 | class UnaryCbcCallbackDialer: | |
107 | public CallDialer, | |
108 | public WithAnswer<Argument1> | |
109 | { | |
110 | public: | |
111 | // class member function that receives our answer | |
112 | typedef void (Destination::*Method)(Argument1 &); | |
113 | ||
114 | UnaryCbcCallbackDialer(Method method, Destination *destination): destination_(destination), method_(method) {} | |
337b9aa4 | 115 | ~UnaryCbcCallbackDialer() override = default; |
e5ddd4ce AR |
116 | |
117 | /* CallDialer API */ | |
118 | bool canDial(AsyncCall &) { return destination_.valid(); } | |
119 | void dial(AsyncCall &) {((*destination_).*method_)(arg1_); } | |
337b9aa4 | 120 | void print(std::ostream &os) const final { os << '(' << arg1_ << ')'; } |
e5ddd4ce AR |
121 | |
122 | /* WithAnswer API */ | |
337b9aa4 | 123 | Argument1 &answer() final { return arg1_; } |
e5ddd4ce AR |
124 | |
125 | private: | |
126 | CbcPointer<Destination> destination_; ///< object to deliver the answer to | |
127 | Method method_; ///< Destination method to call with the answer | |
128 | Argument1 arg1_; | |
129 | }; | |
130 | ||
131 | /// CallDialer for single-parameter callback methods of AsyncJob classes. | |
132 | /// \sa UnaryCbcCallbackDialer and UnaryFunCallbackDialer. | |
133 | template <class Job, typename Argument1> | |
134 | class UnaryJobCallbackDialer: | |
135 | public UnaryMemFunT<Job, Argument1, Argument1&>, | |
136 | public WithAnswer<Argument1> | |
137 | { | |
138 | public: | |
139 | using Base = UnaryMemFunT<Job, Argument1, Argument1&>; | |
140 | ||
141 | UnaryJobCallbackDialer(const CbcPointer<Job> &aJob, typename Base::Method aMethod): | |
142 | Base(aJob, aMethod, {}) {} | |
143 | ||
144 | /* WithAnswer API */ | |
337b9aa4 | 145 | Argument1 &answer() final { return this->arg1; } |
e5ddd4ce AR |
146 | }; |
147 | ||
148 | /// whether the given type is an AsyncJob | |
149 | /// reduces code duplication in declarations further below | |
150 | template <typename T> | |
151 | using IsAsyncJob = typename std::conditional< | |
152 | std::is_base_of<AsyncJob, T>::value, | |
153 | std::true_type, | |
154 | std::false_type | |
155 | >::type; | |
156 | ||
157 | /// helper function to simplify UnaryCbcCallbackDialer creation | |
09835feb | 158 | template <class Destination, typename Argument1, std::enable_if_t<!IsAsyncJob<Destination>::value, int> = 0> |
e5ddd4ce AR |
159 | UnaryCbcCallbackDialer<Destination, Argument1> |
160 | callbackDialer(void (Destination::*method)(Argument1 &), Destination * const destination) | |
161 | { | |
162 | static_assert(!std::is_base_of<AsyncJob, Destination>::value, "wrong wrapper"); | |
163 | return UnaryCbcCallbackDialer<Destination, Argument1>(method, destination); | |
164 | } | |
165 | ||
166 | /// helper function to simplify UnaryJobCallbackDialer creation | |
09835feb | 167 | template <class Destination, typename Argument1, std::enable_if_t<IsAsyncJob<Destination>::value, int> = 0> |
e5ddd4ce AR |
168 | UnaryJobCallbackDialer<Destination, Argument1> |
169 | callbackDialer(void (Destination::*method)(Argument1 &), Destination * const destination) | |
170 | { | |
171 | static_assert(std::is_base_of<AsyncJob, Destination>::value, "wrong wrapper"); | |
172 | return UnaryJobCallbackDialer<Destination, Argument1>(destination, method); | |
173 | } | |
174 | ||
175 | /// helper function to simplify UnaryFunCallbackDialer creation | |
176 | template <typename Argument1> | |
177 | UnaryFunCallbackDialer<Argument1> | |
178 | callbackDialer(void (*destination)(Argument1 &)) | |
179 | { | |
180 | return UnaryFunCallbackDialer<Argument1>(destination); | |
181 | } | |
182 | ||
183 | /// helper function to create an AsyncCallback object that matches an AsyncCall | |
184 | /// based on a WithAnswer answer dialer. | |
185 | template <class Call> | |
186 | AsyncCallback<typename Call::Dialer::Answer> | |
187 | AsyncCallback_(const RefCount<Call> &call) | |
188 | { | |
189 | return AsyncCallback<typename Call::Dialer::Answer>(call); | |
190 | } | |
191 | ||
192 | /// AsyncCall for calling back a class method compatible with | |
193 | /// callbackDialer(). TODO: Unify with JobCallback() which requires dialers | |
194 | /// that feed the job pointer to the non-default CommCommonCbParams constructor. | |
195 | #define asyncCallback(dbgSection, dbgLevel, method, object) \ | |
196 | AsyncCallback_(asyncCall((dbgSection), (dbgLevel), #method, \ | |
197 | callbackDialer(&method, (object)))) | |
198 | ||
199 | // TODO: Use C++20 __VA_OPT__ to merge this with asyncCallback(). | |
200 | /// AsyncCall for calling back a function | |
201 | #define asyncCallbackFun(dbgSection, dbgLevel, function) \ | |
202 | AsyncCallback_(asyncCall((dbgSection), (dbgLevel), #function, \ | |
203 | callbackDialer(&function))) | |
204 | ||
205 | #endif // SQUID_SRC_BASE_ASYNCCALLBACKS_H | |
206 |