Jump to content

Event Architectures


Recommended Posts

I'm starting to plan out the architecture for an application I'm going to have to write by the end of the year and really want to be able to use some kind of object based event system for synchronization.

Basically ObjectA will have a whole slew of events, and other objects are free to subscribe and unsubscribe to public ones as they see fit. Standard stuff.

The rub being that some events need to be synchronous: the code which invokes the event must block until all subscribers have returned. Now correct me if I'm wrong, but this excludes the ability to use the built in user events without getting creative. For static events, ones generated by say, the user interface, you can check a box when configuring an event structure case to lock the front panel until event is processed, which essentially makes that particular event frame* synchronous: the action which invoked the event won't return until the event has been processed. For user events, this option isn't available, they appear to be entirely asynchronous.

Now I have a few ideas brewing in my head, some more mature than others, and I think it can be implemented abstractly enough that the resulting framework can be reasonably light weight and extensible. I'm just wondering if anyone is aware of existing LabVIEW frameworks that do this? I don't need anything fancy like inter-process/remote communication, although recent discussions have really got me thinking about it, seems like it could be a fun challenge to add that layer into the mix.

*Aside: Something I haven't characterized well, but I find fascinating: if you have multiple event structures pending on the same static event, LabVIEW appears to be able to sort out the fact that each individual frame might or might not be synchronous. I find the idea that synchronicity is strictly a property of the delegate and not the event itself interesting.

Cheers,

-m

Link to comment

I have been pondering for some time:

Should synchronous behavior be part of the messaging system or part of the behavior of the client?

(I don't know. We always use asynchronous messaging. Some of our clients send a message and wait to get some data ultimately indicating that some external event took place. But the sending client's state machine implements this behavior. I suppose for some applications truly synchronous messaging might be more appropriate....)

Sorry. This doesn't really answer your question!

Link to comment

This sounds like the job for your basic list-based observer pattern:

post-906-030240500 1280008866_thumb.png

Each interested subscriber (observer) registers itself with the subject. When the subject needs to 'send the event' to its subscribers, it calls the Notify method which in turn calls the Update method on all registered subscribers.

Link to comment

*Aside: Something I haven't characterized well, but I find fascinating: if you have multiple event structures pending on the same static event, LabVIEW appears to be able to sort out the fact that each individual frame might or might not be synchronous. I find the idea that synchronicity is strictly a property of the delegate and not the event itself interesting.

<thinking out loud>

Events, by definition, are instantaneous. They don't have a duration so the concept of synchronicity as applied to events is meaningless.

