Jump to content

Unexpected Event Structure Non-Timeout Behavior


Recommended Posts

Hi all,

I can say that I have come across a behavior like this. It is similar to having a case structure called by a string and someplace somewhere you mistype the case you want to go to or you don't use case insensitive match and mix up the case. If you haven't wired up a catch all that tells you you have misspelled you need to debug to find it. This could be quick if you an experienced LabVIEW programmer but what about a new user? Having worked with a dozens of other programmers that haven't created their own programming style yet and somewhat hide code it is easy to have this case come up. Mostly in my previous code there is a single case to handle communications between processes and we tried to fit other dynamic events in there when necessary to avoid the weirdness and control how to search for where events are called from. There needs to be better control over the events, like what exists in all Semaphores. Perhaps it is something as simple as an error case, which is for unhandled events. A default case that if the event fires it goes there if there is no event case to handle it. The case structure has a default, which is a catch all, why doesn't the event structure?

Dean

Link to comment

I didn't know about this behavior, but I agree with Chris. I also think that while configuration options do make it faster to write code, they also tend to make the code harder to understand and maintain. Requiring developers to explicitly define what they want to do by writing code isn't necessarily a bad thing.

Doesn't anyone else think that a "timeout" that can never time out is in the least bit strange in concept?

It *can* timeout, it just *doesn't* because it keeps receiving events before the timeout expires.

Also, using the timeout frame is a common use-case for reading other data that needs polling or other updates between events.

It is a common use-case, but it is also (IMO) an overused and misguided use case. In principle, anything that is in the timeout case (case structure or event structure) should be code that is entirely optional to the correct operation of the component. The component has no control over when it receives messages/events so there is no way to guarantee the timeout case will execute; ergo, putting code in the timeout case that "needs" to be executed is a flawed approach. The "correct" way to handle the situation is to create a parallel loop that sends messages/user events at regular intervals to trigger those other tasks. Using a timeout condition for necessary processing is a convenient shortcut--and I sometimes use it--but it shouldn't be considered a robust solution.

post-7603-0-95668200-1300216867_thumb.pn

(Image taken from this post.)

Additionally, I use the timeout for polling stuff too, but sometimes I would rather a regular execution regardless of current event activity. I have always thought it would be nice if you could configure the Event Structure to fire off an event every x milliseconds without much programming. Configuring a timer (or multiple timers) would sure be nice.

On a recent thread I think alluded to a TimerSlave component I have partially finished. It's essentially a version of the timer loop in the image above, encapsulated in a slave loop object and exposing the timing and message name parameters as part of the creator method. I've been thinking about releasing it as part of LapDog. Would something like that be useful to you?

But what I do have a strong position on is that if I could inspect/flush/otherwise manipulate the queue for an Event Structure I and others would've been able to understand, work around, and even exploit this behavior for better software years ago rather than wasting time cursing at LabVIEW.

All the more reason to use queues instead of user events. ;)

Link to comment
An event is either signalled or is not not. No other language I know of has event time-outs...

Other languages aren't based on dataflow :)

I have to come down on the side of allowing the user to require the event structure to explicitly handle each event (I think that's what crelf proposed, if I'm following all this) just like removing the default value from case structures requires the user to explicitly implement all cases.

I'm suggesting that could be an option, as long as we keep the current behaviour.

Link to comment

On a recent thread I think alluded to a TimerSlave component I have partially finished. It's essentially a version of the timer loop in the image above, encapsulated in a slave loop object and exposing the timing and message name parameters as part of the creator method. I've been thinking about releasing it as part of LapDog. Would something like that be useful to you?

Always keen to have a look at your stuff thumbup1.gif

FWIW I still think it would be nice if Timers could be configured for an Event Structure and LabVIEW handled the implementation.

It is a common use-case, but it is also (IMO) an overused and misguided use case. In principle, anything that is in the timeout case (case structure or event structure) should be code that is entirely optional to the correct operation of the component. The component has no control over when it receives messages/events so there is no way to guarantee the timeout case will execute; ergo, putting code in the timeout case that "needs" to be executed is a flawed approach. The "correct" way to handle the situation is to create a parallel loop that sends messages/user events at regular intervals to trigger those other tasks. Using a timeout condition for necessary processing is a convenient shortcut--and I sometimes use it--but it shouldn't be considered a robust solution.

I think everyone would agree on that, except in this case here: we explicitly register and know the events the module will handle at run-time and accept that wrt the Timeout case. However, other events we did not expect now affect the behavior of our code.

All the more reason to use queues instead of user events. ;)

Everything has pros and cons that influence our design decisions (Queues vs Events etc...) - its nasty surprises I don't like wacko.gif

