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