Jump to content

XControls and user events


Recommended Posts

Here is a XControl design question:

If I want to use user events to generate events in the owning VI where should these events be created? (not generated)

Option A

Inside the XControl and with a read-only property a VI could get the user event and register for it.

Pro: you have a tight integration between the control and the event. Using the Init and Uninit abilities you can exactly define when to create and destroy the event references

- you can have multiple listeners

Con: you can create useless event references which get very much events which are never written.

Option B

Inside the owning VI, with a special 'Create Event Reference' VI and a write-only property.

Pro: only event references that are really used are created

Con: a special event typedef is needed.

After writing these things up I lean toward option A.

I only have used option B, does anyone have some experience with option A?

Ton

Link to comment

Option B has another Pro, in that it's more consistent with standard events, where you create the event explicitly in the caller VI. I don't have any experience with either method, so I can't comment more.

Interesting. I view it the other way in that since it is a broadcast mechanism it doesn't matter where you create it as it is the reposibility of the receiver to register for it.

Here is a XControl design question:

If I want to use user events to generate events in the owning VI where should these events be created? (not generated)

Option A

Inside the XControl and with a read-only property a VI could get the user event and register for it.

Pro: you have a tight integration between the control and the event. Using the Init and Uninit abilities you can exactly define when to create and destroy the event references

- you can have multiple listeners

Con: you can create useless event references which get very much events which are never written.

Option B

Inside the owning VI, with a special 'Create Event Reference' VI and a write-only property.

Pro: only event references that are really used are created

Con: a special event typedef is needed.

After writing these things up I lean toward option A.

I only have used option B, does anyone have some experience with option A?

Ton

I would consider A more in line with event implementations not only in LV but in other languages too (for example in Delphi, control events are only available once you place that control on the FP).

In B, what if you create an event that doesn't exist in the XControl? B also requires external initialisers where as A is self contained and the events are available just by placing the control. However, in the case of A I think you need a backup plan for if you generate events and there are no registered recipients since you will just fill up the message queue.

Link to comment

Here is a XControl design question:

If I want to use user events to generate events in the owning VI where should these events be created? (not generated)

Option A

Inside the XControl and with a read-only property a VI could get the user event and register for it.

Pro: you have a tight integration between the control and the event. Using the Init and Uninit abilities you can exactly define when to create and destroy the event references

- you can have multiple listeners

Con: you can create useless event references which get very much events which are never written.

Can you negate (at least partially) this con by having the event creation happen when the read-only property vi is run. With a simple flag in the XControl state could you then check whether to generate events and destroy it in the UnInit ability ?

I guess there's still be the problem of the user getting the event property, using it once and then ignoring all suvsequent events - in which case you might want a method to inform the XControl that this was the case and replace a simple flag with a reference counting mechanism to keep track of the event being used in multiple places.

In general I like option A better because the XControl defines what the user event data type is - just seems a little bit more robust to my mind.

Link to comment
Option A

Inside the XControl and with a read-only property a VI could get the user event and register for it.

Pro: you have a tight integration between the control and the event. Using the Init and Uninit abilities you can exactly define when to create and destroy the event references

- you can have multiple listeners

Con: you can create useless event references which get very much events which are never written.

Why would you create useless event references? You shouldn't actually create the event reference until someone calls the property to get the reference. You do the creation in "UserEventReference Read.vi" on demand, not when it is constructed. You can have a single event that the facade knows how to process that is "add this event reference to the references my event structure is already registered for" that you fire the first time someone calls the property read to create the event. Your facade VI processes this meta-event to add to the array of registered references.

Link to comment

Why would you create useless event references? You shouldn't actually create the event reference until someone calls the property to get the reference. You do the creation in "UserEventReference Read.vi" on demand, not when it is constructed. You can have a single event that the facade knows how to process that is "add this event reference to the references my event structure is already registered for" that you fire the first time someone calls the property read to create the event. Your facade VI processes this meta-event to add to the array of registered references.

To clarify, I mean an event that is generated inside the XControl and where the owning application is registring for.

I can see how to create the event reference in the property node instead of the init ability.

Ton

Link to comment

I'd probably go with creating the event ref in the property node when it's called the first time, all subsequent times you just return the previously created ref that you stored in your state data.

Although in the past I've used the concept you suggested in option A in some of my classes. It does create a 'useless' ref until someone registers for it, but I doubt it takes up a lot of resources since the actual event queues are created when a listener registers for the event (I guess..).

