]> git.ipfire.org Git - thirdparty/squid.git/blame - src/event.cc
Maintenance: automate header guards 2/3 (#1655)
[thirdparty/squid.git] / src / event.cc
CommitLineData
48f44632 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
48f44632 7 */
8
bbc27441
AJ
9/* DEBUG: section 41 Event Processing */
10
f7f3304a 11#include "squid.h"
4a28fc55 12#include "base/Random.h"
27bc2077 13#include "event.h"
8822ebee 14#include "mgr/Registration.h"
602d9612 15#include "Store.h"
5bed43d6 16#include "tools.h"
48f44632 17
074d6a40 18#include <cmath>
aa14d759 19
48f44632 20/* The list of event processes */
62e76326 21
4ba55996 22static OBJH eventDump;
aee3523a 23static const char *last_event_ran = nullptr;
48f44632 24
26ac0430 25// This AsyncCall dialer can be configured to check that the event cbdata is
5dee3116 26// valid before calling the event handler
27class EventDialer: public CallDialer
28{
29public:
30 typedef CallDialer Parent;
31
32 EventDialer(EVH *aHandler, void *anArg, bool lockedArg);
33 EventDialer(const EventDialer &d);
337b9aa4 34 ~EventDialer() override;
5dee3116 35
337b9aa4 36 void print(std::ostream &os) const override;
5dee3116 37 virtual bool canDial(AsyncCall &call);
38
39 void dial(AsyncCall &) { theHandler(theArg); }
40
41private:
42 EVH *theHandler;
43 void *theArg;
44 bool isLockedArg;
45};
46
47EventDialer::EventDialer(EVH *aHandler, void *anArg, bool lockedArg):
f53969cc 48 theHandler(aHandler), theArg(anArg), isLockedArg(lockedArg)
5dee3116 49{
50 if (isLockedArg)
451bde6d 51 (void)cbdataReference(theArg);
5dee3116 52}
53
54EventDialer::EventDialer(const EventDialer &d):
f53969cc 55 theHandler(d.theHandler), theArg(d.theArg), isLockedArg(d.isLockedArg)
5dee3116 56{
57 if (isLockedArg)
451bde6d 58 (void)cbdataReference(theArg);
5dee3116 59}
60
61EventDialer::~EventDialer()
62{
63 if (isLockedArg)
64 cbdataReferenceDone(theArg);
65}
66
67bool
26ac0430
AJ
68EventDialer::canDial(AsyncCall &call)
69{
5dee3116 70 // TODO: add Parent::canDial() that always returns true
71 //if (!Parent::canDial())
72 // return false;
73
74 if (isLockedArg && !cbdataReferenceValid(theArg))
75 return call.cancel("stale handler data");
76
77 return true;
78}
79
80void
81EventDialer::print(std::ostream &os) const
82{
83 os << '(';
84 if (theArg)
85 os << theArg << (isLockedArg ? "*?" : "");
86 os << ')';
87}
88
cc8c4af2
AJ
89ev_entry::ev_entry(char const * aName, EVH * aFunction, void * aArgument, double evWhen, int aWeight, bool haveArg) :
90 name(aName),
91 func(aFunction),
92 arg(haveArg ? cbdataReference(aArgument) : aArgument),
93 when(evWhen),
94 weight(aWeight),
95 cbdata(haveArg),
aee3523a 96 next(nullptr)
5dee3116 97{
98}
99
100ev_entry::~ev_entry()
101{
102 if (cbdata)
103 cbdataReferenceDone(arg);
104}
a553a5a3 105
48f44632 106void
601af4c6 107eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
48f44632 108{
a553a5a3 109 EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata);
48f44632 110}
111
8f3db324 112/* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
113void
52040193 114eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight)
8f3db324 115{
52040193 116 if (delta_ish >= 3.0) {
4a28fc55 117 static std::mt19937 rng(RandomSeed32());
f6c935b5 118 auto third = (delta_ish/3.0);
09835feb 119 std::uniform_real_distribution<> thirdIsh(delta_ish - third, delta_ish + third);
f6c935b5 120 delta_ish = thirdIsh(rng);
8f3db324 121 }
62e76326 122
f720985e 123 eventAdd(name, func, arg, delta_ish, weight);
8f3db324 124}
125
93775f90 126void
582b6456 127eventDelete(EVH * func, void *arg)
93775f90 128{
a553a5a3 129 EventScheduler::GetInstance()->cancel(func, arg);
130}
62e76326 131
a553a5a3 132void
15b3c0d7 133eventInit(void)
a553a5a3 134{
8822ebee 135 Mgr::RegisterAction("events", "Event Queue", eventDump, 0, 1);
a553a5a3 136}
62e76326 137
a553a5a3 138static void
139eventDump(StoreEntry * sentry)
140{
141 EventScheduler::GetInstance()->dump(sentry);
142}
143
a553a5a3 144int
145eventFind(EVH * func, void *arg)
146{
147 return EventScheduler::GetInstance()->find(func, arg);
148}
149
5dee3116 150EventScheduler EventScheduler::_instance;
a553a5a3 151
aee3523a 152EventScheduler::EventScheduler(): tasks(nullptr)
a553a5a3 153{}
154
155EventScheduler::~EventScheduler()
156{
157 clean();
158}
159
160void
161EventScheduler::cancel(EVH * func, void *arg)
162{
163 ev_entry **E;
164 ev_entry *event;
62e76326 165
aee3523a 166 for (E = &tasks; (event = *E) != nullptr; E = &(*E)->next) {
62e76326 167 if (event->func != func)
168 continue;
169
aa93f210 170 if (arg && event->arg != arg)
62e76326 171 continue;
172
173 *E = event->next;
174
62ee09ca 175 delete event;
62e76326 176
26ac0430
AJ
177 if (arg)
178 return;
179 /*
180 * DPW 2007-04-12
181 * Since this method may now delete multiple events (when
182 * arg is NULL) it no longer returns after a deletion and
183 * we have a potential NULL pointer problem. If we just
184 * deleted the last event in the list then *E is now equal
185 * to NULL. We need to break here or else we'll get a NULL
186 * pointer dereference in the last clause of the for loop.
187 */
aee3523a 188 if (nullptr == *E)
26ac0430 189 break;
93775f90 190 }
62e76326 191
aa93f210 192 if (arg)
26ac0430 193 debug_trap("eventDelete: event not found");
93775f90 194}
195
aa14d759
AR
196// The event API does not guarantee exact timing, but guarantees that no event
197// is fired before it is due. We may delay firing, but never fire too early.
a553a5a3 198int
aa14d759 199EventScheduler::timeRemaining() const
a553a5a3 200{
201 if (!tasks)
aa839030 202 return EVENT_IDLE;
a553a5a3 203
aa14d759
AR
204 if (tasks->when <= current_dtime) // we are on time or late
205 return 0; // fire the event ASAP
8ff3fa2e 206
aa14d759
AR
207 const double diff = tasks->when - current_dtime; // microseconds
208 // Round UP: If we come back a nanosecond earlier, we will wait again!
209 const int timeLeft = static_cast<int>(ceil(1000*diff)); // milliseconds
210 // Avoid hot idle: A series of rapid select() calls with zero timeout.
211 const int minDelay = 1; // millisecond
212 return max(minDelay, timeLeft);
a553a5a3 213}
214
215int
ced8def3 216EventScheduler::checkEvents(int)
48f44632 217{
aa14d759
AR
218 int result = timeRemaining();
219 if (result != 0)
220 return result;
62e76326 221
aa14d759
AR
222 do {
223 ev_entry *event = tasks;
224 assert(event);
62e76326 225
5dee3116 226 /* XXX assumes event->name is static memory! */
227 AsyncCall::Pointer call = asyncCall(41,5, event->name,
26ac0430 228 EventDialer(event->func, event->arg, event->cbdata));
5dee3116 229 ScheduleCallHere(call);
230
231 last_event_ran = event->name; // XXX: move this to AsyncCallQueue
232 const bool heavy = event->weight &&
26ac0430 233 (!event->cbdata || cbdataReferenceValid(event->arg));
62e76326 234
235 tasks = event->next;
5dee3116 236 delete event;
62e76326 237
aa14d759
AR
238 result = timeRemaining();
239
5dee3116 240 // XXX: We may be called again during the same event loop iteration.
241 // Is there a point in breaking now?
26ac0430 242 if (heavy)
5dee3116 243 break; // do not dequeue events following a heavy event
aa14d759 244 } while (result == 0);
1d5161bd 245
aa14d759 246 return result;
48f44632 247}
248
a553a5a3 249void
250EventScheduler::clean()
48f44632 251{
a553a5a3 252 while (ev_entry * event = tasks) {
253 tasks = event->next;
a553a5a3 254 delete event;
255 }
256
aee3523a 257 tasks = nullptr;
4ba55996 258}
259
a553a5a3 260void
1a51cf72 261EventScheduler::dump(Packable *out)
4ba55996 262{
236b1f2a 263 if (last_event_ran)
1a51cf72
AJ
264 out->appendf("Last event to run: %s\n\n", last_event_ran);
265
266 out->appendf("%-25s\t%-15s\t%s\t%s\n",
267 "Operation",
268 "Next Execution",
269 "Weight",
270 "Callback Valid?");
271
272 for (auto *e = tasks; e; e = e->next) {
273 out->appendf("%-25s\t%0.3f sec\t%5d\t %s\n",
274 e->name, (e->when ? e->when - current_dtime : 0), e->weight,
275 (e->arg && e->cbdata) ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A");
4ba55996 276 }
277}
f1fc2a8d 278
a553a5a3 279bool
280EventScheduler::find(EVH * func, void * arg)
f1fc2a8d 281{
62e76326 282
cb9f32a9 283 ev_entry *event;
62e76326 284
aee3523a 285 for (event = tasks; event != nullptr; event = event->next) {
a553a5a3 286 if (event->func == func && event->arg == arg)
287 return true;
f1fc2a8d 288 }
62e76326 289
a553a5a3 290 return false;
f1fc2a8d 291}
46ca5fc6 292
a553a5a3 293EventScheduler *
294EventScheduler::GetInstance()
46ca5fc6 295{
a553a5a3 296 return &_instance;
297}
62e76326 298
a553a5a3 299void
300EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
301{
a5a02499 302 // Use zero timestamp for when=0 events: Many of them are async calls that
303 // must fire in the submission order. We cannot use current_dtime for them
304 // because it may decrease if system clock is adjusted backwards.
305 const double timestamp = when > 0.0 ? current_dtime + when : 0;
cb9f32a9 306 ev_entry *event = new ev_entry(name, func, arg, timestamp, weight, cbdata);
a553a5a3 307
cb9f32a9 308 ev_entry **E;
bf95c10a 309 debugs(41, 7, "schedule: Adding '" << name << "', in " << when << " seconds");
a553a5a3 310 /* Insert after the last event with the same or earlier time */
311
312 for (E = &tasks; *E; E = &(*E)->next) {
313 if ((*E)->when > event->when)
314 break;
46ca5fc6 315 }
62e76326 316
a553a5a3 317 event->next = *E;
318 *E = event;
46ca5fc6 319}
f53969cc 320