Link to comment

I headed to the IE and found these two ideas on the subject of Timers in the Event Structure.

Cyclic event for event structure ... a kind of timeout but cyclic !

Combine the event structure and the timed loop

Given we implement the functionality one way of another, it may be nice to have it native.

Also, there is (the start of) a few interesting comments in the above ideas.

Maybe there should be an option to unset this should be available (like an "allow unhandled events to be registered" checkbox in the event dialog if the dynamic event handles are shown).

As they are a few ideas that have come out of this thread, the authors should definitely post them up to the IE so we can vote them up!

Link to comment

I think ShaunR is just shy too admit thats how he likes to program too

Absolutely nothing of technical merit to add here, but I can not believe you actually used the words "ShaunR" and "shy" in the same sentence. :P

Link to comment

Not thinking about the underlying mechanisms, here is how I (used to) think about how the Register For Events node and event cases in the Event Structure work:

The Register For Events node adds to the list of event sources and their corresponding events that I can register for by adding to the list of Event Sources in the Edit Events dialog for the Event Structure (at edit time). In spite of its name, I didn't think that this node causes LabVIEW to start looking for (registering for?) those events--I thought the actual checking for events (registration?) only occurs at runtime if a case (configured from the appropriate item in the Edit Events dialog) in the Event Structure is defined to handle it. Why would the Event Structure look for a front panel control's Value Change event only if I created a case for it, but look for a calling VI's front panel control's Value Change (dynamically registered) event whether or not I created a case for it?

The Register For Events node does allow you to change the source of the event at runtime, but does not allow you to change the type of event that can be acted upon (this can only be changed at edit time). Nor does it allow you to start or stop looking for (registering or unregistering for?) the event (even though the event source could potentially be changed to a null object). I guess that is another reason why I figured the node only defines events that can be "registered for" in the Event Structure, but does not actually "register" the events.

So the actual behavior is that registration for a non-dynamic event (this is called "static registration") occurs only if an event case is created for that event; but registration for dynamic events occurs at the Register For Events node, whether or not an event case is created for it. After writing this sentence, I see that the behavior is actually consistent with the terminology and documentation. But if you rename the Register For Events node to something like "Define Dynamic Events", I think the terminology would be consistent with the behavior I (and I think a lot of other people) were expecting and, to me, seems more logical and self-consistent. Basically, the Register For Events node should add items to the Edit Events dialog, and creating a case to handle the event should register for the event.

So, having thought about it more, it appears the behavior we see is actually correct given the terminology. But it would still make more sense to me to have all events registered the same way (i.e., defining an event case). Given that this behavior is not likely to change, I do agree with having a default event case or generating an error if an event isn't explicitly handled.

Link to comment
Basically, the Register For Events node should add items to the Edit Events dialog, and creating a case to handle the event should register for the event.

That would introduce a nasty race condition into user events.

Consider this block diagram. Under LV's current scheme, the Event Structure is guaranteed to hear the event. If you changed the behavior of the Register For Event node to not actually do the registration, you would miss the event. That safety net makes possible a number of event architectures that would not otherwise be possible. Moreover, the Event structure would have to redo its registration every time the while loop iterated, which would either be a performance penalty or another timing hole, depending upon how it was implemented.

post-5877-0-77151600-1300316339_thumb.pn

Given that this behavior is not likely to change, I do agree with having a default event case or generating an error if an event isn't explicitly handled.
The view inside R&D seems to be moving toward, "If a user had found this during the LV 6.1 beta, we would definitely call this a bug and change LV to break break the VI unless there was a case for every dynamic event. But it has been there for 9.5 years. So how the heck do we fix it now?" I don't know what if anything will happen with this CAR. It is getting a thorough debate. At the moment, it could be anything. Ideas I've heard include just updating the documentation, doing something special during Execution Highlighting to show that the event fired but nothing happened, mutating to have a default case, or mutating to add a no-op case for every dynamic event and *then* breaking any future VIs that violate the rule. We'll see what the future holds.
Link to comment

That would introduce a nasty race condition into user events.

Consider this block diagram...

I have used that construct so many times that if the behavior changed, almost all of the applications I've built would have race conditions introduced into them. That behavior is one of the reasons I like the event architecture so much, it is quite elegant in how it separates event consumption from handling. Not that the LabVIEW event implementation is without shortcomings though...

Link to comment

This really is a very interesting topic!

After reading through the discussion, I think the current behaviour is absolutely correct. But I'm glad I understand it now :-)

I absolutely agree with Justin Goeres: There should be tools to monitor and influence event queues!

They could be similar to "Get Queue Status" and "Flush Queue".