Link to comment

I struggled with this question for a while too, though not specifically with regards to XControls.

If I'm understanding your question, first you have to ask yourself, "should I use an observer pattern or a publish-subscribe pattern?" Often these two terms are used interchangably but I think there are important differences. In an observer pattern, the code being observed has no knowledge of any observers. It might have 20 observers; it might have 0. It doesn't care and just continues doing what it's doing. If you think of observing something through a telescope, the thing you're looking at generally doesn't know you exist, much less that you are interested in it. In a publish-subscribe pattern the subscriber has to register with the publisher and the publisher generally keeps track of who the subscribers are and how many subscribers there are. Consider subscribing to a newspaper; you call up the publisher, they record your name and address, and then they deliver the paper to your roof until you tell them to stop.

Which pattern you choose depends on how you want to manage the lifetime of the publisher/observable code module. If you want the module to self-manage it's lifetime and stop only when nothing depends on the events it generates, use the publish-subscribe pattern. If you're willing to manage the module's lifetime yourself or if you don't care if the module stops while other code is waiting on its events, use the observer pattern.

User events work pretty well for the observer pattern. However, if you expose the User Event Refnum, be aware that observing code can destroy the refnum and generate an error in the observable code. I prefer to expose the Event Registration Refnum and keep the User Event Refnums private. That protects the observable code from malicious code and inexperienced developers. The downside is that it's harder for the observing code to dynamically register/unregister for a subset of the events the observable code produces. I've experimented with using an event manager class as mediator between the observable code and the observing code. The event manager registers for all the events the observable modules expose. The observing code then tells the event manager which events it is specifically interested in. I think there must be a better way but I haven't figured it out yet.

I don't have a very good feel for implementing a robust publish-subscribe pattern. My sense is injecting user events into the publisher isn't the best way to do it. Callback VIs? Separate subscribe/unsubscribe methods for bookkeeping? I don't know; I haven't explored it enough.

For the observer pattern, I prefer option A. I have an example on my other computer. I'll try to post it later today.

However, in the case of A I think you need a backup plan for if you generate events and there are no registered recipients since you will just fill up the message queue.

I agree with everything you said except this. I believe the user event queue exists at the event structure, not the the user event refnum or event registration refnum. If there are no registered event structures, there is no queue to fill up.

Why would you create useless event references? You shouldn't actually create the event reference until someone calls the property to get the reference.

Is the resource overhead of generating a user event on a user event refnum or event registration refnum that is not wired into an event structure high enough that this is something we need to worry about? Or is this just an easier way to manage the bookkeeping of which events the listener is interested in?

Your facade VI processes this meta-event to add to the array of registered references.

Since the user event refnums and event registration refnums are strongly typed, you can only put them in an array if they have the same data type. What's the recommended technique for dynamically registering/unregistering for events that have different data types?

  • Thanks 1
Link to comment

I agree with everything you said except this. I believe the user event queue exists at the event structure, not the the user event refnum or event registration refnum. If there are no registered event structures, there is no queue to fill up.

Ooooh. I didn't realise this. I knew windows event queues can be swamped with messages if there is no sink and if you can get enough in before the message managerr times them out. I assumed (obviously wrongly) that it worked in a similar fashion

Since the user event refnums and event registration refnums are strongly typed, you can only put them in an array if they have the same data type. What's the recommended technique for dynamically registering/unregistering for events that have different data types?

Thinking about this. If you are going to supply an event refnum from the caller (I prefer encapsulated but what the hell), the Xcontrol can bundle its events onto it. Then when you wire the resultant event cluster through a register to an Event case, you can choose not only the calling vis events, but the Xcontrols as well. It would give the Xcontrol tight integration to the callers events and a fairly close approximation to how real other event driven langueages operate.

Edited by ShaunR
Link to comment

I agree with everything you said except this. I believe the user event queue exists at the event structure, not the the user event refnum or event registration refnum. If there are no registered event structures, there is no queue to fill up.

I wouldn't be to sure about that.. In my understanding the queue exists at the event registration refnum. This is also illustrated by the LV help that states you shouldn't wire an event registration ref to more than one event structure to prevent race condition situations. So in order to have multiple observers observe you, you have to either publish the event refnum or a unique event registration ref for every observer.

Since the user event refnums and event registration refnums are strongly typed, you can only put them in an array if they have the same data type. What's the recommended technique for dynamically registering/unregistering for events that have different data types?

