--- 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 <rweikusat@mobileactivedefense.com>
  *
  * 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<class ev_tag *>(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<class ev_tag *>(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<class ev_tag *>(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<class ev_tag *>(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 */

