]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/event.cc
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 41 Event Processing */
12 #include "compat/drand48.h"
14 #include "mgr/Registration.h"
15 #include "profiler/Profiler.h"
16 #include "SquidTime.h"
22 /* The list of event processes */
24 static OBJH eventDump
;
25 static const char *last_event_ran
= NULL
;
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
32 typedef CallDialer Parent
;
34 EventDialer(EVH
*aHandler
, void *anArg
, bool lockedArg
);
35 EventDialer(const EventDialer
&d
);
36 virtual ~EventDialer();
38 virtual void print(std::ostream
&os
) const;
39 virtual bool canDial(AsyncCall
&call
);
41 void dial(AsyncCall
&) { theHandler(theArg
); }
49 EventDialer::EventDialer(EVH
*aHandler
, void *anArg
, bool lockedArg
):
50 theHandler(aHandler
), theArg(anArg
), isLockedArg(lockedArg
)
53 (void)cbdataReference(theArg
);
56 EventDialer::EventDialer(const EventDialer
&d
):
57 theHandler(d
.theHandler
), theArg(d
.theArg
), isLockedArg(d
.isLockedArg
)
60 (void)cbdataReference(theArg
);
63 EventDialer::~EventDialer()
66 cbdataReferenceDone(theArg
);
70 EventDialer::canDial(AsyncCall
&call
)
72 // TODO: add Parent::canDial() that always returns true
73 //if (!Parent::canDial())
76 if (isLockedArg
&& !cbdataReferenceValid(theArg
))
77 return call
.cancel("stale handler data");
83 EventDialer::print(std::ostream
&os
) const
87 os
<< theArg
<< (isLockedArg
? "*?" : "");
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
),
101 cbdataReferenceDone(arg
);
105 eventAdd(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
107 EventScheduler::GetInstance()->schedule(name
, func
, arg
, when
, weight
, cbdata
);
110 /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
112 eventAddIsh(const char *name
, EVH
* func
, void *arg
, double delta_ish
, int weight
)
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
);
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.
123 eventAdd(name
, func
, arg
, delta_ish
, weight
);
127 eventDelete(EVH
* func
, void *arg
)
129 EventScheduler::GetInstance()->cancel(func
, arg
);
135 Mgr::RegisterAction("events", "Event Queue", eventDump
, 0, 1);
139 eventDump(StoreEntry
* sentry
)
141 EventScheduler::GetInstance()->dump(sentry
);
145 eventFreeMemory(void)
147 EventScheduler::GetInstance()->clean();
151 eventFind(EVH
* func
, void *arg
)
153 return EventScheduler::GetInstance()->find(func
, arg
);
156 EventScheduler
EventScheduler::_instance
;
158 EventScheduler::EventScheduler(): tasks(NULL
)
161 EventScheduler::~EventScheduler()
167 EventScheduler::cancel(EVH
* func
, void *arg
)
172 for (E
= &tasks
; (event
= *E
) != NULL
; E
= &(*E
)->next
) {
173 if (event
->func
!= func
)
176 if (arg
&& event
->arg
!= arg
)
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.
199 debug_trap("eventDelete: event not found");
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.
205 EventScheduler::timeRemaining() const
210 if (tasks
->when
<= current_dtime
) // we are on time or late
211 return 0; // fire the event ASAP
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
);
222 EventScheduler::checkEvents(int)
224 int result
= timeRemaining();
228 PROF_start(eventRun
);
231 ev_entry
*event
= tasks
;
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
);
239 last_event_ran
= event
->name
; // XXX: move this to AsyncCallQueue
240 const bool heavy
= event
->weight
&&
241 (!event
->cbdata
|| cbdataReferenceValid(event
->arg
));
246 result
= timeRemaining();
248 // XXX: We may be called again during the same event loop iteration.
249 // Is there a point in breaking now?
251 break; // do not dequeue events following a heavy event
252 } while (result
== 0);
259 EventScheduler::clean()
261 while (ev_entry
* event
= tasks
) {
270 EventScheduler::dump(StoreEntry
* sentry
)
276 storeAppendPrintf(sentry
, "Last event to run: %s\n\n", last_event_ran
);
278 storeAppendPrintf(sentry
, "%-25s\t%-15s\t%s\t%s\n",
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");
293 EventScheduler::find(EVH
* func
, void * arg
)
298 for (event
= tasks
; event
!= NULL
; event
= event
->next
) {
299 if (event
->func
== func
&& event
->arg
== arg
)
307 EventScheduler::GetInstance()
313 EventScheduler::schedule(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
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
);
322 debugs(41, 7, HERE
<< "schedule: Adding '" << name
<< "', in " << when
<< " seconds");
323 /* Insert after the last event with the same or earlier time */
325 for (E
= &tasks
; *E
; E
= &(*E
)->next
) {
326 if ((*E
)->when
> event
->when
)