Practical in your XControl (or class or whatever code module you want to make observable) you have a finite number of possible dynamic events that you can send and they all serve their own purpose. I'd simply put them all in an event-ref cluster inside my internal data structure. No need for putting them in arrays imho. (It would be the same as wanting to stuff several different clusters in an array... which ofcouse is possible using variants classes.. :lol:)

  • Like 1
Link to comment

Thinking about this. If you are going to supply an event refnum from the caller...

I'm not following. Why would you send a User Event Refnum from the caller to the xctl if the goal is simply to get a cluster of the xctl's User Events? I don't want the xctl to control when the calling code registers for events.

Then when you wire the resultant event cluster through a register to an Event case, you can choose not only the calling vis events, but the Xcontrols as well.

This still requires the calling code to have direct access to the xctl's User Event Refnums, which I don't think is a good general purpose pattern. Too much risk of rogue code destroying the refnum somewhere along the way. If that happens the xctl has to be smart enough to create a new User Event Refnum and all the listeners have to be smart enough to reregister for the event. IMO this causes too much overhead for the xctl developer, couples xctl code to listener code too tightly, and most importantly, simply allows poorly written listeners too much control over the xctl's internals.

--------------------

Here's the example code I developed several months ago. I cleaned it up but didn't test it thoroughly so you may find bugs.

post-7603-125555503738_thumb.png

--------------------

[Edit]

I wouldn't be to sure about that.. In my understanding the queue exists at the event registration refnum. This is also illustrated by the LV help that states you shouldn't wire an event registration ref to more than one event structure to prevent race condition situations. So in order to have multiple observers observe you, you have to either publish the event refnum or a unique event registration ref for every observer.

Yep, you're right. I stand corrected.

"If the user event is not registered, the Generate User Event function has no effect. If the user event is registered but no Event structure is waiting on it, LabVIEW queues the user event and data until an Event structure executes to handle the event. You can register for the same user event multiple times by using separate Register For Event functions, in which case each queue associated with an event registration refnum receives its own copy of the user event and associated event data each time the Generate User Event function executes."

Looks like the Observer example I just posted needs some... uhh... re-engineering. frusty.gif

Observer Pattern.zip

Edited by Daklu
Link to comment

To join your XControls event registration with some other event registration (ie whatever else the user may have), the use of your XControl could always bundle the two registrations together:

post-9667-125555196218_thumb.png

Now that is a trick I didn't know! (and I thought I knew all about events).

About the 'client code can delete the user event' discussion, they can as easily delete the registration refnum.

In this case the general rule is 'delete what you created, leave what you received'.

Ton

Link to comment

About the 'client code can delete the user event' discussion, they can as easily delete the registration refnum.

In this case the general rule is 'delete what you created, leave what you received'.

Yep, with the way I was doing it they certainly can and that would screw everything up. Thanks to Jeffery's correction and AQ's remark about waiting until someone asks for the reference before creating it, maybe a better way to handle it is to register the user event when the user calls GetRegRefnum and pass the RegRefnum out. That way (presumably) each caller gets a unique RegRefnum and they can't step on those of other listeners.

Maybe that's what AQ is saying...? I read it as creating the User Event, not the Registration Refnum, in the GetUserEvent method, and I don't understand why that is preferable. Creating unique user events for each listener requires a lot more supporting code than creating unique registration refnums.

Edited by Daklu
Link to comment

Maybe that's what AQ is saying...? I read it as creating the User Event, not the Registration Refnum, in the GetUserEvent method, and I don't understand why that is preferable. Creating unique user events for each listener requires a lot more supporting code than creating unique registration refnums.

The idea is not to create a unique user event for each listener, just store the user event ref on the first call to GetUserEvent and return the previously created refnum on all subsequent calls. Each observer registers it with it's own reg-events node.

As for the observers being able to destroy the event ref, I see your point, but in general I second Ton's statement: 'delete what you created, leave what you received'.

Also: Document your API well and your users should be OK. And in the case your user doesn't read the docs/description, make sure errors are propagated correctly when attempting to generate an event.

Link to comment

I'm not following. Why would you send a User Event Refnum from the caller to the xctl if the goal is simply to get a cluster of the xctl's User Events? I don't want the xctl to control when the calling code registers for events.

I was referring to the Option B where a create method was used. It is identical the what ShaunH describes (using the bundle to integrate events) but from within the control instead of external.

