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