The event handling structure is synchronous in the same way every other structure is synchronous. Ton has pointed out elsewhere that there may be multiple event queues: one for front panel event and one for user events. Enabling the "lock front panel" option appears to simply hide additional elements in the front panel event queue from the event structures until the "locked" frame has finished. The timeout case (and presumably user events) will still execute in a parallel event structure. (Personally I'm not sure what the purpose is of the "lock front panel" option. It doesn't prevent front panel events from occurring; it just stores them in a pre-buffer so the user isn't aware that his button click was captured.)

</thinking out loud>

Any chance you come from a C# background? A couple months ago I discovered that in C# event handlers run in the same thread as the event. (Don't know if that's true for other languages as well.) If there are multiple event handlers for a single event then they execute synchronously instead of in parallel. I saw a post from one user explaining this and then asking, "why would you want event handlers running in a separate thread?" There's some cognitive dissonance going on here because I could only think, "why wouldn't you want event handlers running in a separate thread?"

I'm starting to plan out the architecture for an application I'm going to have to write by the end of the year and really want to be able to use some kind of object based event system for synchronization...

The rub being that some events need to be synchronous: the code which invokes the event must block until all subscribers have returned. Now correct me if I'm wrong, but this excludes the ability to use the built in user events without getting creative.

I think events in general are the wrong tool to use if you're trying to achieve synchronization. My feeling is having subscribers register callbacks with the publisher is a more appropriate technique to use. That said, you might be able to get there by sending notifiers along with the event data and counting the number of notifications you receive. I think this is inherently unsafe though. Events are broadcasted. They might be picked up by 100 subscribers; they might be picked up by 0. The event producer has no way of knowing if the event consumer is actually registered to receive the event.

Link to comment

We always use asynchronous messaging.

Me too in most cases.

This sounds like the job for your basic list-based observer pattern:

Yep, you've pretty much got it. Like I said, basic standard stuff. Just nothing that works out of the box, so to speak, in LabVIEW.

Events, by definition, are instantaneous. They don't have a duration so the concept of synchronicity as applied to events is meaningless.

Naw, there's definitely cases where synchronous events are used. You don't even need to look outside of LabVIEW. Don't have LV handy at the moment, but I think NI calls them filter events? Stuff like the Panel Close? event, which allows you to discard it. This event is by definition synchronous, since whatever signals the event must block until the event frame finishes executing to know whether or not to discard the event and proceed with the follow up event. I've also worked with similar events in other UI environments with in bubble style events, where an event is generated by an object, and bubbles up to each containing object until either everything has serially had their turn or the event gets discarded. Also done plenty of these style events other languages where the event delegates are pretty much just function pointers. Events are signaled by rolling through the pointer array serially (for synchronous cases).

Any chance you come from a C# background? A couple months ago I discovered that in C# event handlers run in the same thread as the event. (Don't know if that's true for other languages as well.) If there are multiple event handlers for a single event then they execute synchronously instead of in parallel. I saw a post from one user explaining this and then asking, "why would you want event handlers running in a separate thread?" There's some cognitive dissonance going on here because I could only think, "why wouldn't you want event handlers running in a separate thread?"

Sure, I've done some C#, but more C/C++. I've been around enough programming languages where I see them all as just grammar (even LabVIEW). They get a job done, you just need to know how to use each. There are valid use cases for both style of events, and what should be used really just depends on the behavior you want to design around. I just never was aware of an "out of the box" way to do the synchronous calls in LabVIEW.

Norm!

That's awesome. I remember seeing a discussion about that a while ago but completely missed the sync/async part of the topic, thanks for jogging my memory. I'll have to check that out first thing when I get in to the office on Monday. I worry though that since the behavior change from asynchronous to synchronous isn't documented, that it might be a bug and I'm not sure I'd be comfortable releasing code which requires that behavior. Still warrants further investigation though.

Funny you also play with the notifier. That's one of the ways I was thinking of being able to return a value from the events.

Link to comment

Norm!

That's awesome. I remember seeing a discussion about that a while ago but completely missed the sync/async part of the topic, thanks for jogging my memory. I'll have to check that out first thing when I get in to the office on Monday. I worry though that since the behavior change from asynchronous to synchronous isn't documented, that it might be a bug and I'm not sure I'd be comfortable releasing code which requires that behavior. Still warrants further investigation though.

Funny you also play with the notifier. That's one of the ways I was thinking of being able to return a value from the events.

Just to poke an old horse, LVx (LabVIEW Export) was designed for this exact process. The problem is that the lack of proper documentation has always scared people away, but at the core of it, it provides an event based messaging system that has already built in it, the ability to go synchronous or asynchronous. Each different command is a different child class of LVx and an individual command is comprised of both command parameters and return response. So you send your command across, and if you have it configured to wait for response, then you get your command back loaded w/ the results. It's quite handy and I use it for all interprocess communications (it's all over LabVIEW Speak fwiw).

It has the added benefit of working not only from VI - VI in the same application instance, but also across application boundaries and even network boundaries.

Nancy Hollenback recently went crazy with it and even deployed it to a RT system successfully.

There is a learning curve, but if the key fits, it's easier to turn a key than pick the lock.

With NI week coming up, i'm going crazy getting content ready. LVx might be one of the packages I get ready for it.

Does this sound like something you could use for this?

Link to comment

Naw, there's definitely cases where synchronous events are used.

I think we are using the word "event" in slightly different contexts. I was thinking about an "event" along the lines of how it is used in physics and separating the instantaneous event itself from the bits that process the event in software. Sometimes this kind of thinking helps lead me to solutions. This time... not so much. :D

Can I ask what kind of things you're doing that require synchronous events? Off the top of my head I think my initial reaction to your problem would have been to try and change the design so synchronous events wouldn't be needed. Clearly using synchronous events is a better route to take. I'd like to get some understanding of what use cases people encounter where they are used.

There is a little trick I found out about a little while back.

