]> git.ipfire.org Git - thirdparty/squid.git/blame - src/event.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / event.cc
CommitLineData
48f44632 1/*
bde978a6 2 * Copyright (C) 1996-2015 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 "compat/drand48.h"
27bc2077 13#include "event.h"
8822ebee 14#include "mgr/Registration.h"
582c2af2 15#include "profiler/Profiler.h"
602d9612
A
16#include "SquidTime.h"
17#include "Store.h"
5bed43d6 18#include "tools.h"
48f44632 19
074d6a40 20#include <cmath>
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
d5f8d05f
FC
91ev_entry::ev_entry(char const * aName, EVH * aFunction, void * aArgument, double evWhen,
92 int aWeight, bool haveArgument) : name(aName), func(aFunction),
f53969cc
SM
93 arg(haveArgument ? cbdataReference(aArgument) : aArgument), when(evWhen), weight(aWeight),
94 cbdata(haveArgument)
5dee3116 95{
96}
97
98ev_entry::~ev_entry()
99{
100 if (cbdata)
101 cbdataReferenceDone(arg);
102}
a553a5a3 103
48f44632 104void
601af4c6 105eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
48f44632 106{
a553a5a3 107 EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata);
48f44632 108}
109
8f3db324 110/* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
111void
52040193 112eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight)
8f3db324 113{
52040193 114 if (delta_ish >= 3.0) {
62e76326 115 const double two_third = (2.0 * delta_ish) / 3.0;
116 delta_ish = two_third + (drand48() * two_third);
117 /*
118 * I'm sure drand48() isn't portable. Tell me what function
119 * you have that returns a random double value in the range 0,1.
120 */
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
144void
145eventFreeMemory(void)
146{
147 EventScheduler::GetInstance()->clean();
148}
149
150int
151eventFind(EVH * func, void *arg)
152{
153 return EventScheduler::GetInstance()->find(func, arg);
154}
155
5dee3116 156EventScheduler EventScheduler::_instance;
a553a5a3 157
5dee3116 158EventScheduler::EventScheduler(): tasks(NULL)
a553a5a3 159{}
160
161EventScheduler::~EventScheduler()
162{
163 clean();
164}
165
166void
167EventScheduler::cancel(EVH * func, void *arg)
168{
169 ev_entry **E;
170 ev_entry *event;
62e76326 171
79d39a72 172 for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) {
62e76326 173 if (event->func != func)
174 continue;
175
aa93f210 176 if (arg && event->arg != arg)
62e76326 177 continue;
178
179 *E = event->next;
180
62ee09ca 181 delete event;
62e76326 182
26ac0430
AJ
183 if (arg)
184 return;
185 /*
186 * DPW 2007-04-12
187 * Since this method may now delete multiple events (when
188 * arg is NULL) it no longer returns after a deletion and
189 * we have a potential NULL pointer problem. If we just
190 * deleted the last event in the list then *E is now equal
191 * to NULL. We need to break here or else we'll get a NULL
192 * pointer dereference in the last clause of the for loop.
193 */
194 if (NULL == *E)
195 break;
93775f90 196 }
62e76326 197
aa93f210 198 if (arg)
26ac0430 199 debug_trap("eventDelete: event not found");
93775f90 200}
201
aa14d759
AR
202// The event API does not guarantee exact timing, but guarantees that no event
203// is fired before it is due. We may delay firing, but never fire too early.
a553a5a3 204int
aa14d759 205EventScheduler::timeRemaining() const
a553a5a3 206{
207 if (!tasks)
aa839030 208 return EVENT_IDLE;
a553a5a3 209
aa14d759
AR
210 if (tasks->when <= current_dtime) // we are on time or late
211 return 0; // fire the event ASAP
8ff3fa2e 212
aa14d759
AR
213 const double diff = tasks->when - current_dtime; // microseconds
214 // Round UP: If we come back a nanosecond earlier, we will wait again!
215 const int timeLeft = static_cast<int>(ceil(1000*diff)); // milliseconds
216 // Avoid hot idle: A series of rapid select() calls with zero timeout.
217 const int minDelay = 1; // millisecond
218 return max(minDelay, timeLeft);
a553a5a3 219}
220
221int
ced8def3 222EventScheduler::checkEvents(int)
48f44632 223{
aa14d759
AR
224 int result = timeRemaining();
225 if (result != 0)
226 return result;
62e76326 227
1d5161bd 228 PROF_start(eventRun);
229
aa14d759
AR
230 do {
231 ev_entry *event = tasks;
232 assert(event);
62e76326 233
5dee3116 234 /* XXX assumes event->name is static memory! */
235 AsyncCall::Pointer call = asyncCall(41,5, event->name,
26ac0430 236 EventDialer(event->func, event->arg, event->cbdata));
5dee3116 237 ScheduleCallHere(call);
238
239 last_event_ran = event->name; // XXX: move this to AsyncCallQueue
240 const bool heavy = event->weight &&
26ac0430 241 (!event->cbdata || cbdataReferenceValid(event->arg));
62e76326 242
243 tasks = event->next;
5dee3116 244 delete event;
62e76326 245
aa14d759
AR
246 result = timeRemaining();
247
5dee3116 248 // XXX: We may be called again during the same event loop iteration.
249 // Is there a point in breaking now?
26ac0430 250 if (heavy)
5dee3116 251 break; // do not dequeue events following a heavy event
aa14d759 252 } while (result == 0);
1d5161bd 253
254 PROF_stop(eventRun);
aa14d759 255 return result;
48f44632 256}
257
a553a5a3 258void
259EventScheduler::clean()
48f44632 260{
a553a5a3 261 while (ev_entry * event = tasks) {
262 tasks = event->next;
a553a5a3 263 delete event;
264 }
265
266 tasks = NULL;
4ba55996 267}
268
a553a5a3 269void
270EventScheduler::dump(StoreEntry * sentry)
4ba55996 271{
62e76326 272
cb9f32a9 273 ev_entry *e = tasks;
62e76326 274
236b1f2a 275 if (last_event_ran)
62e76326 276 storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran);
277
cc192b50 278 storeAppendPrintf(sentry, "%-25s\t%-15s\t%s\t%s\n",
62e76326 279 "Operation",
280 "Next Execution",
281 "Weight",
282 "Callback Valid?");
283
4ba55996 284 while (e != NULL) {
cc192b50 285 storeAppendPrintf(sentry, "%-25s\t%0.3f sec\t%5d\t %s\n",
32f10aa5 286 e->name, e->when ? e->when - current_dtime : 0, e->weight,
f53969cc 287 (e->arg && e->cbdata) ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A");
62e76326 288 e = e->next;
4ba55996 289 }
290}
f1fc2a8d 291
a553a5a3 292bool
293EventScheduler::find(EVH * func, void * arg)
f1fc2a8d 294{
62e76326 295
cb9f32a9 296 ev_entry *event;
62e76326 297
a553a5a3 298 for (event = tasks; event != NULL; event = event->next) {
299 if (event->func == func && event->arg == arg)
300 return true;
f1fc2a8d 301 }
62e76326 302
a553a5a3 303 return false;
f1fc2a8d 304}
46ca5fc6 305
a553a5a3 306EventScheduler *
307EventScheduler::GetInstance()
46ca5fc6 308{
a553a5a3 309 return &_instance;
310}
62e76326 311
a553a5a3 312void
313EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
314{
a5a02499 315 // Use zero timestamp for when=0 events: Many of them are async calls that
316 // must fire in the submission order. We cannot use current_dtime for them
317 // because it may decrease if system clock is adjusted backwards.
318 const double timestamp = when > 0.0 ? current_dtime + when : 0;
cb9f32a9 319 ev_entry *event = new ev_entry(name, func, arg, timestamp, weight, cbdata);
a553a5a3 320
cb9f32a9 321 ev_entry **E;
d65986ae 322 debugs(41, 7, HERE << "schedule: Adding '" << name << "', in " << when << " seconds");
a553a5a3 323 /* Insert after the last event with the same or earlier time */
324
325 for (E = &tasks; *E; E = &(*E)->next) {
326 if ((*E)->when > event->when)
327 break;
46ca5fc6 328 }
62e76326 329
a553a5a3 330 event->next = *E;
331 *E = event;
46ca5fc6 332}
f53969cc 333