More important, I think we really need a Timer Event similar to the Timer in LabWindows/CVI.

I know how to programm something like this in LabVIEW, but I really think this should be a standard functionality that works out of the box.

People should be encouraged to use this instead of the Timeout case, because from my experience I can say that in most cases a novice LabVIEW programmer uses the Timeout case, what he really wants is a timer that fires every T ms.

One very simple implementation (from the user's point of view) that comes to my mind would look like this:

You can chose either a Timeout or a Timer event case (but not both in the same event structure).

The Timeout behaves just like the current Timeout.

The Timer event is triggered every T ms, no matter what happens.

I could also think about much more complex APIs with Timer Object References etc.

This could bring us a load of flexibility, but it might be to complex to be used by novices.

In an ideal world we would have both: The simple Timer value that is connected directly to the event structure and Timer Objects that can be registered using "Register For Events" and manipulated using Property Nodes (change timer value, activate, deactivate).

Link to comment

Always keen to have a look at your stuff

I'll try to wrap it up and post a preview soon.

FWIW I still think it would be nice if Timers could be configured for an Event Structure and LabVIEW handled the implementation.

So do I, but there are still implementation decisions that NI would need to make that will add complexity and/or limit their usefulness in some situations. Should the timer event be added to the front of the event queue or the back? Maybe it should be configurable? If so, how? Can timer events be turned on and off at run time? Can timer events be created or the timing parameters modified at run time? How do we configure multiple independent timer events on a single event struct? These are the kinds of functionalities people will want with automatic event timers. Perhaps not at first, but it won't be long before users discover the limitations of a simple timer event are too restrictive.

Maybe the wizards at R&D already have an idea for how to cleanly implement timer events. Maybe it looks a lot like a user event. I don't know... I do know that events in general hide a lot of stuff from LV developers in an effort to keep them easy to use. It's that hidden stuff that causes the nasty surprises nobody likes.

I think everyone would agree on that, except in this case here: we explicitly register and know the events the module will handle at run-time and accept that wrt the Timeout case. However, other events we did not expect now affect the behavior of our code.

I'm not sure what you mean by, "other events we did not expect." If an event structure is registered for an event, it should expect to receive that event notification at any time. An event structure can't receive messages it didn't expect.

That's a cheap shot.

My apologies Justin. I didn't intend it to be shot at all--cheap or otherwise.

Link to comment
  • 4 weeks later...

I was the one who brought this to Justins attention at the CLA Summit, and I was a bit surprised at how many didn't know this, but even more so at how many saw this as a bug. Let me explain;

I have many times run into unexpected behaviour (not just in programming :rolleyes:), and often times these experiences just add weight to the "experience-belt". It wasn't more than about a year ago I myself ran into the mentioned issue with unhandled events resetting the timeout counter. At the exact time of discovery of course a huge bubble of "WTF?" appeared above our heads. But it really wasn't much different to the first time I experinced how sharing a registered event refnum between two event structures leads to (at the time) unexpected behaviour, or when I experienced the weirdness when two timed structures have the same name, or the unclarity of it all when I discovered that some FP parts of reentrant VIs can't be unique, while they can when spawned from a vit. All cases with perfectly reasonable explanations, and all cases that made me a better and more precise programmer.

I know how this works now, and it makes sense for me - I just take it into consideration when I use an event case structure. Maybe it's because I do not use the timeout case that much? I see the timeout case of an event structure as something of a "lazy way out" of a task that really was asking for something more deliberate. We all know that we shouldn't count on the timeout being deterministic in any way, why are we surprised when this fact stretches to "it may never fire"? Even though the cause for it not firing is another one but imprecision or low priority. I only use the timeout case for doing a check that needs to be done if none of the other event cases does it - a check for an error condition for instance, so I can end my code snippet and inform someone about it. But in reality this probably should be taken care of by a watchdog using a deliberate and specific user event for this task - it's just easy to use the timeout case. I merely know that if I register more events than I handle in a given event structure, then I can't rely on this fallback mechanism - and in those cases I do something else (i.e. the proper thing instead).

So I'm in the group that accept this as nominal albeit opaque behaviour. Maybe the solution is a "Default" case when not handling all registered user events? That could coexist with the Timeout case, make the feature obvious, and allign it a bit with the ordinary case structure/enum scenario (the latter presents a huge pitfall when configuring a case to handle a range of values that truncate to value_m...value_n by the way, but that's another and much more hazardous topic than this).

I think it's probably because most people only use the event case for the UI (and generally wire -1 to it). And only a few are brave enough to base a whole inter-process messaging system purely on events.So if anyone is going to find it....it's you biggrin.gif

