]>
Commit | Line | Data |
---|---|---|
48f44632 | 1 | /* |
5dee3116 | 2 | * $Id: event.cc,v 1.51 2008/02/12 23:27:42 rousskov Exp $ |
48f44632 | 3 | * |
f43e2ec2 | 4 | * DEBUG: section 41 Event Processing |
48f44632 | 5 | * AUTHOR: Henrik Nordstrom |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 8 | * ---------------------------------------------------------- |
48f44632 | 9 | * |
2b6662ba | 10 | * Squid is the result of efforts by numerous individuals from |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
48f44632 | 18 | * |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
23 | * | |
24 | * This program is distributed in the hope that it will be useful, | |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License | |
30 | * along with this program; if not, write to the Free Software | |
cbdec147 | 31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 32 | * |
48f44632 | 33 | */ |
34 | ||
a553a5a3 | 35 | #include "event.h" |
62ee09ca | 36 | #include "CacheManager.h" |
e6ccf245 | 37 | #include "Store.h" |
cc192b50 | 38 | #include "SquidTime.h" |
48f44632 | 39 | |
40 | /* The list of event processes */ | |
62e76326 | 41 | |
48f44632 | 42 | |
4ba55996 | 43 | static OBJH eventDump; |
236b1f2a | 44 | static const char *last_event_ran = NULL; |
48f44632 | 45 | |
5dee3116 | 46 | // This AsyncCall dialer can be configured to check that the event cbdata is |
47 | // valid before calling the event handler | |
48 | class EventDialer: public CallDialer | |
49 | { | |
50 | public: | |
51 | typedef CallDialer Parent; | |
52 | ||
53 | EventDialer(EVH *aHandler, void *anArg, bool lockedArg); | |
54 | EventDialer(const EventDialer &d); | |
55 | virtual ~EventDialer(); | |
56 | ||
57 | virtual void print(std::ostream &os) const; | |
58 | virtual bool canDial(AsyncCall &call); | |
59 | ||
60 | void dial(AsyncCall &) { theHandler(theArg); } | |
61 | ||
62 | private: | |
63 | EVH *theHandler; | |
64 | void *theArg; | |
65 | bool isLockedArg; | |
66 | }; | |
67 | ||
68 | EventDialer::EventDialer(EVH *aHandler, void *anArg, bool lockedArg): | |
69 | theHandler(aHandler), theArg(anArg), isLockedArg(lockedArg) | |
70 | { | |
71 | if (isLockedArg) | |
72 | cbdataReference(theArg); | |
73 | } | |
74 | ||
75 | EventDialer::EventDialer(const EventDialer &d): | |
76 | theHandler(d.theHandler), theArg(d.theArg), isLockedArg(d.isLockedArg) | |
77 | { | |
78 | if (isLockedArg) | |
79 | cbdataReference(theArg); | |
80 | } | |
81 | ||
82 | EventDialer::~EventDialer() | |
83 | { | |
84 | if (isLockedArg) | |
85 | cbdataReferenceDone(theArg); | |
86 | } | |
87 | ||
88 | bool | |
89 | EventDialer::canDial(AsyncCall &call) { | |
90 | // TODO: add Parent::canDial() that always returns true | |
91 | //if (!Parent::canDial()) | |
92 | // return false; | |
93 | ||
94 | if (isLockedArg && !cbdataReferenceValid(theArg)) | |
95 | return call.cancel("stale handler data"); | |
96 | ||
97 | return true; | |
98 | } | |
99 | ||
100 | void | |
101 | EventDialer::print(std::ostream &os) const | |
102 | { | |
103 | os << '('; | |
104 | if (theArg) | |
105 | os << theArg << (isLockedArg ? "*?" : ""); | |
106 | os << ')'; | |
107 | } | |
108 | ||
109 | ||
110 | ev_entry::ev_entry(char const * name, EVH * func, void * arg, double when, | |
111 | int weight, bool cbdata) : name(name), func(func), | |
112 | arg(cbdata ? cbdataReference(arg) : arg), when(when), weight(weight), | |
113 | cbdata(cbdata) | |
114 | { | |
115 | } | |
116 | ||
117 | ev_entry::~ev_entry() | |
118 | { | |
119 | if (cbdata) | |
120 | cbdataReferenceDone(arg); | |
121 | } | |
a553a5a3 | 122 | |
48f44632 | 123 | void |
601af4c6 | 124 | eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata) |
48f44632 | 125 | { |
a553a5a3 | 126 | EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata); |
48f44632 | 127 | } |
128 | ||
8f3db324 | 129 | /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */ |
130 | void | |
52040193 | 131 | eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight) |
8f3db324 | 132 | { |
52040193 | 133 | if (delta_ish >= 3.0) { |
62e76326 | 134 | const double two_third = (2.0 * delta_ish) / 3.0; |
135 | delta_ish = two_third + (drand48() * two_third); | |
136 | /* | |
137 | * I'm sure drand48() isn't portable. Tell me what function | |
138 | * you have that returns a random double value in the range 0,1. | |
139 | */ | |
8f3db324 | 140 | } |
62e76326 | 141 | |
f720985e | 142 | eventAdd(name, func, arg, delta_ish, weight); |
8f3db324 | 143 | } |
144 | ||
93775f90 | 145 | void |
582b6456 | 146 | eventDelete(EVH * func, void *arg) |
93775f90 | 147 | { |
a553a5a3 | 148 | EventScheduler::GetInstance()->cancel(func, arg); |
149 | } | |
62e76326 | 150 | |
a553a5a3 | 151 | void |
152 | eventInit(CacheManager &manager) | |
153 | { | |
154 | manager.registerAction("events", "Event Queue", eventDump, 0, 1); | |
155 | } | |
62e76326 | 156 | |
a553a5a3 | 157 | static void |
158 | eventDump(StoreEntry * sentry) | |
159 | { | |
160 | EventScheduler::GetInstance()->dump(sentry); | |
161 | } | |
162 | ||
163 | void | |
164 | eventFreeMemory(void) | |
165 | { | |
166 | EventScheduler::GetInstance()->clean(); | |
167 | } | |
168 | ||
169 | int | |
170 | eventFind(EVH * func, void *arg) | |
171 | { | |
172 | return EventScheduler::GetInstance()->find(func, arg); | |
173 | } | |
174 | ||
5dee3116 | 175 | EventScheduler EventScheduler::_instance; |
a553a5a3 | 176 | |
5dee3116 | 177 | EventScheduler::EventScheduler(): tasks(NULL) |
a553a5a3 | 178 | {} |
179 | ||
180 | EventScheduler::~EventScheduler() | |
181 | { | |
182 | clean(); | |
183 | } | |
184 | ||
185 | void | |
186 | EventScheduler::cancel(EVH * func, void *arg) | |
187 | { | |
188 | ev_entry **E; | |
189 | ev_entry *event; | |
62e76326 | 190 | |
79d39a72 | 191 | for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) { |
62e76326 | 192 | if (event->func != func) |
193 | continue; | |
194 | ||
aa93f210 | 195 | if (arg && event->arg != arg) |
62e76326 | 196 | continue; |
197 | ||
198 | *E = event->next; | |
199 | ||
62ee09ca | 200 | delete event; |
62e76326 | 201 | |
aa93f210 | 202 | if (arg) |
203 | return; | |
d65986ae | 204 | /* |
205 | * DPW 2007-04-12 | |
206 | * Since this method may now delete multiple events (when | |
207 | * arg is NULL) it no longer returns after a deletion and | |
208 | * we have a potential NULL pointer problem. If we just | |
209 | * deleted the last event in the list then *E is now equal | |
210 | * to NULL. We need to break here or else we'll get a NULL | |
211 | * pointer dereference in the last clause of the for loop. | |
212 | */ | |
213 | if (NULL == *E) | |
214 | break; | |
93775f90 | 215 | } |
62e76326 | 216 | |
aa93f210 | 217 | if (arg) |
218 | debug_trap("eventDelete: event not found"); | |
93775f90 | 219 | } |
220 | ||
a553a5a3 | 221 | int |
222 | EventScheduler::checkDelay() | |
223 | { | |
224 | if (!tasks) | |
aa839030 | 225 | return EVENT_IDLE; |
a553a5a3 | 226 | |
8ff3fa2e | 227 | int result = (int) ((tasks->when - current_dtime) * 1000); |
228 | ||
229 | if (result < 0) | |
230 | return 0; | |
231 | ||
232 | return result; | |
a553a5a3 | 233 | } |
234 | ||
235 | int | |
8ff3fa2e | 236 | EventScheduler::checkEvents(int timeout) |
48f44632 | 237 | { |
62e76326 | 238 | |
48f44632 | 239 | struct ev_entry *event = NULL; |
62e76326 | 240 | |
c43f5247 | 241 | if (NULL == tasks) |
a553a5a3 | 242 | return checkDelay(); |
62e76326 | 243 | |
c43f5247 | 244 | if (tasks->when > current_dtime) |
a553a5a3 | 245 | return checkDelay(); |
62e76326 | 246 | |
1d5161bd | 247 | PROF_start(eventRun); |
248 | ||
15584534 | 249 | debugs(41, 5, HERE << "checkEvents"); |
62e76326 | 250 | |
c43f5247 | 251 | while ((event = tasks)) { |
62e76326 | 252 | if (event->when > current_dtime) |
253 | break; | |
254 | ||
5dee3116 | 255 | /* XXX assumes event->name is static memory! */ |
256 | AsyncCall::Pointer call = asyncCall(41,5, event->name, | |
257 | EventDialer(event->func, event->arg, event->cbdata)); | |
258 | ScheduleCallHere(call); | |
259 | ||
260 | last_event_ran = event->name; // XXX: move this to AsyncCallQueue | |
261 | const bool heavy = event->weight && | |
262 | (!event->cbdata || cbdataReferenceValid(event->arg)); | |
62e76326 | 263 | |
264 | tasks = event->next; | |
5dee3116 | 265 | delete event; |
62e76326 | 266 | |
5dee3116 | 267 | // XXX: We may be called again during the same event loop iteration. |
268 | // Is there a point in breaking now? | |
269 | if (heavy) | |
270 | break; // do not dequeue events following a heavy event | |
d90c79ee | 271 | } |
1d5161bd | 272 | |
273 | PROF_stop(eventRun); | |
a553a5a3 | 274 | return checkDelay(); |
48f44632 | 275 | } |
276 | ||
a553a5a3 | 277 | void |
278 | EventScheduler::clean() | |
48f44632 | 279 | { |
a553a5a3 | 280 | while (ev_entry * event = tasks) { |
281 | tasks = event->next; | |
a553a5a3 | 282 | delete event; |
283 | } | |
284 | ||
285 | tasks = NULL; | |
4ba55996 | 286 | } |
287 | ||
a553a5a3 | 288 | void |
289 | EventScheduler::dump(StoreEntry * sentry) | |
4ba55996 | 290 | { |
62e76326 | 291 | |
4ba55996 | 292 | struct ev_entry *e = tasks; |
62e76326 | 293 | |
236b1f2a | 294 | if (last_event_ran) |
62e76326 | 295 | storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran); |
296 | ||
cc192b50 | 297 | storeAppendPrintf(sentry, "%-25s\t%-15s\t%s\t%s\n", |
62e76326 | 298 | "Operation", |
299 | "Next Execution", | |
300 | "Weight", | |
301 | "Callback Valid?"); | |
302 | ||
4ba55996 | 303 | while (e != NULL) { |
cc192b50 | 304 | storeAppendPrintf(sentry, "%-25s\t%0.3f sec\t%5d\t %s\n", |
32f10aa5 | 305 | e->name, e->when ? e->when - current_dtime : 0, e->weight, |
601af4c6 | 306 | (e->arg && e->cbdata) ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A"); |
62e76326 | 307 | e = e->next; |
4ba55996 | 308 | } |
309 | } | |
f1fc2a8d | 310 | |
a553a5a3 | 311 | bool |
312 | EventScheduler::find(EVH * func, void * arg) | |
f1fc2a8d | 313 | { |
62e76326 | 314 | |
f1fc2a8d | 315 | struct ev_entry *event; |
62e76326 | 316 | |
a553a5a3 | 317 | for (event = tasks; event != NULL; event = event->next) { |
318 | if (event->func == func && event->arg == arg) | |
319 | return true; | |
f1fc2a8d | 320 | } |
62e76326 | 321 | |
a553a5a3 | 322 | return false; |
f1fc2a8d | 323 | } |
46ca5fc6 | 324 | |
a553a5a3 | 325 | EventScheduler * |
326 | EventScheduler::GetInstance() | |
46ca5fc6 | 327 | { |
a553a5a3 | 328 | return &_instance; |
329 | } | |
62e76326 | 330 | |
a553a5a3 | 331 | void |
332 | EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata) | |
333 | { | |
a5a02499 | 334 | // Use zero timestamp for when=0 events: Many of them are async calls that |
335 | // must fire in the submission order. We cannot use current_dtime for them | |
336 | // because it may decrease if system clock is adjusted backwards. | |
337 | const double timestamp = when > 0.0 ? current_dtime + when : 0; | |
338 | struct ev_entry *event = new ev_entry(name, func, arg, timestamp, weight, cbdata); | |
a553a5a3 | 339 | |
340 | struct ev_entry **E; | |
d65986ae | 341 | debugs(41, 7, HERE << "schedule: Adding '" << name << "', in " << when << " seconds"); |
a553a5a3 | 342 | /* Insert after the last event with the same or earlier time */ |
343 | ||
344 | for (E = &tasks; *E; E = &(*E)->next) { | |
345 | if ((*E)->when > event->when) | |
346 | break; | |
46ca5fc6 | 347 | } |
62e76326 | 348 | |
a553a5a3 | 349 | event->next = *E; |
350 | *E = event; | |
46ca5fc6 | 351 | } |