]> git.ipfire.org Git - thirdparty/squid.git/blob - src/event.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / event.cc
1 /*
2 * Copyright (C) 1996-2015 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 41 Event Processing */
10
11 #include "squid.h"
12 #include "compat/drand48.h"
13 #include "event.h"
14 #include "mgr/Registration.h"
15 #include "profiler/Profiler.h"
16 #include "SquidTime.h"
17 #include "Store.h"
18 #include "tools.h"
19
20 #include <cmath>
21
22 /* The list of event processes */
23
24 static OBJH eventDump;
25 static const char *last_event_ran = NULL;
26
27 // This AsyncCall dialer can be configured to check that the event cbdata is
28 // valid before calling the event handler
29 class EventDialer: public CallDialer
30 {
31 public:
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
43 private:
44 EVH *theHandler;
45 void *theArg;
46 bool isLockedArg;
47 };
48
49 EventDialer::EventDialer(EVH *aHandler, void *anArg, bool lockedArg):
50 theHandler(aHandler), theArg(anArg), isLockedArg(lockedArg)
51 {
52 if (isLockedArg)
53 (void)cbdataReference(theArg);
54 }
55
56 EventDialer::EventDialer(const EventDialer &d):
57 theHandler(d.theHandler), theArg(d.theArg), isLockedArg(d.isLockedArg)
58 {
59 if (isLockedArg)
60 (void)cbdataReference(theArg);
61 }
62
63 EventDialer::~EventDialer()
64 {
65 if (isLockedArg)
66 cbdataReferenceDone(theArg);
67 }
68
69 bool
70 EventDialer::canDial(AsyncCall &call)
71 {
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
82 void
83 EventDialer::print(std::ostream &os) const
84 {
85 os << '(';
86 if (theArg)
87 os << theArg << (isLockedArg ? "*?" : "");
88 os << ')';
89 }
90
91 ev_entry::ev_entry(char const * aName, EVH * aFunction, void * aArgument, double evWhen,
92 int aWeight, bool haveArgument) : name(aName), func(aFunction),
93 arg(haveArgument ? cbdataReference(aArgument) : aArgument), when(evWhen), weight(aWeight),
94 cbdata(haveArgument)
95 {
96 }
97
98 ev_entry::~ev_entry()
99 {
100 if (cbdata)
101 cbdataReferenceDone(arg);
102 }
103
104 void
105 eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
106 {
107 EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata);
108 }
109
110 /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
111 void
112 eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight)
113 {
114 if (delta_ish >= 3.0) {
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 */
121 }
122
123 eventAdd(name, func, arg, delta_ish, weight);
124 }
125
126 void
127 eventDelete(EVH * func, void *arg)
128 {
129 EventScheduler::GetInstance()->cancel(func, arg);
130 }
131
132 void
133 eventInit(void)
134 {
135 Mgr::RegisterAction("events", "Event Queue", eventDump, 0, 1);
136 }
137
138 static void
139 eventDump(StoreEntry * sentry)
140 {
141 EventScheduler::GetInstance()->dump(sentry);
142 }
143
144 void
145 eventFreeMemory(void)
146 {
147 EventScheduler::GetInstance()->clean();
148 }
149
150 int
151 eventFind(EVH * func, void *arg)
152 {
153 return EventScheduler::GetInstance()->find(func, arg);
154 }
155
156 EventScheduler EventScheduler::_instance;
157
158 EventScheduler::EventScheduler(): tasks(NULL)
159 {}
160
161 EventScheduler::~EventScheduler()
162 {
163 clean();
164 }
165
166 void
167 EventScheduler::cancel(EVH * func, void *arg)
168 {
169 ev_entry **E;
170 ev_entry *event;
171
172 for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) {
173 if (event->func != func)
174 continue;
175
176 if (arg && event->arg != arg)
177 continue;
178
179 *E = event->next;
180
181 delete event;
182
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;
196 }
197
198 if (arg)
199 debug_trap("eventDelete: event not found");
200 }
201
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.
204 int
205 EventScheduler::timeRemaining() const
206 {
207 if (!tasks)
208 return EVENT_IDLE;
209
210 if (tasks->when <= current_dtime) // we are on time or late
211 return 0; // fire the event ASAP
212
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);
219 }
220
221 int
222 EventScheduler::checkEvents(int)
223 {
224 int result = timeRemaining();
225 if (result != 0)
226 return result;
227
228 PROF_start(eventRun);
229
230 do {
231 ev_entry *event = tasks;
232 assert(event);
233
234 /* XXX assumes event->name is static memory! */
235 AsyncCall::Pointer call = asyncCall(41,5, event->name,
236 EventDialer(event->func, event->arg, event->cbdata));
237 ScheduleCallHere(call);
238
239 last_event_ran = event->name; // XXX: move this to AsyncCallQueue
240 const bool heavy = event->weight &&
241 (!event->cbdata || cbdataReferenceValid(event->arg));
242
243 tasks = event->next;
244 delete event;
245
246 result = timeRemaining();
247
248 // XXX: We may be called again during the same event loop iteration.
249 // Is there a point in breaking now?
250 if (heavy)
251 break; // do not dequeue events following a heavy event
252 } while (result == 0);
253
254 PROF_stop(eventRun);
255 return result;
256 }
257
258 void
259 EventScheduler::clean()
260 {
261 while (ev_entry * event = tasks) {
262 tasks = event->next;
263 delete event;
264 }
265
266 tasks = NULL;
267 }
268
269 void
270 EventScheduler::dump(StoreEntry * sentry)
271 {
272
273 ev_entry *e = tasks;
274
275 if (last_event_ran)
276 storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran);
277
278 storeAppendPrintf(sentry, "%-25s\t%-15s\t%s\t%s\n",
279 "Operation",
280 "Next Execution",
281 "Weight",
282 "Callback Valid?");
283
284 while (e != NULL) {
285 storeAppendPrintf(sentry, "%-25s\t%0.3f sec\t%5d\t %s\n",
286 e->name, e->when ? e->when - current_dtime : 0, e->weight,
287 (e->arg && e->cbdata) ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A");
288 e = e->next;
289 }
290 }
291
292 bool
293 EventScheduler::find(EVH * func, void * arg)
294 {
295
296 ev_entry *event;
297
298 for (event = tasks; event != NULL; event = event->next) {
299 if (event->func == func && event->arg == arg)
300 return true;
301 }
302
303 return false;
304 }
305
306 EventScheduler *
307 EventScheduler::GetInstance()
308 {
309 return &_instance;
310 }
311
312 void
313 EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata)
314 {
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;
319 ev_entry *event = new ev_entry(name, func, arg, timestamp, weight, cbdata);
320
321 ev_entry **E;
322 debugs(41, 7, HERE << "schedule: Adding '" << name << "', in " << when << " seconds");
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;
328 }
329
330 event->next = *E;
331 *E = event;
332 }
333