2 * Copyright (C) 1996-2023 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 "base/Random.h"
14 #include "mgr/Registration.h"
20 /* The list of event processes */
22 static OBJH eventDump
;
23 static const char *last_event_ran
= nullptr;
25 // This AsyncCall dialer can be configured to check that the event cbdata is
26 // valid before calling the event handler
27 class EventDialer
: public CallDialer
30 typedef CallDialer Parent
;
32 EventDialer(EVH
*aHandler
, void *anArg
, bool lockedArg
);
33 EventDialer(const EventDialer
&d
);
34 ~EventDialer() override
;
36 void print(std::ostream
&os
) const override
;
37 virtual bool canDial(AsyncCall
&call
);
39 void dial(AsyncCall
&) { theHandler(theArg
); }
47 EventDialer::EventDialer(EVH
*aHandler
, void *anArg
, bool lockedArg
):
48 theHandler(aHandler
), theArg(anArg
), isLockedArg(lockedArg
)
51 (void)cbdataReference(theArg
);
54 EventDialer::EventDialer(const EventDialer
&d
):
55 theHandler(d
.theHandler
), theArg(d
.theArg
), isLockedArg(d
.isLockedArg
)
58 (void)cbdataReference(theArg
);
61 EventDialer::~EventDialer()
64 cbdataReferenceDone(theArg
);
68 EventDialer::canDial(AsyncCall
&call
)
70 // TODO: add Parent::canDial() that always returns true
71 //if (!Parent::canDial())
74 if (isLockedArg
&& !cbdataReferenceValid(theArg
))
75 return call
.cancel("stale handler data");
81 EventDialer::print(std::ostream
&os
) const
85 os
<< theArg
<< (isLockedArg
? "*?" : "");
89 ev_entry::ev_entry(char const * aName
, EVH
* aFunction
, void * aArgument
, double evWhen
, int aWeight
, bool haveArg
) :
92 arg(haveArg
? cbdataReference(aArgument
) : aArgument
),
100 ev_entry::~ev_entry()
103 cbdataReferenceDone(arg
);
107 eventAdd(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
109 EventScheduler::GetInstance()->schedule(name
, func
, arg
, when
, weight
, cbdata
);
112 /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */
114 eventAddIsh(const char *name
, EVH
* func
, void *arg
, double delta_ish
, int weight
)
116 if (delta_ish
>= 3.0) {
117 static std::mt19937
rng(RandomSeed32());
118 auto third
= (delta_ish
/3.0);
119 std::uniform_real_distribution
<> thirdIsh(delta_ish
- third
, delta_ish
+ third
);
120 delta_ish
= thirdIsh(rng
);
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 eventFind(EVH
* func
, void *arg
)
147 return EventScheduler::GetInstance()->find(func
, arg
);
150 EventScheduler
EventScheduler::_instance
;
152 EventScheduler::EventScheduler(): tasks(nullptr)
155 EventScheduler::~EventScheduler()
161 EventScheduler::cancel(EVH
* func
, void *arg
)
166 for (E
= &tasks
; (event
= *E
) != nullptr; E
= &(*E
)->next
) {
167 if (event
->func
!= func
)
170 if (arg
&& event
->arg
!= arg
)
181 * Since this method may now delete multiple events (when
182 * arg is NULL) it no longer returns after a deletion and
183 * we have a potential NULL pointer problem. If we just
184 * deleted the last event in the list then *E is now equal
185 * to NULL. We need to break here or else we'll get a NULL
186 * pointer dereference in the last clause of the for loop.
193 debug_trap("eventDelete: event not found");
196 // The event API does not guarantee exact timing, but guarantees that no event
197 // is fired before it is due. We may delay firing, but never fire too early.
199 EventScheduler::timeRemaining() const
204 if (tasks
->when
<= current_dtime
) // we are on time or late
205 return 0; // fire the event ASAP
207 const double diff
= tasks
->when
- current_dtime
; // microseconds
208 // Round UP: If we come back a nanosecond earlier, we will wait again!
209 const int timeLeft
= static_cast<int>(ceil(1000*diff
)); // milliseconds
210 // Avoid hot idle: A series of rapid select() calls with zero timeout.
211 const int minDelay
= 1; // millisecond
212 return max(minDelay
, timeLeft
);
216 EventScheduler::checkEvents(int)
218 int result
= timeRemaining();
223 ev_entry
*event
= tasks
;
226 /* XXX assumes event->name is static memory! */
227 AsyncCall::Pointer call
= asyncCall(41,5, event
->name
,
228 EventDialer(event
->func
, event
->arg
, event
->cbdata
));
229 ScheduleCallHere(call
);
231 last_event_ran
= event
->name
; // XXX: move this to AsyncCallQueue
232 const bool heavy
= event
->weight
&&
233 (!event
->cbdata
|| cbdataReferenceValid(event
->arg
));
238 result
= timeRemaining();
240 // XXX: We may be called again during the same event loop iteration.
241 // Is there a point in breaking now?
243 break; // do not dequeue events following a heavy event
244 } while (result
== 0);
250 EventScheduler::clean()
252 while (ev_entry
* event
= tasks
) {
261 EventScheduler::dump(Packable
*out
)
264 out
->appendf("Last event to run: %s\n\n", last_event_ran
);
266 out
->appendf("%-25s\t%-15s\t%s\t%s\n",
272 for (auto *e
= tasks
; e
; e
= e
->next
) {
273 out
->appendf("%-25s\t%0.3f sec\t%5d\t %s\n",
274 e
->name
, (e
->when
? e
->when
- current_dtime
: 0), e
->weight
,
275 (e
->arg
&& e
->cbdata
) ? cbdataReferenceValid(e
->arg
) ? "yes" : "no" : "N/A");
280 EventScheduler::find(EVH
* func
, void * arg
)
285 for (event
= tasks
; event
!= nullptr; event
= event
->next
) {
286 if (event
->func
== func
&& event
->arg
== arg
)
294 EventScheduler::GetInstance()
300 EventScheduler::schedule(const char *name
, EVH
* func
, void *arg
, double when
, int weight
, bool cbdata
)
302 // Use zero timestamp for when=0 events: Many of them are async calls that
303 // must fire in the submission order. We cannot use current_dtime for them
304 // because it may decrease if system clock is adjusted backwards.
305 const double timestamp
= when
> 0.0 ? current_dtime
+ when
: 0;
306 ev_entry
*event
= new ev_entry(name
, func
, arg
, timestamp
, weight
, cbdata
);
309 debugs(41, 7, "schedule: Adding '" << name
<< "', in " << when
<< " seconds");
310 /* Insert after the last event with the same or earlier time */
312 for (E
= &tasks
; *E
; E
= &(*E
)->next
) {
313 if ((*E
)->when
> event
->when
)