]> git.ipfire.org Git - thirdparty/squid.git/blob - src/base/AsyncJob.cc
Merge from trunk
[thirdparty/squid.git] / src / base / AsyncJob.cc
1 /*
2 * DEBUG: section 93 ICAP (RFC 3507) Client
3 */
4
5 #include "squid.h"
6 #include "base/AsyncCall.h"
7 #include "base/AsyncJob.h"
8 #include "base/TextException.h"
9 #include "cbdata.h"
10 #include "MemBuf.h"
11
12
13 unsigned int AsyncJob::TheLastId = 0;
14
15 AsyncJob *AsyncJob::AsyncStart(AsyncJob *job)
16 {
17 assert(job);
18 CallJobHere(93, 5, job, AsyncJob::noteStart);
19 return job;
20 }
21
22 AsyncJob::AsyncJob(const char *aTypeName): typeName(aTypeName), inCall(NULL), id(++TheLastId)
23 {
24 debugs(93,3, "AsyncJob of type " << typeName << " constructed, this=" << this <<
25 " [async" << id << ']');
26 }
27
28 AsyncJob::~AsyncJob()
29 {
30 debugs(93,3, "AsyncJob of type " << typeName << " destructed, this=" << this <<
31 " [async" << id << ']');
32 }
33
34 void AsyncJob::noteStart()
35 {
36 start();
37 }
38
39 void AsyncJob::start()
40 {
41 }
42
43 // XXX: temporary code to replace calls to "delete this" in jobs-in-transition.
44 // Will be replaced with calls to mustStop() when transition is complete.
45 void AsyncJob::deleteThis(const char *aReason)
46 {
47 Must(aReason);
48 stopReason = aReason;
49 if (inCall != NULL) {
50 // if we are in-call, then the call wrapper will delete us
51 debugs(93, 4, typeName << " will NOT delete in-call job, reason: " << stopReason);
52 return;
53 }
54
55 // there is no call wrapper waiting for our return, so we fake it
56 debugs(93, 5, typeName << " will delete this, reason: " << stopReason);
57 AsyncCall::Pointer fakeCall = asyncCall(93,4, "FAKE-deleteThis",
58 MemFun(this, &AsyncJob::deleteThis, aReason));
59 inCall = fakeCall;
60 callEnd();
61 // delete fakeCall;
62 }
63
64 void AsyncJob::mustStop(const char *aReason)
65 {
66 // XXX: temporary code to catch cases where mustStop is called outside
67 // of an async call context. Will be removed when that becomes impossible.
68 // Until then, this will cause memory leaks and possibly other problems.
69 if (!inCall) {
70 stopReason = aReason;
71 debugs(93, 5, typeName << " will STALL, reason: " << stopReason);
72 return;
73 }
74
75 Must(inCall != NULL); // otherwise nobody will delete us if we are done()
76 Must(aReason);
77 if (!stopReason) {
78 stopReason = aReason;
79 debugs(93, 5, typeName << " will stop, reason: " << stopReason);
80 } else {
81 debugs(93, 5, typeName << " will stop, another reason: " << aReason);
82 }
83 }
84
85 bool AsyncJob::done() const
86 {
87 // stopReason, set in mustStop(), overwrites all other conditions
88 return stopReason != NULL || doneAll();
89 }
90
91 bool AsyncJob::doneAll() const
92 {
93 return true; // so that it is safe for kids to use
94 }
95
96 bool AsyncJob::canBeCalled(AsyncCall &call) const
97 {
98 if (inCall != NULL) {
99 // This may happen when we have bugs or some module is not calling
100 // us asynchronously (comm used to do that).
101 debugs(93, 5, HERE << inCall << " is in progress; " <<
102 call << " canot reenter the job.");
103 return call.cancel("reentrant job call");
104 }
105
106 return true;
107 }
108
109 void AsyncJob::callStart(AsyncCall &call)
110 {
111 // we must be called asynchronously and hence, the caller must lock us
112 Must(cbdataReferenceValid(toCbdata()));
113
114 Must(!inCall); // see AsyncJob::canBeCalled
115
116 inCall = &call; // XXX: ugly, but safe if callStart/callEnd,Ex are paired
117 debugs(inCall->debugSection, inCall->debugLevel,
118 typeName << " status in:" << status());
119 }
120
121 void AsyncJob::callException(const std::exception &e)
122 {
123 // we must be called asynchronously and hence, the caller must lock us
124 Must(cbdataReferenceValid(toCbdata()));
125
126 mustStop("exception");
127 }
128
129 void AsyncJob::callEnd()
130 {
131 if (done()) {
132 debugs(93, 5, *inCall << " ends job" << status());
133
134 AsyncCall::Pointer inCallSaved = inCall;
135 void *thisSaved = this;
136
137 swanSong();
138
139 delete this; // this is the only place where the object is deleted
140
141 // careful: this object does not exist any more
142 debugs(93, 6, HERE << *inCallSaved << " ended " << thisSaved);
143 return;
144 }
145
146 debugs(inCall->debugSection, inCall->debugLevel,
147 typeName << " status out:" << status());
148 inCall = NULL;
149 }
150
151 // returns a temporary string depicting transaction status, for debugging
152 const char *AsyncJob::status() const
153 {
154 static MemBuf buf;
155 buf.reset();
156
157 buf.append(" [", 2);
158 if (stopReason != NULL) {
159 buf.Printf("Stopped, reason:");
160 buf.Printf("%s",stopReason);
161 }
162 buf.Printf(" job%d]", id);
163 buf.terminate();
164
165 return buf.content();
166 }
167
168
169 /* JobDialer */
170
171 JobDialer::JobDialer(AsyncJob *aJob): job(NULL), lock(NULL)
172 {
173 if (aJob) {
174 lock = cbdataReference(aJob->toCbdata());
175 job = aJob;
176 }
177 }
178
179 JobDialer::JobDialer(const JobDialer &d): CallDialer(d),
180 job(NULL), lock(NULL)
181 {
182 if (d.lock && cbdataReferenceValid(d.lock)) {
183 lock = cbdataReference(d.lock);
184 Must(d.job);
185 job = d.job;
186 }
187 }
188
189 JobDialer::~JobDialer()
190 {
191 cbdataReferenceDone(lock); // lock may be NULL
192 }
193
194
195 bool
196 JobDialer::canDial(AsyncCall &call)
197 {
198 if (!lock)
199 return call.cancel("job was gone before the call");
200
201 if (!cbdataReferenceValid(lock))
202 return call.cancel("job gone after the call");
203
204 Must(job);
205 return job->canBeCalled(call);
206 }
207
208 void
209 JobDialer::dial(AsyncCall &call)
210 {
211 Must(lock && cbdataReferenceValid(lock)); // canDial() checks for this
212 Must(job);
213
214 job->callStart(call);
215
216 try {
217 doDial();
218 } catch (const std::exception &e) {
219 debugs(call.debugSection, 3,
220 HERE << call.name << " threw exception: " << e.what());
221 job->callException(e);
222 }
223
224 job->callEnd(); // may delete job
225 }