I'm with Michael A. here. I love events, and use them all the time for messaging. A favourite use case of mine is having a number of dynamically dispatched modules draw their own input and output events from a master pool, and then just start generating data to, and receiving data from, all the other running modules. One exemplary case is this customer project; about 4,000 VIs, Real-Time system + Host, 50 modules divided between the two, about 1200 unique events and thousands of connections criss-crossing the deck. The modules load up, they draw their events and the event handler subsystem just takes it from there. Practically indistinguishable from magic :shifty:.

Cheers,

Steen

Link to comment
  • 2 weeks later...

How about just adding the 'Make This Case Default' option to the event case. If it is set, then that case fires when the registered event received does not have a case defined for it. This seems like the most consistent way to handle this and match existing paradigms.

You could allow existing event cases to not have the default case for backwards compatibility, but all new event cases dropped will have the default case included and the dev will need to remove it to get back to the current behavior. That way, you are making a conscious decision to not handle all registered events with some case. Also, you could prompt the dev with a warning that unhandled events will reset the timer when they remove the default case.

Finally, you could leave off the default case until they 'Show Dynamic Event Terminals', then add it at that time. To make it clear that it was added, force the event structure to display that case.

Just my $0.02...

[edit] frusty.gifoops.gif Didn't notice a page 2 before posting. So, just add my suggestion to all the others for a default case. rolleyes.gif

Edited by John Lokanis
Link to comment

... if I could inspect/flush/otherwise manipulate the queue for an Event Structure I and others would've been able to understand, work around, and even exploit this behavior for better software years ago rather than wasting time cursing at LabVIEW.

I agree with Justin, this whole issue is just a reflection that the event structure is more of a "black box" than we thought. A very dark black box ph34r.gif. If there was a way to "probe" for an event structure we could "see" when an event that has been registered for and it is not being handle "fires" and resets the timer. Which seems to be one of the ideas tossed around inside NI:

The view inside R&D seems to be moving toward, "If a user had found this during the LV 6.1 beta, we would definitely call this a bug and change LV to break break the VI unless there was a case for every dynamic event. But it has been there for 9.5 years. So how the heck do we fix it now?" I don't know what if anything will happen with this CAR. It is getting a thorough debate. At the moment, it could be anything. Ideas I've heard include just updating the documentation, doing something special during Execution Highlighting to show that the event fired but nothing happened, mutating to have a default case, or mutating to add a no-op case for every dynamic event and *then* breaking any future VIs that violate the rule. We'll see what the future holds.

I don't think we necessarily need to break future VIs, because as it has been discussed before, there might be someone out there than in these 9.5 years might be taking advantage of this "feature". So, I vote for "updating the documentation", "doing something special during Execution Highlighting to show that the event fired but nothing happened" and the one that I just mentioned about having a way to probe the event queue.

I agree with jgcode that we need to vote the good ideas regarding this up. I did a quick search in the Idea Exchange and I couldn't find an idea regarding being able to configure timers for an event structure.

FWIW I still think it would be nice if Timers could be configured for an Event Structure and LabVIEW handled the implementation.

jgcode, have you added that idea? if so where, I would like to vote for it.

Well those were my 2 pesos ;)

I hope I see you all at next year's CLA Summit. biggrin.gif

Fab

Link to comment

I agree with jgcode that we need to vote the good ideas regarding this up. I did a quick search in the Idea Exchange and I couldn't find an idea regarding being able to configure timers for an event structure... ...jgcode, have you added that idea? if so where, I would like to vote for it.

I headed to the IE and found these two ideas on the subject of Timers in the Event Structure.

Cyclic event for event structure ... a kind of timeout but cyclic !

Combine the event structure and the timed loop

Given we implement the functionality one way of another, it may be nice to have it native.

Also, there is (the start of) a few interesting comments in the above ideas.

As they are a few ideas that have come out of this thread, the authors should definitely post them up to the IE so we can vote them up!

Hi Fab!

I searched the IE and found the above so I didn't add my own.

FWIW, I don't think I want the features of Timed Structure just a simple configured Timer(s) that would expire and get added to the end of the (event) queue - as that is the behaviour I would get if I coded it myself.

If you think this warrants a new IE please let me know or go ahead and create it and I will vote for it cool.gif

Link to comment
  • 3 months later...

