]> git.ipfire.org Git - thirdparty/squid.git/blob - src/base/AsyncCall.h
ba802879878e220341f9cb4ab222ad824cb72e6a
[thirdparty/squid.git] / src / base / AsyncCall.h
1 /*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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_ASYNCCALL_H
10 #define SQUID_SRC_BASE_ASYNCCALL_H
11
12 #include "base/CodeContext.h"
13 #include "base/forward.h"
14 #include "base/InstanceId.h"
15 #include "event.h"
16 #include "RefCount.h"
17
18 /**
19 \defgroup AsynCallsAPI Async-Calls API
20 \par
21 * A call is asynchronous if the caller proceeds after the call is made,
22 * and the callee receives the call during the next main loop iteration.
23 * Asynchronous calls help avoid nasty call-me-when-I-call-you loops
24 * that humans often have trouble understanding or implementing correctly.
25 \par
26 * Asynchronous calls are currently implemented via Squid events. The call
27 * event stores the pointer to the callback function and cbdata-protected
28 * callback data. To call a method of an object, the method is wrapped
29 * in a method-specific, static callback function and the pointer to the
30 * object is passed to the wrapper. For the method call to be safe, the
31 * class must be cbdata-enabled.
32 \par
33 * You do not have to use the macros below to make or receive asynchronous
34 * method calls, but they give you a uniform interface and handy call
35 * debugging.
36 */
37
38 class CallDialer;
39
40 class AsyncCall: public RefCountable
41 {
42 public:
43 using Pointer = AsyncCallPointer;
44
45 AsyncCall(int aDebugSection, int aDebugLevel, const char *aName);
46 ~AsyncCall() override;
47
48 void make(); // fire if we can; handles general call debugging
49
50 // can be called from canFire() for debugging; always returns false
51 bool cancel(const char *reason);
52
53 bool canceled() const { return isCanceled != nullptr; }
54
55 virtual CallDialer *getDialer() = 0;
56
57 void print(std::ostream &os);
58
59 /// remove us from the queue; we are head unless we are queued after prev
60 void dequeue(AsyncCall::Pointer &head, AsyncCall::Pointer &prev);
61
62 void setNext(AsyncCall::Pointer aNext) {
63 theNext = aNext;
64 }
65
66 AsyncCall::Pointer &Next() {
67 return theNext;
68 }
69
70 public:
71 const char *const name;
72
73 /// what the callee is expected to work on
74 CodeContext::Pointer codeContext;
75
76 const int debugSection;
77 const int debugLevel;
78 const InstanceId<AsyncCall> id;
79
80 protected:
81 virtual bool canFire();
82
83 virtual void fire() = 0;
84
85 AsyncCall::Pointer theNext; ///< for AsyncCallList and similar lists
86
87 private:
88 const char *isCanceled; // set to the cancellation reason by cancel()
89
90 // not implemented to prevent nil calls from being passed around and unknowingly scheduled, for now.
91 AsyncCall();
92 AsyncCall(const AsyncCall &);
93 };
94
95 inline
96 std::ostream &operator <<(std::ostream &os, AsyncCall &call)
97 {
98 call.print(os);
99 return os;
100 }
101
102 /**
103 \ingroup AsyncCallAPI
104 * Interface for all async call dialers
105 */
106 class CallDialer
107 {
108 public:
109 CallDialer() {}
110 virtual ~CallDialer() {}
111
112 // TODO: Add these for clarity when CommCbFunPtrCallT is gone
113 //virtual bool canDial(AsyncCall &call) = 0;
114 //virtual void dial(AsyncCall &call) = 0;
115
116 virtual void print(std::ostream &os) const = 0;
117 };
118
119 /**
120 \ingroup AsyncCallAPI
121 * This template implements an AsyncCall using a specified Dialer class
122 */
123 template <class DialerClass>
124 class AsyncCallT: public AsyncCall
125 {
126 public:
127 using Dialer = DialerClass;
128
129 AsyncCallT(int aDebugSection, int aDebugLevel, const char *aName,
130 const Dialer &aDialer): AsyncCall(aDebugSection, aDebugLevel, aName),
131 dialer(aDialer) {}
132
133 AsyncCallT(const AsyncCallT<Dialer> &o):
134 AsyncCall(o.debugSection, o.debugLevel, o.name),
135 dialer(o.dialer) {}
136
137 ~AsyncCallT() override {}
138
139 CallDialer *getDialer() override { return &dialer; }
140
141 Dialer dialer;
142
143 protected:
144 bool canFire() override {
145 return AsyncCall::canFire() &&
146 dialer.canDial(*this);
147 }
148 void fire() override { dialer.dial(*this); }
149
150 private:
151 AsyncCallT & operator=(const AsyncCallT &); // not defined. call assignments not permitted.
152 };
153
154 template <class Dialer>
155 RefCount< AsyncCallT<Dialer> >
156 asyncCall(int aDebugSection, int aDebugLevel, const char *aName,
157 const Dialer &aDialer)
158 {
159 return new AsyncCallT<Dialer>(aDebugSection, aDebugLevel, aName, aDialer);
160 }
161
162 /** Call scheduling helper. Use ScheduleCallHere if you can. */
163 bool ScheduleCall(const char *fileName, int fileLine, const AsyncCall::Pointer &);
164
165 /** Call scheduling helper. */
166 #define ScheduleCallHere(call) ScheduleCall(__FILE__, __LINE__, (call))
167
168 #endif /* SQUID_SRC_BASE_ASYNCCALL_H */
169