There was a small discussion after I voiced my opinion about the ecore
event system.  As can be expected, some one suggested I submit a patch.
This is not a patch, but a statement of the problem and two proposed
solutions.  I'll write either solution.

My comments where prompted by someone suggesting that ecore should
handle events "very well".  My objection to the current state of event
handling is that it is not done "very well", it is merely done "well".
I have been dealing with event systems, at all levels, since the early
1980's.  I have seen the entire spectrum, from really very good, to
downright evil.  I think I have a good handle on how to do event
systems "very well".

I should state right now that while I have dealt with the ecore event
system during my work in ecore_exe, I have not had much more exposure
than that.  On the other hand, the executive summary of this email is
that ecore events are probably fine for most of what they are used for,
but will not scale well for fork'n'pipe.  Raster suggested at the
beginning that I use ecore events for fork'n'pipe.  He knows the entire
system much better than me, so I bowed to his wisdom.  Solution two is
to just not use ecore events for fork'n'pipe.  I think I can do better
than that with solution one though.

One good metric for event systems is how many useless, ignored events
do they send.  A good event system will also have as little code
between the event generator and the event consumer as possible.  These
two can be a bit of a trade off if things are not done well.  Sending
less useless events might involve having more code to decide what
events to send where.  It doesn't have to though.

Typical event systems are part of systems that have some object
oriented aspects.  Not surprising as events are usually generated by
one object to be consumed by one or more other objects.  The ideal is
if each generating object already knows exactly which consuming objects
to send to, and just does straight away.  Nothing useless is sent, and
the sending object just iterates through a list of objects to send to,
meaning minimal code gets in the way.

We don't live in an ideal world however.  The sending object does not
know any internal state of the consuming objects, and thus cannot
always make ignore/consume decisions on their behalf.  So some useless
events are always gonna happen.  Useless events waste CPU time, and are
to be minimised even if we can't avoid them altogether.  The trick is
to come up with the minimal code that will not waste too much CPU time.

The registered callbacks system used in ecore is a good system that can
make a decent trade off if the granularity is done right.  My basic
problem  with ecore is that I think the granularity could be better.
It's fine, although not ideal for user input level events, but won't
scale well for heavy IPC level events.

For user input events, we are talking low bandwidth, small amounts of
data stuff.  Keystrokes at 100 words a minute, mouse clicks, even mouse
moves are not events that happen quickly, or require much data to
describe.  While quick responses are usually required for UI stuff, we
are talking human quick, not computer quick.  So these sort of events
CAN take their time getting sent.  On the other hand, it is often hard
for user input event generating objects to know exactly which objects
to send to.  A keystroke often has to filter through many layers of
objects, with contextual decisions being made in each until some object
consumes it.

User input events are the low end of the spectrum, but usually quite
important, and event systems are often designed around them.  My
limited exposure to ecore events gives we the impression that they are
up to the task.

At the other end of the spectrum is fork'n'pipe, with possibly large
amounts of data being sent as quickly as possible from one object to
another.  These sorts of events often CAN be dealt with using minimal
code and minimal useless events, because when you start an exe to read
from it, there is no one else interested in the read data.  The sending
object only needs to send to one consuming object, and that consuming
object is known ahead of time, it's the object that started the exe.

This is not a good match to the current ecore event system, as the
ecore event system will send read data events to all objects that
requested read data events, from all exes.  It is up to the consuming
objects to decide to ignore any particular read event.  With lots of
exes being read from, some of them sending possibly lots of data, this
will not scale well.

To make matters worse, there is line buffering, which will send lots of
read data events one per line, rather than in one big blob.  This could
mean lots of small read data events being sent, sometimes lots of them
very quickly.  This will scale very poorly.  Especially as they all
have to be ignored by all but one of the read data consuming objects.
So it only takes one line buffered exe with lots of short lines sent
quickly to bog down the entire system.

Solution one is for the ecore event system to do some checking of
generating objects on behalf of the consuming object.  Add a pointer to
the callback structure that defaults to NULL.  When an object registers
a callback, it can optionally supply that pointer.  When an event
occurs, the system that creates the event can optionally provide a
pointer as well, which also defaults to NULL.  When the event system
gets around to dispatching that event to that callback, it compares the
two pointers, and only calls the callback if they match.  Since both
pointers default to NULL, we don't have to change any existing code to
cope since NULL == NULL and the callback gets called anyway.

Any callback that has to decide whether or not to ignore or consume an
event has to do some sort of test.  In the case of the exe read data
event, most likely this is a comparison of the pointer to the exe
returned when the code created the exe against the exe pointer in the
read data event structure passed to the callback.  This also means that
the code has to conspire to pass the exe pointer to the callback
somehow.  This will be as the callbacks data pointer, as part of a
structure that is passed via that data pointer, or in a global
pointer/structure.  In this instance we are exchanging a function call
and return plus a pointer comparison with just the pointer comparison.
This is a speed gain during the processing of multiple callbacks at a
time when speed might be very important.

At the other end of the spectrum, for user input events that don't make
use of this extra pointer, we are adding a pointer comparison.  This is
a speed loss at a time when speed is probably not important at all.

Each event type must document what it uses as a pointer so that code
registering callbacks knows what to use if anything.

As stated earlier, solution two is to not use the ecore event system
for exe read data.  As I am writing the exe data reading code
(fork'n'pipe) I will want what I just described as the solution, I'll
probably just copy ecore events and modify it as stated.

Someone is likely to propose solution zero, ignore all this waffle and
just use unmodified ecore events for fork'n'pipe.  If we go with this
solution, the time will come when some idiot will pass all the GNU
source code through several line buffered fork'n'pipes and complain
when his system freezes.  At that point, I'll point out that I had
predicted this exact problem, and point to solution one.

Attachment: pgpJ8lnW6V4W8.pgp
Description: PGP signature

Reply via email to