I'm coming to this discussion after NI Week and having listened to the topic reverberating off the walls post Stephen's session. FWIW, I think those who think this is a bug or "should be changed anyway" are confusing the concept of timeout with the concept of "Default (Case)". Timing out the Event structure is not a guaranteed method to "catch" any and all unspecified events and why should it be, esp if a timeout is given to constrain it. An event is an event and, while each event will have a timestamp, none are polled but all are received -- regardless of the action taken by the structure. It's up to the programmer to "clean up the mess" he/she creates in registering the event. An unregistered event is STILL an event -- and can't NOT be an event. It's just not a very wisely programmed event: like an uncaught setjmp() back in the REALLY old days always "set your gets before you get sets into your code"....

Ok so that's just two cents worth from a non CLA who is, in many ways, really old school so this is text and not in cool graphics.

  • Like 1
Link to comment
An event is an event and, while each event will have a timestamp, none are polled but all are received -- regardless of the action taken by the structure.

Exactly!

An unregistered event is STILL an event -- and can't NOT be an event.

I agree with that, as long as I can change it to:

An unhandled event is STILL an event -- and can't NOT be an event.
Link to comment

Posted Today, 02:41 PM

snapback.pngVal Brown, on 09 August 2011 - 03:26 AM, said:

An event is an event and, while each event will have a timestamp, none are polled but all are received -- regardless of the action taken by the structure.

Exactly!

snapback.pngVal Brown, on 09 August 2011 - 03:26 AM, said:

An unregistered event is STILL an event -- and can't NOT be an event.

I agree with that, as long as I can change it to:

Val Brown misquoted by crelf said: (Hey! I did NOT say that the damn IDE did)

An unhandled event is STILL an event -- and can't NOT be an event.

My comments begin here:

I think what you might mean -- to be precisely accurate linguistically -- is that an event is STILL an event -- and can't not be an event. AND a REGISTERED event is not JUST an event. It is an event that has an additional component (viz the "registration") and should, therefore, expect some additional response, such as to be received by some event handler in a way that doesn't just get "lost in cyber hell" if the programmer falls asleep at the wheel.

Yes, I understand that argument and it's the basis for a great architectural discussion -- one that already happened about 30 years ago in some circles, but that's another story for another time (if you're not already yawning....).

Now regardless of that history, it is exactly that extension that I object to -- because an event is an event. Even registering an event shouldn't have an influence on the event per se, nor it's overall construct unless it's an object and being an object becomes an intrinsic attribute of an event; or else the default IDE behavior should be to populate a case SOMEWHERE that explicitly receives that (now) registered event and forces an error if not explicitly handled by the programmer.

Now the argument could be made that the event handler could have some other facility for handling registering events that do not have an explicitly declared interface; and essentially that is what is being argued here by those saying "isn't this a bug?". And perhaps that is an attribute that even should be integral to a well designed IDE but I'm not so sure about that -- at the conceptual level. And, yes, all of these various ideas would make life easier in some ways. So, in the end, it's frequently expediency that prevails.I just think that the philosophical issue -- history aside -- remains important, regardless of how it is answered in a particular IDE like LV.

OK, back off the soap box and time for more beer...ah actually single malt.

FWIW as a user I say: NI make it easy for us. At another level though I think these various changes are a problem waiting to happen down the line because they break (OK bend) the purity of the IDE at the abstract level.

Edited by Val Brown
  • Like 1
Link to comment

A lot of people seem to miss the fact that adding a default case WILL NOT make the VI behave the way Justin wanted it to work. A default case would mean an event fired, which would mean the timeout case would not fire. Making the timeout and the default case be the same case would mean that the timeout case would fire far more frequently than it should. The conversation about possible changes to this behavior appears to think that adding a default case would fix this issue. It wouldn't. Adding a default case just makes it clearer what is going on with the event structure... it does not give the behavior Justin tried for.

With regard to the default case, Fabiola, you argue against changing the behavior because it would break older VIs. If you read my post again, you'll note that I included "mutate older VIs to add the default case", so they wouldn't change behavior at all.

Link to comment

Yes, this is an aspect of the kinds of confusion raised by thinking this is "wrong" behavior and should be "handled in the background" by the structure itself instead of explicit programming actions on the part of the programmer.

A lot of people seem to miss the fact that adding a default case WILL NOT make the VI behave the way Justin wanted it to work. A default case would mean an event fired, which would mean the timeout case would not fire. Making the timeout and the default case be the same case would mean that the timeout case would fire far more frequently than it should. The conversation about possible changes to this behavior appears to think that adding a default case would fix this issue. It wouldn't. Adding a default case just makes it clearer what is going on with the event structure... it does not give the behavior Justin tried for.

With regard to the default case, Fabiola, you argue against changing the behavior because it would break older VIs. If you read my post again, you'll note that I included "mutate older VIs to add the default case", so they wouldn't change behavior at all.

Link to comment
  • 2 years later...

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.