You can register a UserEvent with a callback VI

Cool trick Norm! :star:

I thought Register Event Callback was only for ActiveX/.Net objects. In fact, I went and checked the help file to see if I had missed anything and found this: "Use this function to register .NET and ActiveX events only." Any idea what nasties might be lurking under the covers?

Just to poke an old horse, LVx (LabVIEW Export) was designed for this exact process.

How old is this horse? I can't remember ever hearing about this and google didn't turn up anything. Is anything published that provides more information about it?

Link to comment

...finally we could start a VI running asynchronously and pass it parameters w/ out needing to use crappy VI server methods.

Anyone who wants this, PLEASE go vote for this idea:

Adding a "(don't) wait until done" option to the Call By Reference node

How old is this horse? I can't remember ever hearing about this and google didn't turn up anything. Is anything published that provides more information about it?

Did you try "LVx LabVIEW"? It brings up this.

  • Like 1
Link to comment

Did you try "LVx LabVIEW"? It brings up this.

Thanks for the link, I doubt I could have found it.

There has been a major update (memory leaks and usability improvements) to the framework.

There is a very basic example within the package I've made but really, to make a new command is a 7 step process that warrants proper documentation or a Utility VI to do most of it for you. But once that's done, luckily, usage is straightforward.

I'll give my obligatory "I'll do what I can to make something shareable and understandable" which unfortunately I've fallen short on over the years

Link to comment

With NI week coming up, i'm going crazy getting content ready. LVx might be one of the packages I get ready for it.

Does this sound like something you could use for this?

It might be. I remember the example you posted a year (?) ago, but I didn't have the time to wade through it determine how to implement it into my existing framework. As you said, adding commands did some somewhat involved. I'd love to see a current version of it, if for any reason even just an academic curiosity. But please, don't rush it if it's not already on the burner.

I think we are using the word "event" in slightly different contexts. I was thinking about an "event" along the lines of how it is used in physics and separating the instantaneous event itself from the bits that process the event in software. Sometimes this kind of thinking helps lead me to solutions. This time... not so much. :D

Indeed! I was using it basically in the context of the observer pattern previously mentioned.

Can I ask what kind of things you're doing that require synchronous events? Off the top of my head I think my initial reaction to your problem would have been to try and change the design so synchronous events wouldn't be needed. Clearly using synchronous events is a better route to take. I'd like to get some understanding of what use cases people encounter where they are used.

It's tough to say. The architecture is still rather nebulous in my brain. I definitely am going with some kind of application object which maintains ownership of data, and allows an arbitrary amount of views which interact with the app. Views would be loaded dynamically, and could also be defined outside the scope of the core application (plug-ins). It's the exact means of interaction that I'm still deciding. Simple API via exposed class methods? A messaging system? Combination of the two? Something else entirely?

Synchronous "events" creep in to one way I was thinking of designing my UI. I like the idea of bubble style events for some features. Say the App object owns ViewA, which in turn owns ViewB. Now ViewB generates an event, it would bubble up to ViewA. If ViewA handles the event, everything ends there, however ViewA decides it has not handled the event, or if ViewA doesn't implement the event observer at all, the event bubbles up to the Application. Note this implies that these types of events return a value. A system like this demands synchronicity because whatever means of signaling is implemented, the code must block at each observer to determine if that node is the end of the event chain. Situations like that though can also open an application to deadlock though, I'm not sure if I want to go there.

I don't know if I want to implement this type of event system though. I was more curious if there was any way to make LabVIEW events even compatible with a synchronous model rather than rolling my own using various combination of sync objects.

Link to comment

The architecture is still rather nebulous in my brain.

