Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

When using an event based component I often feel some pain at maintenance phase.

Since the executed code is all split around it can be quite hard to figure what will be all the code part that will be involved at runtime.

This can lead to subtle and hard to debug problems when someone adds some new event handlers.

Edit from comments: Even with some good practices on-board, like having an application wide event bus and handlers delegating business to other part of the app, there is a moment when the code starts to become hard to read because there is a lot of registered handlers from many different places (especially true when there is a bus).

Then sequence diagram starts to look over complex, time spend to figure out what is happening is increasing and debugging session becomes messy (breakpoint on the handlers manager while iterating on handlers, especially joyful with async handler and some filtering on top of it).

//////////////
Example

I have a service that is retrieving some data on the server. On the client we have a basic component that is calling this service using a callback. To provide extension point to the users of the component and to avoid coupling between different components, we are firing some events: one before the query is sent, one when the answer is coming back and another one in case of a failure. We have a basic set of handlers that are pre-registered which provide the default behavior of the component.

Now users of the component (and we are user of the component too) can add some handlers to perform some change on the behavior (modify the query, logs, data analysis, data filtering, data massaging, UI fancy animation, chain multiple sequential queries, whatever). So some handlers must be executed before/after some others and they are registered from a lots of different entry point in the application.

After a while, it can happens that a dozen or more handlers are registered, and working with that can be tedious and hazardous.

This design emerged because using inheritance was starting to be a complete mess. The event system is used at a kind of composition where you don't know yet what will be your composites.

End of example
//////////////

So I'm wondering how other people are tackling this kind of code. Both when writing and reading it.

Do you have any methods or tools that let you write and maintain such code without to much pain ?

share|improve this question
    
You mean, besides refactoring out logic from event handlers? –  Telastyn Jul 16 '12 at 13:18
    
Document what goes on. –  user1249 Jul 16 '12 at 13:29
    
@Telastyn, I'm not sure to fully understand what do you mean by 'besides refactoring out logic from event handlers'. –  Guillaume Jul 16 '12 at 13:33
    
@Thorbjoern: see my update. –  Guillaume Jul 16 '12 at 13:41
2  
It sounds like you are not using the correct tool for the job? I mean, if the order matters, then you should not be using plain events for those parts of the application in the first place. Basically if there are limitations on the order of events in a typical bus system, it's not a typical bus system anymore, but you are still using it as such. Which means you need to address that issue by adding some extra logic to handle the order. At least, if I understand your problem correctly.. –  stijn Jul 16 '12 at 14:11
show 5 more comments

6 Answers

It sounds like you are looking for State Machines & Event Driven Activities.

However, you might also want to look at State Machine Markup Workflow Sample.

Here you are a short overview of state machine implementation. A state machine workflow consists of states. Each state is composed of one or more event handlers. Each event handler must contain a delay or an IEventActivity as the first activity. Each event handler can also contain a SetStateActivity activity that is used to transition from one state to another.

Each state machine workflow has two properties: InitialStateName and CompletedStateName. When an instance of the state machine workflow is created, it is put into the InitialStateName property. When the state machine reaches the CompletedStateName property, it finishes execution.

share|improve this answer
2  
Whilst this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference. –  Thomas Owens Jul 16 '12 at 14:00
    
But if you have dozens of handlers attached to each state, you'll be not so fine when trying to understand what is going on... And every event based component may not be described as a state machine. –  Guillaume Jul 16 '12 at 15:27
add comment

I think proper logging can help quite a big. Make sure that every event thrown/handled is logged somewhere (you can used logging frameworks for this). When you're debugging, you can consult the logs to see the exact order of execution of your code when the bug occurred. Often this will really help narrow down cause of the problem.

share|improve this answer
    
Yes, that's an helpful advise. The amount of produced logs can then be frightening (I can remember having some hard time to find the cause of a cycle in the event processing of a very complex form.) –  Guillaume Jul 16 '12 at 14:35
    
Maybe one solution is to log the interesting information to a separate log file. Also, you can assign a UUID to each event, so you can track each event more easily. You can also write some reporting tool that allows you to extract specific information from the log files. (Or alternatively, use switches in the code to log different information to different log files). –  Giorgio Jul 29 '12 at 8:07
add comment

Event driven code is not the real problem. I fact I have no problem following logic in even driven code, where call-back are explicitly defined or in-line call-backs are used. For example generator style callbacks in Tornado are very much easy to follow.

What is real hard to debug are dynamically generated function calls. The (anti?)pattern which I would call the Call-back Factory from Hell. However, this kind of function factories are equally hard to debug in traditional flow.

share|improve this answer
add comment

So I'm wondering how other people are tackling this kind of code. Both when writing and reading it.