Link to comment

As for the observers being able to destroy the event ref, I see your point, but in general I second Ton's statement: 'delete what you created, leave what you received'.

Also: Document your API well and your users should be OK. And in the case your user doesn't read the docs/description, make sure errors are propagated correctly when attempting to generate an event.

At the risk of beating a dead horse...

I think it's a huge mistake to let the reliability of your code depend on documentation and alert developers. (Remember when Windows was a cooperatively multitasking OS?) In a single application where the developer has complete control over where user events are destroyed it's probably okay, though I still prefer the encapsulation of keeping the user events private. For reusable code modules, plug in architectures, or other systems where third party code would be using your user events, I think you're just asking for trouble. A well-written error message that crashes your application still crashes your application.

It's not any harder to pass out a RegRefnum than it is a UserEvent. The added protection it provides eliminates an entire category of potential errors future developers have to test for. If the additional protection is essentially free, why wouldn't that be the preferred way to do it? (I feel like I'm missing something fundamental about user events, but I don't know what it is.)

Link to comment

At the risk of beating a dead horse...

I think it's a huge mistake to let the reliability of your code depend on documentation and alert developers. (Remember when Windows was a cooperatively multitasking OS?) In a single application where the developer has complete control over where user events are destroyed it's probably okay, though I still prefer the encapsulation of keeping the user events private. For reusable code modules, plug in architectures, or other systems where third party code would be using your user events, I think you're just asking for trouble. A well-written error message that crashes your application still crashes your application.

It's not any harder to pass out a RegRefnum than it is a UserEvent. The added protection it provides eliminates an entire category of potential errors future developers have to test for. If the additional protection is essentially free, why wouldn't that be the preferred way to do it? (I feel like I'm missing something fundamental about user events, but I don't know what it is.)

I would generally export a RegRefnum. Like you say, it gives extra protection.

One thing to be aware of though.... If you export the a RegRefnum based on the SAME User Event then whenever the Event occurs, the UserEvent is exposed in the Event Structure. Rogue code could then actually destroy the UserEvent and put ALL processes listening to that Event in Limbo.

The only other way would be to implement some kind of two-tier system where the initial internal Event fires an Array of other Events for which each listener has received their own RegRefnum. This way destroying a UserEvent will only affect that listener, not all listeners. Bit more complicated but it should be do-able.

I have asked NI why they expose the Refnum but I have not really heard a compelling reason to do so. I personally would much prefer to NOT have the naked Refnum exposed, or at least have the option to declare it in such a way that it is not exposed (for compatibility reasons).

Shane.

Edited by shoneill
Link to comment

One thing to be aware of though.... the UserEvent is exposed in the Event Structure.

Excellent observation. I hadn't thought of that. frusty.gif It certainly puts a damper on the idea of using user events across code module boundaries.

The only other way would be to implement some kind of two-tier system where the initial internal Event fires an Array of other Events for which each listener has received their own RegRefnum. This way destroying a UserEvent will only affect that listener, not all listeners. Bit more complicated but it should be do-able.

Yeah, but having to reimplement that for every code module? Ugh. Since the UsrEv and UsrEvReg refnums are strongly typed I can't even generalize the functionality in a parent class. I'll have to write custom code for each set of user events exposed by a code module. NI targets non-professional programmers with Labview so I shouldn't be surprised ease-of-use trumps robust code.

If we want to use user events to cross code module boundaries, I guess we're stuck with implementing event mediators (i.e. your suggestion) in each module or Ton's Option B, registering the listener's user event with the subject. I suspect the mediator is slightly easier for the developer using the code module, though I don't see any clear advantages of one over the other. I guess I'll have to implement them and see.

Link to comment

Great discussion. I played around with these ideas a while back and... well... it was a toss up. I attached to this post my xcontrol in both forms. The xcontrol data type is the User Event in the original implementation, and it's the registered event in the alternative design. Pros and cons are as you say, but I find the impact on useability of either design to be minimal. I like your general rule 'delete what you created, leave what you received', though I feel very ackward doing NOTHING with my dynamic registration refnum out of the event structure. This is why in my alternative design I do actually unregister for events in the owning VI -- knowing full well that if I use the registered event in multiple processes I ought to rendezvous prior to unregister.

Sorry, you can't open both xctl's (or examples) simultaneously due to name conflicts. Perhaps I'll fix that and repost.

Clock.zip

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.