I can explain things I've done to address a similar situation. I know it's not what you're looking for but maybe it will give you some ideas. Grab a cup of coffee and pull up a chair--I'm about to get long-winded again. (I've explained this in more detail than you need for the benefit of future readers.)

I'm really bad at UI design. Fortunately there are other developers in my group who are quite good at it; however, since requirements change so frequently during development it doesn't make sense to put in the time to create a polished UI when it's going to have to change next week. I tried to figure out a way I could develop the application functionality using a prototype UI while allowing us to easily drop in a polished UI without editing the functional source code. I ended up using a slightly altered MVC architecture that allows for a somewhat service-based design.

post-7603-013662800 1280259095_thumb.png

The Model contains all the functional logic and processes needed to execute the test. The Views are solely responsible for interacting with the user. The Model and each View are independent services the controller instantiates and monitors. When a service raises an event, the controller catches it, figures out what needs to be done, and uses methods provided by each service to issue the appropriate command.

My Views do not maintain any information about the state of the Model. For example, if I have a Start Test button and a Test Running indicator, my first instinct is to set the indicator to True when the button is pushed and issue a StartTest command to the Model. This can lead to synchronization problems with multiple views. Instead a view will expose a StartButtonPushed event and a Set TestRunningIndicator method. The View tells the controller the button was pushed and waits for the controller to tell it when to set the indicator value. The Model exposes a StartTest method and a TestStarted event.

The execution sequence goes something like this...

User clicks StartTest...

causes... View:StartButtonPressedEvent

triggers... Controller:StartButtonPressedEventHandler

calls... Model:StartTest()

calls... Model:RaiseTestStartedEvent() [alternatively calls... Model:RaiseTestStartedErrorEvent()]

triggers... Controller:TestStartedEventHandler

calls... View:Set TestRunningIndicator()

This lets me implement an arbitrary number of view displaying an arbitrary subset of functionality without worrying too much about keeping all the views synchronized with the latest state of the model. As long as the controller sends the correct messages to the views in its event handlers everything's good. Commands issued from one view will automatically update the model's status in all other views.

This architecture doesn't allow for view plugins. View code is built into the controller at edit time. You can solve that by implementing an abstract ViewBase class in the controller module and let the View modules implement concrete child classes. It also doesn't address your original problem: How do I make the Model stop and wait for a user response without hard coding it in place? Well, I didn't. Not like you want anyway. I ended up having the Model expose the process in small enough incremental methods that the Controller could trap the errors and wait for a user response before calling the next step in the Model's process. I don't really like that solution. Next time I'll probably try putting an abstract ErrorHandler class in the Model and let the Controller define and inject a concrete implementation at runtime. I did implement synchronous events in some Model-View interactions by sending a notifier along as part of the event's data object and it worked okay.

I posted this thread a couple months ago speculating on using the Chain of Command pattern to address the problem of not being able to wait on a user response when a low level sub vi generates an error. I really have no idea what the end result would look like but it's something I've wanted to experiment with.

I like the idea of bubble style events for some features. Say the App object owns ViewA, which in turn owns ViewB. Now ViewB generates an event, it would bubble up to ViewA. If ViewA handles the event, everything ends there, however ViewA decides it has not handled the event, or if ViewA doesn't implement the event observer at all, the event bubbles up to the Application. Note this implies that these types of events return a value. A system like this demands synchronicity because whatever means of signaling is implemented, the code must block at each observer to determine if that node is the end of the event chain.

It sounds like you're asking for overridable events here. ViewB doesn't handle the event so you want to move up the inheritance hierarchy until something does handle the event. However, you don't necessarily want ViewB to be a child of ViewA. I use an EventManagerLibrary that could give you the ability to override events. The EventManager class exposes a method for each event that can be raised. You could drop the library in your Application and create a child EventManager class in ViewA and a grandchild EventManager class in ViewB, each one overriding the events it knows how to handle. (As you said, making it synchronous requires passing a notifier along with the event.)

On the other hand I thought you also needed to be able to make an event synchronous when multiple clients will all handle the event. For example, if you have multiple views connected to the application and the app raises an event all views need to respond to. Did I misunderstand this part?

Link to comment

My Views do not maintain any information about the state of the Model.

Yes, this is exactly what I'm shooting for. I'm mostly modeling the behavior after the Document/View architecture I used in my old C++/MFC programming days.

For example, if I have a Start Test button and a Test Running indicator, my first instinct is to set the indicator to True when the button is pushed and issue a StartTest command to the Model. This can lead to synchronization problems with multiple views...

Indeed, I've also learned that mistake. By setting the UI state immediately in response to an event triggered in the UI means that the view is keeping some amount of state information. This is a no-no, as unless one is really careful, synchronization issues can go unnoticed and be a generous source of buggy behavior.

Basically I'm going to have an Application class which exposes a public API for all views to use. When a view needs to start a test, it fires Application:StartTest(), which in turn will signal an Application:TestStartedEvent. It is the responsibility of any View classes to subscribe to the TestStartedEvent if they need to change their UI in response to this state change. During that response they can query the Application class as necessary for data to update the UI.

This architecture doesn't allow for view plugins. View code is built into the controller at edit time. You can solve that by implementing an abstract ViewBase class in the controller module and let the View modules implement concrete child classes.

Agreed, which is why my class hierarchy is panning out to be somewhat different from the one you attached:

post-11742-073635900 1280329911_thumb.pn

It also doesn't address your original problem: How do I make the Model stop and wait for a user response without hard coding it in place?

I think that's not so much a problem of the chosen architecture, but what is ultimately used as an "event". If an out of the box, LV User Event is used, no, it won't. But if I roll my own it can easily be implemented.

It sounds like you're asking for overridable events here.

Yes, but of course ViewB and ViewA are not related except by virtue of having a common ancestor. I'm wrestling with how I want the event manager to act, I'm exploring a few different directions here. I'll post more when I've thought at least one of them out more thoroughly. One sticking point I've run into in my design is that I have to allow for multiple instances of a given View. I had a good solution but it relied on singetons, but I think I can adapt it...more later, hopefully before NI Week.

On the other hand I thought you also needed to be able to make an event synchronous when multiple clients will all handle the event. For example, if you have multiple views connected to the application and the app raises an event all views need to respond to. Did I misunderstand this part?

No, you got it. But I think I've changed my mind. Events aren't really the way to go in that situation: If all views are required to respond an event, this is better implemented via an abstract class method of the View class, not an event. Since it's a method, it is by definition synchronous. Similarly, if an event can be, but is not required to be overriden a simiple overridable method will do the trick.

Argh, I somehow hit the submit button by mistake, please bear with me as I rush to finish the post...there doesn't seem to be a delete option?

Anyways, your CoC idea is interesting as well. I've been wrestling with how to deal with errors in background processes that run asynchronously. I...don't have a solution yet.

Edited by mje
Link to comment

Sounds like we're pretty much on the same page. It's nice to know I'm not straying too far from the beaten path. (I do wish events were easier to deal with in LV though.)

I'm wrestling with how I want the event manager to act, I'm exploring a few different directions here.

Give me a shout if you want to take a look at my EventManagerLibrary and I'll post a link. I'd be interested in any feedback--positive or negative.

Link to comment

Ok, here's what I got. You may be aware I have a messaging architecture I've been using for a bit now, MessagePump. It has proven to be very robust to me over the last two years, and I've been wanting to "fix" it a bit by updating it and making it part of a more comprehensive library.

Ultimately, the idea of an event is now almost gone from my framework, and replaced with Messaging. Fundamentally they're the same: some arbitrary signal which is broadcast to a queue owned by some other task. As I buried down into the design of an event based system, I found myself running into much of the same situations I did when designing the messaging framework, and I really didn't like how similar they were going to end up being. So I think I've decided to expand on the existing system and add a few extra classes, and ultimately forget about starting a new type of architecture. Here's where I've landed:

post-11742-085550500 1280429255_thumb.pn

Note that NONE of this beyond the existing MessagePump class has been implemented, and even that class is going to change when I get around to making version 2.0 as there were a few decisions I made on it that I'm not particularly happy with, and the class really ought to be redesigned now that DVRs exist.

The whole idea of registering as an event observer is gone (as are events). Instead, messages will be passed around continuously, a given View is free to implement whatever message handler it wants. By design a MessagePump can take any message, ones it does not implement handlers for are ultimately discarded harmlessly by the MessagePump base class. When a View wishes to broadcast a message, it must decide if it wants to contain the scope of the message to itself and it's children, or if it wants the message to be global. In the former case it posts the message to itself, in the later case the View obtains a reference to the owning Application and broadcasts it from there.

Synchronous messaging, if needed, can be performed a number of ways by the existing MessagePump framework, no changes needed there.

There will be limitations though. The idea of "bubble" events won't work without some extra work. The bubbling system requires the signal to work it's way up the hierarchy, from source, to parent, to grandparent. The system I've described works in reverse, things naturally flow down from source, to child, to grandchild.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.