The model of event driven programming simplifies coding to some extent. It has probably evolved as a replacement of big Select (or case ) statements used in older languages and gained popularity in early Visual development environments such as VB 3 (Don't quote me on the history, I did not check it)!

The model becomes a pain if the event sequence matters and when 1 business action is split across many events. This style of process violates the benefits of this approach. At all costs, try to make the action code encapsulated in the corresponding event and don't raise events from within events. That then becomes far worse than the Spaghetti resulting from the GoTo.

Sometimes developers are eager to provide GUI functionality that requires such event dependency, but really there is no real alternative that is significantly simpler.

Bottom line here is that the technique is not bad if used wisely.

share|improve this answer
    
Is their any alternative design to avoid the 'worse than the Spaghetti' ? –  Guillaume Jul 16 '12 at 15:45
    
I am not sure there is an easy way without simplifying the expected GUI behavior, but if you list all your user interactions with a window/page and for each determine exactly what your program will do, you could begin grouping code into one place and direct several events to a group of subs/methods that are responsible for the real processing of the request. Also separating code that only serve the GUI from code that does the back end processing may help. –  Emmad Kareem Jul 16 '12 at 15:50
    
The need to post this message came from a refactoring when I moved from a inheritance hell to something based on event. On some point it is really better, but on some other it is quite bad... Since I've already had some trouble with 'events gone wild' in some GUI, I'm wondering what can be done to improve maintenance of such event sliced code. –  Guillaume Jul 16 '12 at 16:09
    
Event-driven programming is far older than VB; it was present in the SunTools GUI, and before that I seem to remember that it was built into the Simula language. –  kevin cline Jul 16 '12 at 17:43
    
@kevincline, thanks for the clarification. –  Emmad Kareem Jul 16 '12 at 20:10
show 3 more comments

What has worked for me is making each event stand on its own, without reference to other events. If they are coming in asynchroniously, you don't have a sequence, so trying to figure out what happens in what order is pointless, besides being impossible.

What you do end up with are a bunch of data structures that are getting read and modified and created and removed by a dozen threads in no particular order. You've got to do extemely proper multi-threaded programming, which is not easy. You've also got to think multi-threaded, as in "With this event, I am going to look at the data I have at a particular instant, without regard to what it was a microsecond earlier, without regard to what just changed it, and without regard to what the 100 threads waiting for me to release the lock are going to do to it. Then I will make my changes based on this even and what I see. Then I am done."

One thing I find myself doing is scanning for a particular Collection and making sure that both the reference and the collection itself (if not threadsafe) are locked correctly and synchronized correctly with other data. As more events are added, this chore grows. But if I was tracking the relationships between events, that chore would grow a lot faster. Plus sometimes a lot of the locking can be isolated in its own method, actually making the code simpler.

Treating each thread as a completely independent entity is difficult (because of the hard-core multi-threading) but doable. "Scalable" may be the word I'm looking for. Twice as many events take only twice as much work, and maybe only 1.5 times as much. Trying to coordinate more asynchronious events will bury you quickly.

share|improve this answer
    
I have updated my question. You are totally right about sequence and async stuff. How would you handle a case where event X must executed after all the others event are processed ? It is looking like I have to create a more complex Handlers manager, but I also want to avoid to expose too much complexity to the users. –  Guillaume Jul 16 '12 at 16:28
    
In your set of data, have a switch and some fields that are set when event X is read. When all others are processed, you check the switch and know you have to handle X and you have the data. The switch and the data should stand on their own, actually. When set, you should think, "I have to do this job," not "I have to hande X." Next problem: how do you know the events are done? and what if you get 2 or more events X? Worst case, you can run a looping maintenance thread that checks the situation and can act on its own initiative. (No input for 3 secs? X switch set? Then run shutdown code. –  RalphChapin Jul 16 '12 at 16:43
add comment

I've found that processing events using a stack of internal events (more specifically, a LIFO queue with arbitrary removal) greatly simplifies event-driven programming. It allows you to split the processing of an "external event" into several smaller "internal events", with well-defined state in between. For more information, see my answer to this question.

Here I present a simple example which is solved by this pattern.

Suppose you are using object A to perform some service, and you give it a callback to inform you when it's done. However, A is such that after calling your callback, it may need to do some more work. A hazard arises when, within that callback, you decide that you don't need A any more, and you destroy it some way or another. But you're being called from A - if A, after your callback returns, cannot safely figure out that it was destroyed, a crash could result when it attempts to perform the remaining work.

NOTE: It's true that you could do the "destruction" in some other way, like decrementing a refcount, but that just leads to intermediate states, and extra code and bugs from handling these; better for A to just stop working entirely after you don't need it anymore other than continue in some intermediate state.

In my pattern, A would simply schedule the further work it needs to do by pushing an internal event (job) into the event loop's LIFO queue, then proceed to call the callback, and return to event loop immediately. This piece of code it no longer a hazard, since A just returns. Now, if the callback doesn't destroy A, the pushed job will eventually be executed by the event loop to do its extra work (after the callback is done, and all its pushed jobs, recursively). On the other hand, if the callback does destroy A, A's destructor or deinit function can remove the pushed job from the event stack, implicitly preventing execution of the pushed job.

share|improve this answer
    
Thanks for the link, it's a great answer. –  Guillaume Jul 30 '12 at 10:42
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.