scalable event scheduling

From: Rainer Weikusat <rweikusat_at_mobileactivedefense.com>
Date: Mon, 28 Jan 2013 22:29:33 +0000

Since Mr Rousskov was so kind to publically question my honesty and/or
mental sanity when I claimed that replacing the O(n) event scheduler
with a more scalable implementation would be easy (and in fact, so
easy that even using the STL implementation wouldn't be worth the
effort) and since I need this anyway for connection timeouts and
possibly other stuff in the squid fork I will be maintaining for at
least some time to come and had an hour of free time for some
recreational programming yesterday, I thought I could just do that.

This implementation is based on using a binary heap represented as an
array, the 'next simplest' algorithm after using a sorted, linked
list. People who enjoy near-incomprehensible theoretic explanation of
stuff like this will find something about that in TAOCP vol 3
('Searching and Sorting') around page 148. The main difference are

negative
--------

        - running a timeout is no longer O(1) but O(log n)

        - cancelling a timeout based on function and argument *
          remains O(n) but each actual deletion requires additional
          steps with a time complexity of O(log n)

positive
--------

        - adding events is O(log n)
        
        - timeouts can be canceled with O(log n) time complexity as
          well, one just needs to jump through a few hoops necessary
          to deal with the squid architecture, in particular, the fact
          that evens are scheduled asynchronously wrt to
          EventScheduler::checkEvents

Changes to the Public Interface
-------------------------------

        - the eventAdd routines take an addition void **argument, see
          O(log n) cancelling below

        - a second eventDelete routine was added which takes a void *
          as argument and returns a void *b, see O(log n) cancelling
          below

        - a void eventReleaseTag(void *) routine was added, see ...

O(log n) cancelling
-------------------

This is possible when the caller can tell the event scheduler which
actual event should be cancelled. In order for this to be possible, a
final void ** argument can be passed to eventAdd or eventAddIsh. If
it is not null, the event scheduler will store a value at the
pointed-to location which can be passed to eventDelete to cancel a pending
event 'safely' in logarithmic time, meaning, even if the event was
possibly already put onto the async queue. In case the caller doesn't
want to cancel the event anymore, say, because the corresponding
routine was actually executed, the eventReleaseTag routine must be
used in order to prevent the tag object from being leaked.

This is somewhat ugly but neither the existing 'refcounted pointer'
class nor the 'calllback data' meachanism seemed to be a good fit for
ev_entry objects and I didn't feel like inventing another 'general
thing' for a single use case.

The tag-based eventDelete/ cancel returns the argument pointer stored
in the event object so that a caller can free that (or do other
necessary things with it) without having to deal with storing two
values to be able to cancel the event.

Memory Management
-----------------

Is C-style, in order to exploit realloc for in-place enlarging of
allocated objects. This isn't really integrated well with the code
because while an error code is returned from schedule when this fails,
none of the callers knows about that. A better solution would probably
be to throw a bad_alloc exception manually. The tasks array is grown
exponentially which implies that it will end up being twice as large as
necessary, this being the usual way from preventing sequence of
reallocs because of 'constant growth' of the event list from running
with O(n * n) time complexity in the worst case because of realloc
actually copying the data. IMO, the excess allocation matters little
because if the memory is never touched, the OS won't ever allocate
actual RAM for it.

Other Remarks
-------------

This is published here 'in the hope that it will be useful' (for
something) with the kind permission of my employer. While it is
planned to move this into production 'soon', so far, I've only used it
for internal testing (OTOH, I've implemented quite a lot of these
beasts in the past ...). There are probably all kinds of 'weird,
stylistic issues' and possible, yet unknown bugs, and it doesn't
follow the squid guidelines for code documentation (in theory, I would
be willing to spend more work on this in order to bring it in line
with the requirements for 'squid code', in practice, remembering the
ConnOpener fiasco, I don't think that doing this now would make any
sense).

Received on Mon Jan 28 2013 - 22:29:54 MST

This archive was generated by hypermail 2.2.0 : Wed Jan 30 2013 - 12:00:08 MST