2 * Copyright (C) 1996-2016 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 */
13 #include "mgr/Registration.h"
14 #include "profiler/Profiler.h"
15 #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
, int aWeight
, bool haveArg
) :
94 arg(haveArg
? cbdataReference(aArgument
) : aArgument
),
102 ev_entry::~ev_entry()
105 cbdataReferenceDone(arg
);
109 eventAdd(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
111 EventScheduler::GetInstance()->schedule(name
, func
, arg
, when
, weight
, cbdata
);
114 /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
116 eventAddIsh(const char *name
, EVH
* func
, void *arg
, double delta_ish
, int weight
)
118 if (delta_ish
>= 3.0) {
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);
123 xuniform_real_distribution
<> thirdIsh(delta_ish
- third
, delta_ish
+ third
);
124 delta_ish
= thirdIsh(rng
);
127 eventAdd(name
, func
, arg
, delta_ish
, weight
);
131 eventDelete(EVH
* func
, void *arg
)
133 EventScheduler::GetInstance()->cancel(func
, arg
);
139 Mgr::RegisterAction("events", "Event Queue", eventDump
, 0, 1);
143 eventDump(StoreEntry
* sentry
)
145 EventScheduler::GetInstance()->dump(sentry
);
149 eventFreeMemory(void)
151 EventScheduler::GetInstance()->clean();
155 eventFind(EVH
* func
, void *arg
)
157 return EventScheduler::GetInstance()->find(func
, arg
);
160 EventScheduler
EventScheduler::_instance
;
162 EventScheduler::EventScheduler(): tasks(NULL
)
165 EventScheduler::~EventScheduler()
171 EventScheduler::cancel(EVH
* func
, void *arg
)
176 for (E
= &tasks
; (event
= *E
) != NULL
; E
= &(*E
)->next
) {
177 if (event
->func
!= func
)
180 if (arg
&& event
->arg
!= arg
)
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.
203 debug_trap("eventDelete: event not found");
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.
209 EventScheduler::timeRemaining() const
214 if (tasks
->when
<= current_dtime
) // we are on time or late
215 return 0; // fire the event ASAP
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
);
226 EventScheduler::checkEvents(int)
228 int result
= timeRemaining();
232 PROF_start(eventRun
);
235 ev_entry
*event
= tasks
;
238 /* XXX assumes event->name is static memory! */
239 AsyncCall::Pointer call
= asyncCall(41,5, event
->name
,
240 EventDialer(event
->func
, event
->arg
, event
->cbdata
));
241 ScheduleCallHere(call
);
243 last_event_ran
= event
->name
; // XXX: move this to AsyncCallQueue
244 const bool heavy
= event
->weight
&&
245 (!event
->cbdata
|| cbdataReferenceValid(event
->arg
));
250 result
= timeRemaining();
252 // XXX: We may be called again during the same event loop iteration.
253 // Is there a point in breaking now?
255 break; // do not dequeue events following a heavy event
256 } while (result
== 0);
263 EventScheduler::clean()
265 while (ev_entry
* event
= tasks
) {
274 EventScheduler::dump(StoreEntry
* sentry
)
280 storeAppendPrintf(sentry
, "Last event to run: %s\n\n", last_event_ran
);
282 storeAppendPrintf(sentry
, "%-25s\t%-15s\t%s\t%s\n",
289 storeAppendPrintf(sentry
, "%-25s\t%0.3f sec\t%5d\t %s\n",
290 e
->name
, e
->when
? e
->when
- current_dtime
: 0, e
->weight
,
291 (e
->arg
&& e
->cbdata
) ? cbdataReferenceValid(e
->arg
) ? "yes" : "no" : "N/A");
297 EventScheduler::find(EVH
* func
, void * arg
)
302 for (event
= tasks
; event
!= NULL
; event
= event
->next
) {
303 if (event
->func
== func
&& event
->arg
== arg
)
311 EventScheduler::GetInstance()
317 EventScheduler::schedule(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
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;
323 ev_entry
*event
= new ev_entry(name
, func
, arg
, timestamp
, weight
, cbdata
);
326 debugs(41, 7, HERE
<< "schedule: Adding '" << name
<< "', in " << when
<< " seconds");
327 /* Insert after the last event with the same or earlier time */
329 for (E
= &tasks
; *E
; E
= &(*E
)->next
) {
330 if ((*E
)->when
> event
->when
)