Thread: Event based system design

  1. #1
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262

    Event based system design

    Hi everybody,

    I'm considering implementing a general purpose event-based system in C++. Events will be asynchronous. My question regarding design is: should event handlers have a return value, or should it send back the "return value" in the form of an event, itself?
    What is the most common way? What are the advantages and disadvantages of both?

    I'll show some C++-pseudo code to describe what I mean. The exact implementation details will differ, but it's just to give you a general idea of what it would be used like.

    The return value method would look similar to this:
    Code:
    SomeEventInterface iface;
    [...]
    
    AsyncEvent<ReturnType> ret1 = iface.send(E1);
    AsyncEvent<ReturnType2> ret2 = iface.send(E2);
    
    if(ret1.hasResult()) {
       [...]
    }
    
    waitResultAny(ret1, ret2);
    Whereas the event return method would look similar to this:
    Code:
    ReplyInterface myIface; // <-- Maybe shared_ptr.
    SomeEventInterface iface;
    
    iface.send(myIface, E);
    Where "ReplyInterface" itself is an event handler that handles the events that "SomeEventInterface" can send.

    While the former is easier to work with, the former can be implemented as basis of the second. Ie the "AsyncEvent" class in the former example can be implemented to be the ReplyInterface itself. This is definitely true if there can only be one type of event sent as a reply (which is equal to the return type method which can have ony one type), but it does get a bit less clear if there can be more than one type of event (such as SuccessEvent and FailureEvent).

    What is the "proper" way of doing this? And why?


    Thanks in advance!

  2. #2
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    I'm not sure an event handler should have a return value at all. What if multiple handlers are all registered for a single event, how will you handle an a priori unknown number of return values, and what if some of them constitute errors?

    If an event handler needs to trigger something else to happen, it should happen by another event, in a truly event-driven system.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Thanks for your reply! I think I agree with that - though many event based systems do seem to allow a return value.

    Anybody who disagrees with this?

  4. #4
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    My question regarding design is: should event handlers have a return value, or should it send back the "return value" in the form of an event, itself?
    If you are going "general purpose", you have no choice but to support the possibility of "returning" a value.

    Forcing a situation where event triggers event is unnecessarily convoluted when a given event is packaging the result of a possibly related task-oriented event.

    Consider what happens in such cases from the client perspective without native support for "returning" a value: the client needs to register an event handler, trigger the event, wait on the result, package the result from within the even handler when the event is handled, flag the result as ready within the event handler, deregister the event handler, and release resources related to the event and its synchronization.

    I am aware you said "asynchronous". That's exactly my point in laying out the above situation. Arbitrary handling of asynchronous events is a fine goal, but a "general purpose" system must necessarily also be capable of expressing "package" events from client perspective.

    A task-oriented event would be one like "Load $(Filename) Into $(std::string)". From client view, this is a perfectly normal opportunity for performance improvements. (This improvement would be not waiting on the read operation to complete from the same code that waits on connections.) Consider a simple HTTP server: the client registers an event handler, the client triggers such an event by packing the associated socket into a `client_data' pointer or similar, the event handler when triggered delivers the file, the event handler releases associated resources, the client code is free to continue waiting (listening) on connections unrelated to what the event handler is or is not doing.

    Such an event is not necessarily a "package" event. To continue, the "package" event would be the result of the operation related to an event. So, from client perspective the "Load $(Filename) Into $(std::string)" as a "package" event necessarily "returns" a `std::string' variable.

    Code:
    std::string sFile(SendEvent<std::string>(sSystem, LOAD_FILE, "somefile.txt"));
    The relationship between the two is purely resolved in the flow of client code. It isn't really a mechanical issue in any way.

    It is, I'd argue, a necessary evil to build such a facility into an event system in order to call it "general purpose".

    While the former is easier to work with, the former can be implemented as basis of the second.
    Assuming asynchronous behavior, with appropriate constructs you can "go in either direction" to get the other mechanic.

    This is definitely true if there can only be one type of event sent as a reply (which is equal to the return type method which can have ony one type), but it does get a bit less clear if there can be more than one type of event (such as SuccessEvent and FailureEvent).
    It is not less clear. It is a part of the API.

    It is exactly the same as the simple ISO C interface:

    Code:
    int DoSomething(int, int, int, complex_result *);
    A handler that is given to understand the event `DO_SOMETHING' needs to understand how to provide `complex_result *' as part of the "return" value when that handler has been told that the relevant data is available. That is the duty entirely of such "package" event handlers.

    The type `ReplyInterface' doesn't need to know how or even what facilities `complex_result *' provides. It only needs to provide a means to retrieve a `complex_result *' when one is available do to the completion of the related event.

    The important part to take from this is related to the duties of a general purpose event handler. It is not your job as the creator of the generic event handler to understand every possible event. Your job is to provide a framework for the creation, management, and communication of events. That is your job.

    You can't provide a real implementation of the handler for the `FOO_BAR' event. The client designed the `FOO_BAR' event; the handler which the client created for that event knows how to deal with `complex_result *', and in exactly the same way, a client using a "package" event will be responsible for dealing with a `complex_result *' as the "return" value for the event.

    You can, of course, provide also a foundation of events, but these will follow an API in the same way a C++ library will have an API. You can provide a `BLINK_CURSOR' event with all the trimmings. You can do this because you know the API. The API is whatever you'd like it to be for that event.

    The change to the example is obvious.

    Code:
    ReplyInterface myIface; // <-- Maybe shared_ptr.
    SomeEventInterface iface;
     
    iface.send(myIface, E);
    
    complex_result * s(GetResult<complex_result>(myIface)); // <-- Maybe shared_ptr.
    
    // handle the results of the event
    The client code so marked deals with the results of the event; you have only provided a means to access those results.

    What if multiple handlers are all registered for a single event, how will you handle an a priori unknown number of return values, and what if some of them constitute errors?
    O_o

    The conceptual notion of "returning a value" is entirely unrelated to the C++ notion of "returning a value".

    It is entirely possible to use a slot system with types (You may look to the Boost facilities that add contextual information to exceptions using stream operators for one such example.) to do exactly that. By wrapping such a concept into a "value" type (like `std::shared_ptr') one could indeed return multiple unrelated types unknown to the code originating the request for such a "return" value.

    The calling code could naturally only inspect the "result" for information it was particularly interested in knowing.

    However, I submit that any event is necessarily part of an API including such information as the number and type of "return" values. To follow your logic here, how would multiple event handlers registered to the same event deal with an unknown number of parameters some of which may be optional or invalid? At some point, a decision would need to be made about the nature of the event in question considering how to deal with this scenario or if even dealing with this scenario is appropriate to the given event. Regardless of the solution, it is obviously necessary for all handlers targeting the same event to follow the same API.

    Consider: a handler is registered to an event such as `READ_SOME' which obviously reads some data from a communication channel and stores that information for later use. The core information such as a reference to the communication channel and storage area is crucial to the event. It is necessary for the correct function of any handler that such information be available. How this happens is irrelevant; all that is important to the discussion is that this information must be made available. More information, such as the type of communication channel, could be provided through the same mechanism and, by that same mechanic, flagged as "empty" or similar.

    *shrug*

    Of course, many event systems are designed so that handlers either "nest" or "link" such that the first handler to actually handle an event breaks further processing of that event.

    Anybody who disagrees with this?
    Every event system I've ever seen has facilities that allow the client to "package" an event into an actual return value.

    You can do it however you choose, but helping clients "package" events is the norm.

    Soma

  5. #5
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    Ok thanks for your answer! I'm going to spend some time considering this ;-).

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Document Based Design Problem
    By Fxguy in forum Windows Programming
    Replies: 6
    Last Post: 01-23-2011, 12:19 AM
  2. how to organize an event driven system
    By Marksman in forum C++ Programming
    Replies: 2
    Last Post: 02-04-2008, 06:11 PM
  3. Actors, cues, event based logic.
    By Shamino in forum Game Programming
    Replies: 2
    Last Post: 04-27-2006, 10:58 PM
  4. Embedded system design
    By andyhunter in forum A Brief History of Cprogramming.com
    Replies: 3
    Last Post: 02-19-2005, 07:10 AM
  5. how to design a CMS (content management system) in C++ ?
    By maulikpatel in forum C++ Programming
    Replies: 1
    Last Post: 06-07-2003, 08:37 PM

Tags for this Thread