--- mad-squid-log-sched/src/event.cc 8 Jan 2013 17:36:44 -0000 1.1.1.3 +++ mad-squid-log-sched/src/event.cc 28 Jan 2013 21:44:55 -0000 1.1.1.3.6.9 @@ -1,6 +1,7 @@ /* * DEBUG: section 41 Event Processing * AUTHOR: Henrik Nordstrom + * Heap-based priority queue: Rainer Weikusat * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- @@ -39,6 +40,8 @@ #include "profiler/Profiler.h" #include "tools.h" +static unsigned const INITIAL_SIZE = 512; + /* The list of event processes */ static OBJH eventDump; @@ -111,7 +114,7 @@ ev_entry::ev_entry(char const * aName, EVH * aFunction, void * aArgument, double evWhen, int aWeight, bool haveArgument) : name(aName), func(aFunction), arg(haveArgument ? cbdataReference(aArgument) : aArgument), when(evWhen), weight(aWeight), - cbdata(haveArgument) + cbdata(haveArgument), tag(NULL) { } @@ -121,15 +124,27 @@ cbdataReferenceDone(arg); } -void -eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata) +class ev_tag +{ +public: + ev_entry *event; + + MEMPROXY_CLASS(ev_tag); +}; + +MEMPROXY_CLASS_INLINE(ev_tag); + +void +eventAdd(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata, + void **tag) { - EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata); + EventScheduler::GetInstance()->schedule(name, func, arg, when, weight, cbdata, tag); } /* same as eventAdd but adds a random offset within +-1/3 of delta_ish */ void -eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight) +eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int weight, + void **tag) { if (delta_ish >= 3.0) { const double two_third = (2.0 * delta_ish) / 3.0; @@ -140,7 +155,7 @@ */ } - eventAdd(name, func, arg, delta_ish, weight); + eventAdd(name, func, arg, delta_ish, weight, tag); } void @@ -149,6 +164,16 @@ EventScheduler::GetInstance()->cancel(func, arg); } +void *eventDelete(void *tag) +{ + return EventScheduler::GetInstance()->cancel(tag); +} + +void eventReleaseTag(void *tag) +{ + EventScheduler::GetInstance()->releaseTag(tag); +} + void eventInit(void) { @@ -175,8 +200,9 @@ EventScheduler EventScheduler::_instance; -EventScheduler::EventScheduler(): tasks(NULL) -{} +EventScheduler::EventScheduler(): tasks(NULL), used(0), max(0) +{ +} EventScheduler::~EventScheduler() { @@ -186,46 +212,44 @@ void EventScheduler::cancel(EVH * func, void *arg) { - ev_entry **E; ev_entry *event; + unsigned ndx; - for (E = &tasks; (event = *E) != NULL; E = &(*E)->next) { - if (event->func != func) - continue; - - if (arg && event->arg != arg) - continue; - - *E = event->next; - - delete event; - - if (arg) - return; - /* - * DPW 2007-04-12 - * Since this method may now delete multiple events (when - * arg is NULL) it no longer returns after a deletion and - * we have a potential NULL pointer problem. If we just - * deleted the last event in the list then *E is now equal - * to NULL. We need to break here or else we'll get a NULL - * pointer dereference in the last clause of the for loop. - */ - if (NULL == *E) - break; + ndx = used; + while (ndx) { + event = tasks[ndx]; + if (event->func == func && (!arg || event->arg == arg)) { + cancelEvent(event); + + if (arg) return; + + if (ndx > used) ndx = used; + } else + --ndx; } +} + +void *EventScheduler::cancel(void *tag) +{ + class ev_tag *ev_tag; + void *arg; - if (arg) - debug_trap("eventDelete: event not found"); + ev_tag = static_cast(tag); + + if (ev_tag->event) arg = cancelEvent(ev_tag->event); + else arg = NULL; + + delete ev_tag; + return arg; } int EventScheduler::checkDelay() { - if (!tasks) + if (!used) return EVENT_IDLE; - int result = (int) ((tasks->when - current_dtime) * 1000); + int result = (int) ((tasks[1]->when - current_dtime) * 1000); if (result < 0) return 0; @@ -236,23 +260,33 @@ int EventScheduler::checkEvents(int timeout) { - ev_entry *event = NULL; + ev_tag *ev_tag; - if (NULL == tasks) + if (!used) return checkDelay(); - if (tasks->when > current_dtime) + event = tasks[1]; + if (event->when > current_dtime) return checkDelay(); PROF_start(eventRun); debugs(41, 5, HERE << "checkEvents"); - while ((event = tasks)) { - if (event->when > current_dtime) - break; - + do { + if (used > 1) { + tasks[1] = tasks[used--]; + tasks[1]->slot = 1; + + if (used > 1) downHeap(1); + } else + used = 0; + + + ev_tag = static_cast(event->tag); + if (ev_tag) ev_tag->event = NULL; + /* XXX assumes event->name is static memory! */ AsyncCall::Pointer call = asyncCall(41,5, event->name, EventDialer(event->func, event->arg, event->cbdata)); @@ -262,14 +296,20 @@ const bool heavy = event->weight && (!event->cbdata || cbdataReferenceValid(event->arg)); - tasks = event->next; - delete event; + delete event; // XXX: We may be called again during the same event loop iteration. // Is there a point in breaking now? if (heavy) break; // do not dequeue events following a heavy event - } + + if (!used) + break; + + event = tasks[1]; + } while (event->when <= current_dtime); + + debugs(41, 5, HERE << "leaving"); PROF_stop(eventRun); return checkDelay(); @@ -278,46 +318,58 @@ void EventScheduler::clean() { - while (ev_entry * event = tasks) { - tasks = event->next; - delete event; + unsigned n; + + if (!tasks) return; + + n = used; + while (n) { + delete tasks[n]; + --n; } + free(tasks); tasks = NULL; } void EventScheduler::dump(StoreEntry * sentry) { - - ev_entry *e = tasks; + ev_entry *e; + unsigned ndx; if (last_event_ran) storeAppendPrintf(sentry, "Last event to run: %s\n\n", last_event_ran); + if (!used) return; + storeAppendPrintf(sentry, "%-25s\t%-15s\t%s\t%s\n", "Operation", "Next Execution", "Weight", "Callback Valid?"); - while (e != NULL) { - storeAppendPrintf(sentry, "%-25s\t%0.3f sec\t%5d\t %s\n", + ndx = 1; + do { + e = tasks[ndx]; + storeAppendPrintf(sentry, "%-25s\t%0.3f sec\t%5d\t %s\n", e->name, e->when ? e->when - current_dtime : 0, e->weight, (e->arg && e->cbdata) ? cbdataReferenceValid(e->arg) ? "yes" : "no" : "N/A"); - e = e->next; - } + + } while (++ndx <= used); } bool EventScheduler::find(EVH * func, void * arg) { + unsigned ndx; - ev_entry *event; + ndx = 1; + while (ndx < used) { + if (tasks[ndx]->func == func && tasks[ndx]->arg == arg) + return true; - for (event = tasks; event != NULL; event = event->next) { - if (event->func == func && event->arg == arg) - return true; + ++ndx; } return false; @@ -329,24 +381,157 @@ return &_instance; } -void -EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata) +int +EventScheduler::schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata, + void **tag) { + if (used == max && growTasks() == -1) return -1; + // Use zero timestamp for when=0 events: Many of them are async calls that // must fire in the submission order. We cannot use current_dtime for them // because it may decrease if system clock is adjusted backwards. const double timestamp = when > 0.0 ? current_dtime + when : 0; ev_entry *event = new ev_entry(name, func, arg, timestamp, weight, cbdata); - ev_entry **E; debugs(41, 7, HERE << "schedule: Adding '" << name << "', in " << when << " seconds"); - /* Insert after the last event with the same or earlier time */ - for (E = &tasks; *E; E = &(*E)->next) { - if ((*E)->when > event->when) - break; + tasks[++used] = event; + event->slot = used; + if (used > 1) upHeap(used); + + if (tag) { + ev_tag *ev_tag = new class ev_tag; + ev_tag->event = event; + *tag = ev_tag; + + event->tag = ev_tag; } - event->next = *E; - *E = event; + return 0; +} + +void +EventScheduler::releaseTag(void *tag) +{ + ev_entry *event; + ev_tag *ev_tag; + + ev_tag = static_cast(tag); + + event = ev_tag->event; + if (event) event->tag = NULL; + + delete ev_tag; +} + +void * +EventScheduler::cancelEvent(ev_entry *event) +{ + ev_tag *ev_tag; + void *arg; + unsigned ndx; + + if (!tasks) + return NULL; + + ndx = event->slot; + arg = event->arg; + + ev_tag = static_cast(event->tag); + if (ev_tag) ev_tag->event = NULL; + + delete event; + + if (ndx < used) { + tasks[ndx] = tasks[used--]; + tasks[ndx]->slot = ndx; + + if (used > 1) { + if (ndx > 1 && tasks[ndx]->when < tasks[ndx / 2]->when) + upHeap(ndx); + else if (ndx <= used / 2) + downHeap(ndx); + } + } else + --used; + + return arg; +} + +int +EventScheduler::growTasks() +{ + ev_entry **p; + unsigned want; + + if (!tasks) { + tasks = (ev_entry **)malloc(sizeof(*tasks) * INITIAL_SIZE); + if (!tasks) return -1; + + max = INITIAL_SIZE - 1; + return 0; + } + + want = (max + 1) * 2; + p = (ev_entry **)realloc(tasks, want * sizeof(*tasks)); + if (!p) return -1; + tasks = p; + max = want - 1; + + return 0; +} + +void +EventScheduler::upHeap(unsigned ndx) +{ + ev_entry *event; + double when; + unsigned next, cur; + + event = tasks[ndx]; + when = event->when; + cur = ndx; + do { + next = cur / 2; + if (when >= tasks[next]->when) break; + + tasks[cur] = tasks[next]; + tasks[cur]->slot = cur; + + cur = next; + } while (cur > 1); + + if (cur != ndx) { + tasks[cur] = event; + event->slot = cur; + } +} + +void +EventScheduler::downHeap(unsigned ndx) +{ + ev_entry *event; + double when; + unsigned next, cur; + + event = tasks[ndx]; + when = event->when; + cur = ndx; + do { + next = cur * 2; + if (next < used && tasks[next]->when < tasks[next + 1]->when) + ++next; + + if (when <= tasks[next]->when) break; + + tasks[cur] = tasks[next]; + tasks[cur]->slot = cur; + + cur = next; + } while (cur <= used / 2); + + if (cur != ndx) { + tasks[cur] = event; + event->slot = cur; + } } --- mad-squid-log-sched/src/event.h 8 Jan 2013 17:36:44 -0000 1.1.1.3 +++ mad-squid-log-sched/src/event.h 28 Jan 2013 21:44:55 -0000 1.1.1.3.6.3 @@ -42,16 +42,19 @@ typedef void EVH(void *); -void eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata=true); -void eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int); +void eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata=true, + void **tag = NULL); +void eventAddIsh(const char *name, EVH * func, void *arg, double delta_ish, int, + void **tag = NULL); void eventDelete(EVH * func, void *arg); +void *eventDelete(void *tag); +void eventReleaseTag(void *tag); void eventInit(void); void eventFreeMemory(void); int eventFind(EVH *, void *); class ev_entry { - public: ev_entry(char const * name, EVH * func, void *arg, double when, int weight, bool cbdata=true); ~ev_entry(); @@ -64,7 +67,8 @@ int weight; bool cbdata; - ev_entry *next; + unsigned slot; + void *tag; }; MEMPROXY_CLASS_INLINE(ev_entry); @@ -78,6 +82,7 @@ ~EventScheduler(); /* cancel a scheduled but not dispatched event */ void cancel(EVH * func, void * arg); + void *cancel(void *tag); /* clean up the used memory in the scheduler */ void clean(); /* how long until the next event ? */ @@ -87,13 +92,21 @@ /* find a scheduled event */ bool find(EVH * func, void * arg); /* schedule a callback function to run in when seconds */ - void schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata=true); + int schedule(const char *name, EVH * func, void *arg, double when, int weight, bool cbdata=true, + void **tag = NULL); int checkEvents(int timeout); + void releaseTag(void *tag); static EventScheduler *GetInstance(); private: static EventScheduler _instance; - ev_entry * tasks; + ev_entry **tasks; + unsigned used, max; + + void *cancelEvent(ev_entry *event); + int growTasks(); + void upHeap(unsigned ndx); + void downHeap(unsigned ndx); }; #endif /* SQUID_EVENT_H */