]> git.ipfire.org Git - thirdparty/squid.git/blob - src/base/AsyncJob.cc
11b2e6e3d614d63f92c9a2f1164040fa8ba7c71f
[thirdparty/squid.git] / src / base / AsyncJob.cc
1 /*
2 * Copyright (C) 1996-2022 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 /* DEBUG: section 93 ICAP (RFC 3507) Client */
10
11 #include "squid.h"
12 #include "base/AsyncCall.h"
13 #include "base/AsyncJob.h"
14 #include "base/AsyncJobCalls.h"
15 #include "base/TextException.h"
16 #include "cbdata.h"
17 #include "MemBuf.h"
18
19 #include <ostream>
20
21 InstanceIdDefinitions(AsyncJob, "job");
22
23 void
24 AsyncJob::Start(const Pointer &job)
25 {
26 CallJobHere(93, 5, job, AsyncJob, start);
27 job->started_ = true; // it is the attempt that counts
28 }
29
30 AsyncJob::AsyncJob(const char *aTypeName) :
31 stopReason(nullptr), typeName(aTypeName), inCall(nullptr)
32 {
33 debugs(93,5, "AsyncJob constructed, this=" << this <<
34 " type=" << typeName << " [" << id << ']');
35 }
36
37 AsyncJob::~AsyncJob()
38 {
39 debugs(93,5, "AsyncJob destructed, this=" << this <<
40 " type=" << typeName << " [" << id << ']');
41 assert(!started_ || swanSang_);
42 }
43
44 void AsyncJob::start()
45 {
46 }
47
48 // XXX: temporary code to replace calls to "delete this" in jobs-in-transition.
49 // Will be replaced with calls to mustStop() when transition is complete.
50 void AsyncJob::deleteThis(const char *aReason)
51 {
52 Must(aReason);
53 stopReason = aReason;
54 if (inCall != nullptr) {
55 // if we are in-call, then the call wrapper will delete us
56 debugs(93, 4, typeName << " will NOT delete in-call job, reason: " << stopReason);
57 return;
58 }
59
60 // there is no call wrapper waiting for our return, so we fake it
61 debugs(93, 5, typeName << " will delete this, reason: " << stopReason);
62 CbcPointer<AsyncJob> self(this);
63 AsyncCall::Pointer fakeCall = asyncCall(93,4, "FAKE-deleteThis",
64 JobMemFun(self, &AsyncJob::deleteThis, aReason));
65 inCall = fakeCall;
66 callEnd();
67 // delete fakeCall;
68 }
69
70 void AsyncJob::mustStop(const char *aReason)
71 {
72 // XXX: temporary code to catch cases where mustStop is called outside
73 // of an async call context. Will be removed when that becomes impossible.
74 // Until then, this will cause memory leaks and possibly other problems.
75 if (!inCall) {
76 stopReason = aReason;
77 debugs(93, 5, typeName << " will STALL, reason: " << stopReason);
78 return;
79 }
80
81 Must(inCall != nullptr); // otherwise nobody will delete us if we are done()
82 Must(aReason);
83 if (!stopReason) {
84 stopReason = aReason;
85 debugs(93, 5, typeName << " will stop, reason: " << stopReason);
86 } else {
87 debugs(93, 5, typeName << " will stop, another reason: " << aReason);
88 }
89 }
90
91 bool AsyncJob::done() const
92 {
93 // stopReason, set in mustStop(), overwrites all other conditions
94 return stopReason != nullptr || doneAll();
95 }
96
97 bool AsyncJob::doneAll() const
98 {
99 return true; // so that it is safe for kids to use
100 }
101
102 bool AsyncJob::canBeCalled(AsyncCall &call) const
103 {
104 if (inCall != nullptr) {
105 // This may happen when we have bugs or some module is not calling
106 // us asynchronously (comm used to do that).
107 debugs(93, 5, inCall << " is in progress; " <<
108 call << " cannot reenter the job.");
109 return call.cancel("reentrant job call");
110 }
111
112 return true;
113 }
114
115 void AsyncJob::callStart(AsyncCall &call)
116 {
117 // we must be called asynchronously and hence, the caller must lock us
118 Must(cbdataReferenceValid(toCbdata()));
119
120 Must(!inCall); // see AsyncJob::canBeCalled
121
122 inCall = &call; // XXX: ugly, but safe if callStart/callEnd,Ex are paired
123 debugs(inCall->debugSection, inCall->debugLevel,
124 typeName << " status in:" << status());
125 }
126
127 void
128 AsyncJob::callException(const std::exception &ex)
129 {
130 debugs(93, 2, ex.what());
131 // we must be called asynchronously and hence, the caller must lock us
132 Must(cbdataReferenceValid(toCbdata()));
133
134 mustStop("exception");
135 }
136
137 void AsyncJob::callEnd()
138 {
139 if (done()) {
140 debugs(93, 5, *inCall << " ends job" << status());
141
142 AsyncCall::Pointer inCallSaved = inCall;
143 void *thisSaved = this;
144
145 // TODO: Swallow swanSong() exceptions to reduce memory leaks.
146
147 // Job callback invariant: swanSong() is (only) called for started jobs.
148 // Here to detect violations in kids that forgot to call our swanSong().
149 assert(started_);
150
151 swanSang_ = true; // it is the attempt that counts
152 swanSong();
153
154 delete this; // this is the only place where a started job is deleted
155
156 // careful: this object does not exist any more
157 debugs(93, 6, *inCallSaved << " ended " << thisSaved);
158 return;
159 }
160
161 debugs(inCall->debugSection, inCall->debugLevel,
162 typeName << " status out:" << status());
163 inCall = nullptr;
164 }
165
166 // returns a temporary string depicting transaction status, for debugging
167 const char *AsyncJob::status() const
168 {
169 static MemBuf buf;
170 buf.reset();
171
172 buf.append(" [", 2);
173 if (stopReason != nullptr) {
174 buf.appendf("Stopped, reason:%s", stopReason);
175 }
176 buf.appendf(" %s%u]", id.prefix(), id.value);
177 buf.terminate();
178
179 return buf.content();
180 }
181