AsyncJob.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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/PackableStream.h"
16 #include "base/TextException.h"
17 #include "cbdata.h"
18 #include "mem/PoolingAllocator.h"
19 #include "MemBuf.h"
20 #include "mgr/Registration.h"
21 #include "Store.h"
22 
23 #include <ostream>
24 #include <unordered_set>
25 
27 
29 static auto &
31 {
32  static const auto jobs = new std::unordered_set<AsyncJob *, std::hash<AsyncJob *>, std::equal_to<AsyncJob *>, PoolingAllocator<AsyncJob *> >();
33  return *jobs;
34 }
35 
36 void
38 {
39  CallJobHere(93, 5, job, AsyncJob, start);
40  job->started_ = true; // it is the attempt that counts
41 }
42 
43 AsyncJob::AsyncJob(const char *aTypeName) :
44  stopReason(nullptr), typeName(aTypeName), inCall(nullptr)
45 {
46  debugs(93,5, "AsyncJob constructed, this=" << this <<
47  " type=" << typeName << " [" << id << ']');
48  AllJobs().insert(this);
49 }
50 
52 {
53  debugs(93,5, "AsyncJob destructed, this=" << this <<
54  " type=" << typeName << " [" << id << ']');
56  AllJobs().erase(this);
57 }
58 
60 {
61 }
62 
63 // XXX: temporary code to replace calls to "delete this" in jobs-in-transition.
64 // Will be replaced with calls to mustStop() when transition is complete.
65 void AsyncJob::deleteThis(const char *aReason)
66 {
67  Must(aReason);
68  stopReason = aReason;
69  if (inCall != nullptr) {
70  // if we are in-call, then the call wrapper will delete us
71  debugs(93, 4, typeName << " will NOT delete in-call job, reason: " << stopReason);
72  return;
73  }
74 
75  // there is no call wrapper waiting for our return, so we fake it
76  debugs(93, 5, typeName << " will delete this, reason: " << stopReason);
77  CbcPointer<AsyncJob> self(this);
78  AsyncCall::Pointer fakeCall = asyncCall(93,4, "FAKE-deleteThis",
79  JobMemFun(self, &AsyncJob::deleteThis, aReason));
80  inCall = fakeCall;
81  callEnd();
82 // delete fakeCall;
83 }
84 
85 void AsyncJob::mustStop(const char *aReason)
86 {
87  // XXX: temporary code to catch cases where mustStop is called outside
88  // of an async call context. Will be removed when that becomes impossible.
89  // Until then, this will cause memory leaks and possibly other problems.
90  if (!inCall) {
91  stopReason = aReason;
92  debugs(93, 5, typeName << " will STALL, reason: " << stopReason);
93  return;
94  }
95 
96  Must(inCall != nullptr); // otherwise nobody will delete us if we are done()
97  Must(aReason);
98  if (!stopReason) {
99  stopReason = aReason;
100  debugs(93, 5, typeName << " will stop, reason: " << stopReason);
101  } else {
102  debugs(93, 5, typeName << " will stop, another reason: " << aReason);
103  }
104 }
105 
106 bool AsyncJob::done() const
107 {
108  // stopReason, set in mustStop(), overwrites all other conditions
109  return stopReason != nullptr || doneAll();
110 }
111 
112 bool AsyncJob::doneAll() const
113 {
114  return true; // so that it is safe for kids to use
115 }
116 
118 {
119  if (inCall != nullptr) {
120  // This may happen when we have bugs or some module is not calling
121  // us asynchronously (comm used to do that).
122  debugs(93, 5, inCall << " is in progress; " <<
123  call << " cannot reenter the job.");
124  return call.cancel("reentrant job call");
125  }
126 
127  return true;
128 }
129 
131 {
132  // we must be called asynchronously and hence, the caller must lock us
134 
135  Must(!inCall); // see AsyncJob::canBeCalled
136 
137  inCall = &call; // XXX: ugly, but safe if callStart/callEnd,Ex are paired
139  typeName << " status in:" << status());
140 }
141 
142 void
143 AsyncJob::callException(const std::exception &ex)
144 {
145  debugs(93, 2, ex.what());
146  // we must be called asynchronously and hence, the caller must lock us
148 
149  mustStop("exception");
150 }
151 
153 {
154  if (done()) {
155  debugs(93, 5, *inCall << " ends job" << status());
156 
157  AsyncCall::Pointer inCallSaved = inCall;
158  void *thisSaved = this;
159 
160  // TODO: Swallow swanSong() exceptions to reduce memory leaks.
161 
162  // Job callback invariant: swanSong() is (only) called for started jobs.
163  // Here to detect violations in kids that forgot to call our swanSong().
164  assert(started_);
165 
166  swanSang_ = true; // it is the attempt that counts
167  swanSong();
168 
169  delete this; // this is the only place where a started job is deleted
170 
171  // careful: this object does not exist any more
172  debugs(93, 6, *inCallSaved << " ended " << thisSaved);
173  return;
174  }
175 
177  typeName << " status out:" << status());
178  inCall = nullptr;
179 }
180 
181 // returns a temporary string depicting transaction status, for debugging
182 const char *AsyncJob::status() const
183 {
184  static MemBuf buf;
185  buf.reset();
186 
187  buf.append(" [", 2);
188  if (stopReason != nullptr) {
189  buf.appendf("Stopped, reason:%s", stopReason);
190  }
191  buf.appendf(" %s%u]", id.prefix(), id.value);
192  buf.terminate();
193 
194  return buf.content();
195 }
196 
197 void
199 {
200  PackableStream os(*e);
201  // this loop uses YAML syntax, but AsyncJob::status() still needs to be adjusted to use YAML
202  const char *indent = " ";
203  for (const auto job: AllJobs()) {
204  os << indent << job->id << ":\n";
205  os << indent << indent << "type: '" << job->typeName << "'\n";
206  os << indent << indent << "status:" << job->status() << '\n';
207  if (!job->started_)
208  os << indent << indent << "started: false\n";
209  if (job->stopReason)
210  os << indent << indent << "stopped: '" << job->stopReason << "'\n";
211  }
212 }
213 
214 void
216 {
217  Mgr::RegisterAction("jobs", "All AsyncJob objects", &AsyncJob::ReportAllJobs, 0, 1);
218 }
219 
bool cancel(const char *reason)
Definition: AsyncCall.cc:56
void terminate()
Definition: MemBuf.cc:241
bool canBeCalled(AsyncCall &call) const
whether we can be called
Definition: AsyncJob.cc:117
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
bool started_
Start() has finished successfully.
Definition: AsyncJob.h:88
AsyncCall::Pointer inCall
the asynchronous call being handled, if any
Definition: AsyncJob.h:86
virtual void callEnd()
called right after the called job method
Definition: AsyncJob.cc:152
virtual void swanSong()
Definition: AsyncJob.h:61
const char * stopReason
reason for forcing done() to be true
Definition: AsyncJob.h:84
int cbdataReferenceValid(const void *p)
Definition: cbdata.cc:270
AsyncJob(const char *aTypeName)
Definition: AsyncJob.cc:43
~AsyncJob() override
Definition: AsyncJob.cc:51
RefCount< AsyncCallT< Dialer > > asyncCall(int aDebugSection, int aDebugLevel, const char *aName, const Dialer &aDialer)
Definition: AsyncCall.h:156
virtual void * toCbdata()=0
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
virtual bool doneAll() const
whether positive goal has been reached
Definition: AsyncJob.cc:112
const int debugLevel
Definition: AsyncCall.h:77
void mustStop(const char *aReason)
Definition: AsyncJob.cc:85
Definition: MemBuf.h:23
const int debugSection
Definition: AsyncCall.h:76
#define CallJobHere(debugSection, debugLevel, job, Class, method)
Definition: AsyncJobCalls.h:59
NullaryMemFunT< C > JobMemFun(const CbcPointer< C > &job, typename NullaryMemFunT< C >::Method method)
#define assert(EX)
Definition: assert.h:17
bool swanSang_
swanSong() was called
Definition: AsyncJob.h:89
static void RegisterWithCacheManager()
Definition: AsyncJob.cc:215
InstanceIdDefinitions(AsyncJob, "job")
const char * typeName
kid (leaf) class name, for debugging
Definition: AsyncJob.h:85
bool done() const
the job is destroyed in callEnd() when done()
Definition: AsyncJob.cc:106
virtual const char * status() const
internal cleanup; do not call directly
Definition: AsyncJob.cc:182
void deleteThis(const char *aReason)
Definition: AsyncJob.cc:65
static void ReportAllJobs(StoreEntry *)
writes a cache manager report about all jobs existing in this worker
Definition: AsyncJob.cc:198
void callStart(AsyncCall &call)
Definition: AsyncJob.cc:130
static auto & AllJobs()
a set of all AsyncJob objects in existence
Definition: AsyncJob.cc:30
char * content()
start of the added data
Definition: MemBuf.h:41
STL Allocator that uses Squid memory pools for memory management.
void RegisterAction(char const *action, char const *desc, OBJH *handler, Protected, Atomic, Format)
Definition: Registration.cc:54
virtual void start()
called by AsyncStart; do not call directly
Definition: AsyncJob.cc:59
#define Must(condition)
Definition: TextException.h:75
void reset()
Definition: MemBuf.cc:129
virtual void callException(const std::exception &e)
called when the job throws during an async call
Definition: AsyncJob.cc:143
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static void Start(const Pointer &job)
Definition: AsyncJob.cc:37

 

Introduction

Documentation

Support

Miscellaneous