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