LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Register for events causes freezes if used in more than one event structure


@Jason King wrote:

Create a subVI that takes a VI (or control) ref and a user event (with data equivilent to a mouse move event) as input.  The VI registers for mouse move events on the reference input.  It would do any logic you want to filter the events, and would fire the user event when the criteria is met.  Run this SubVI in parallel with your main application, which will have an ES registered for the user event.  Make the VI re-entrant and you should be able to use it in multiple VIs at a time.  You'd probably want to have it watch a global or for a user event that will tell it when to terminate.

What, you mean like this? Smiley Very Happy

I have done these things in the past, but architecturally, they don't always "justify" moving the code to another VI.


___________________
Try to take over the world!
Message 21 of 39
(10,821 Views)

And let's not forget that the original question is still unanswered... Smiley Wink


___________________
Try to take over the world!
Message 22 of 39
(10,824 Views)

Sorry, I've been really busy with the beta and haven't had time to devote to this until now, as it requires a rather lengthy explanation.

First, a little background:

With static event registration, an event queue is directly associated with the Event structure, and events are queued only when the front panel of the VI containing an Event structure is running (or reserved to be run by a top-level caller VI).  This is nice and easy to use, but clearly limited to simple use cases and simple applications.

Dynamic events are intended extend LabVIEW event handling by giving the user complete control over the lifetime of event generation.  You control exactly when your app starts to queue events, which events are generated, when the events are handled, and when to stop event generation.  Since these are separate moments in the lifetime of an application, they need to be controlled by separate nodes.  An application using dynamic events at various points may need to:
  1. Register for events to start them being enqueued..
  2. Handle currently registered events as they occur in one or more Event structures, which run subject to the dataflow requirements of the application (taking care that the Event structures run often enough not get backlogged so as to allow your app to be responsive).
  3. Optionally unregister for and reregister for different objects in your app as may be required to maintain application state or transition between different modes in the UI.
  4. Unregister for events when they are no longer needed.
To allow for a flexible system, I hope it is at least 'intuitive' to you that these operations need to be separate entities.

Now, in order for the system to be able to keep track of events as they happen from the moment they are registered, the event queue for dynamic events clearly must be associated with the Register for Events node. From the moment this node executes, the specified events must be enqueued by the system, and there is no way for the system to tell what Event structures will eventually run to actually handle those events and pull them from the queue.  To do so would require static analysis of your entire VI Hierarchy, and even that wouldn't work in the face of dynamic calls, or the use globals or other asynchronous communication to pass the event registration refnum.

Therefore, the queue is associated with the event registration refnum produced by the Register for Events node, and normal LabVIEW dataflow is used to pass this queue reference to the Event structure downstream which will eventually process events from it.  This is a natural way to handle events in a data flow language, and I think this should be pretty intuitive to LabVIEW users as well.

Events in each event queue are reference counted, and each event is guaranteed to be processed in the order delivered (per queue), and to be handled by at least one downstream Event structure (and in correct programs, exactly one.)  When an Event structure starts to process an event from its wired dynamic queue, it increments the ref count. When the diagram finishes or the event is otherwise discarded, the ref count is decreased, and when it goes to zero the event is removed from the queue.  If multiple Event structures happen to try to read from the same dynamic queue, more than one of them may get the event if they happen to execute while the first Event structure is still executing, so the event's ref count may go higher than 1.  But the only guarantee is that it will go to 1 (to make  sure at least one Event structure handles it), and then back to zero.

So the question is, why don't we allow an arbitrary number of Event structures to be guaranteed to process dynamic events from the same dynamic queue.  And the answer is that if we did, we'd have to know ahead of time how many there are, and as I stated above, we can't do this because we have no way to analyze where data flow might take the dynamic event queue refnum without actually executing the code.   (Note to computer scientists out there: this is equivalent to solving the famous Halting Problem, and is NP-complete)

If an Event structure receives an event via its dynamic event queue that it doesn't have a handling diagram for, it silently discards the event.  You could certainly argue that we should trigger some sort of detectable error when this happens (via a 'default' event case or error out terminal, for example, and we have discussed this and it may show up in a future release).  However, you cannot reasonably argue that we should leave the event on the event queue if it is not handled, because that would cause problems in many more situations than the current behavior.  The common, correct use case is to have one Event structure per dynamic event registration, and so if we left the event in the queue if unhandled, expecting it to be handled by another Event structure, then the queue would be blocked indefinitely if there was no one else to handle the event, which is most of the time.

Your idea of having the Event structures own the dynamic queues would not work for several reasons.  Event structures can be anywhere in the VI hierarchy (or not even loaded yet if called dynamically).  There's no guarantee a given Event structure will even run, and there would be no way to know which Event structures the user intends to handle events for which VIs, controls, user events, etc.  So LabVIEW would have to put a copy of each 'enabled' event in each queue for all Event structures currently loaded, and then decide only when those Event structures actually run if the events really belonged in that queue based on which dynamic reference value they got.  Event structures which never run would have their queues grow without bound.  And Event structures in dynamically loaded VIs would miss the events which happened before they were loaded, even though the events were registered for.  Further, if every Event structure had a separate queue per event, it would be very hard to maintain the rule that events are handled by the Event structure in the order they occur.  It's not hard to see that this is an unworkable idea.

So to answer your tangential questions:

Yes, the main reason events are not removed from the queue until the event handling diagram completes is because of filter events.  The event queue actually stores event information from the OS which is not visible on the diagram, such as the original raw system event data, the system window handle the event was on, etc.  This information must be maintained and reprocessed when the filter event handler completes, and if a filter event is registered by multiple observers, must be redispatched to the next event handler until all register handlers have executed (or the event is discarded).

And yes, we still do not consider your original issue a bug. The behavior of event handlers when used incorrectly in this way is by definition undefined, and it's undefined for a good reason.  The extra bookkeeping, mutexes, etc. to make multiple Event structures reading from the same event queue work as you expect would add overhead to the whole event mechanism.  There is a trade off here, and we simply don't consider it worth it to complicate the entire event architecture to make 'bad' code work in predictable ways at the expense of making 'good' code slower.

We may consider adding diagram warnings for the cases where we can detect you are branching an event registration refnum.  However, it's not possible to prevent it completely; you could, for example, write the registration to a global and then read it from multiple unrelated subVIs.

In your example, because the bottom loop's Event structure doesn't use any of the dynamic events the event registration, it would indeed be possible for us to pretend the refnum wasn't even wired in this case and not try to read events from this queue.  But there's no reason for us to do so because it would do nothing to solve the more general problem.  You can populate this Event structure with diagram handlers for any subset of the registered events and the same behavior will occur.

We do  take care to try to ensure that even in cases of 'bad code', we always maintain data integrity, do not leak memory, and do not put any VI in a state where it cannot be aborted via the stop button on the panel.  But the current "undefined behavior" when you try to have multiple Event structures read from one dynamic queue is part of the design, is intentional, and  is a reasonable trade off to make the common, correctly written case work efficiently.  We could do more to make things work consistently in these cases, but it would slow down the common case, and it still would not be possible to eliminate the fundamental race condition that exists because it is not possible in general to know how many Event structures might handle the event.

--Craig

Message Edited by Craig S. on 05-11-2007 04:27 PM

Message 23 of 39
(10,809 Views)
[Addendum post because the previous one exceeded the maximum post limit]

If you are fundamentally unhappy with the way Event structures work in LabVIEW, you might consider using Event Callback VIs instead (available in the Active X palette, but they can be used for builtin LabVIEW events as well).   Now, I personally consider this mechanism inferior to Event structures because callbacks are asynchronous, anti-dataflow, you must have a separate VI per event type, and they force you to move your code out-of-band.  However, they do avoid the issue you have a problem with because you must specify the callback handler VI reference at the same time you register for the events, so there is no question of not knowing which handler will handle the events a priori.

--Craig

Message 24 of 39
(10,796 Views)

Craig S. wrote:

And yes, we still do not consider your original issue a bug.
Thanks, that was all I wanted to know.
 
 
 
 
Just kidding. Smiley Happy
First of all, please note that my current view of the bug is that the ES gets stuck until its timeout expires (without executing the timeout case) even though it still has events in its queue. If this is deemed to be acceptable because the circumstances leading to it are "illegal", then that's fine by me.
 
Second, thank you for the detailed answer. These are highly valued by a lot of members in the community.
 
Third, I would like to point out that my proposal was slightly different from what you described (it wasn't detailed at all, for a good reason, so I can see how you misunderstood it). The point was to turn dynamic events upside down - that dynamic events would only be queued when event structures which registered for them would be in a run state. That is, if an ES was not in memory, the dynamic events associated with it would not be queued for it (although they might be queued for other ESs).
 
This is more in line with static ES behavior (they would also have their queue inflate without control if they were in a section of the code where they were in memory, but not actually executing) and contrary to the behavior of queues, where the Obtain Queue primitive generates the queue. As I mentioned in the original post where I brought it up, I realize how this is problematic both from a compatibility point of view and from other angles.
 
The reason I claimed this would be more intuitive is because it would behave like static events. My experience with ES led me to believe that I could simply handle the same dynamic event and definitely separate dynamic events from the same node. I don't remember whether I read the help for it at the time, but I definitely did not read the caveats. This contradicting behavior between ESs and queues forces the user to choose one model and I suspect that most users will instinctively choose the ES model, because it's related, more powerful and less complicated.
 
In any case, I can't really think of any other two nodes in LV which have to be coupled. The closest would probably be Dequeue Element, but if you were to use two dequeues for a single queue, you could still better control the results (you could re-enqueue or use the preview primitive to decide when to dequeue, etc.). I guess this simply needs to be better stressed in the documentation, and I will file a CAR against that.
 
Lastly, I would like to say that I do much prefer events to callbacks and that I don't have any actual, real world problem.
I ran into this when I was tasked with finishing someone else's program. That program uses an event structure with the signaling property to trigger all of its actions (sort of a state machine) and I was not allowed to change that structure.
At one point, I was given a .NET component I needed to interface with which could take some time to perform its action, so I moved it to another loop, so that it would not delay the UI. Since the main loop was using an ES, I needed to transfer the data back and forth and so I used dynamic events. Since the two loops used different events, I wasn't concerned with using the same node to register their events.
 
After I realized that this is happening, I simply separated the registration into two nodes.
If you want to increase your grief, you could check out this thread which describes another problem I ran into it when running that program (although that might not be a bug in LV). Unfortunately, no AE responded to that thread yet, so I have no way of knowing.

___________________
Try to take over the world!
Message 25 of 39
(10,764 Views)
MENTAL NOTE:
PRINT THIS THREAD!!!!! and reread, reread, make notes, understand, reread, hilight.

One other question: could/can Darren's beloved VI Analyzer detect the forking of a event registration refnum (or ERF)?

Ton
Free Code Capture Tool! Version 2.1.3 with comments, web-upload, back-save and snippets!
Nederlandse LabVIEW user groep www.lvug.nl
My LabVIEW Ideas

LabVIEW, programming like it should be!
Message 26 of 39
(10,721 Views)


@TonP wrote:

One other question: could/can Darren's beloved VI Analyzer detect the forking of a event registration refnum (or ERF)?

I'm guessing it could, since scripting does allow you to know if a wire is forked. Finding the type of the wire seems to require a couple of extra steps, but it's possible that was changed, or that I missed something.

___________________
Try to take over the world!
Message 27 of 39
(10,716 Views)
Nice discussion!
 
The Event Structure has been long over due in getting this type of treatment. Short of experiencing the pains ourselves this the best way of brining the edge behaviour of the ES into focus ("As face answereth to face, and iron answererth to iron, so does a man trieth words." Proverbs)
 
On a side note...  
 

tst wrote
 
 I can't really think of any other two nodes in LV which have to be coupled. The closest would probably be Dequeue Element,
 

 
Multiple Dequeue Elements reading from a single que are useful when you have a lot of autonomous operations that can take place in parallel. An example would be collectiving the "E-List" (if the NI forum did not concider me an atack Smiley Wink )
 
You spwan off a bunch of threads that get the user ID by reading from a single queue that has one queue entry for each user. The spawned thread each get the user specs from the forum and return the stats via another queue (multiple writters, single reader). This parallel approach let me achieve a 1 MHz down-load rate (before the forum started to refuse my connections Smiley Surprised ) Using a single query loop only achieved a 0.1 Mhz download.
 
Ben
Retired Senior Automation Systems Architect with Data Science Automation LabVIEW Champion Knight of NI and Prepper LinkedIn Profile YouTube Channel
Message 28 of 39
(10,689 Views)
Hello Proven Enthusiastic Veteran.
At one of page www.ni.com you somebody solve how calculate area irregular region who draw mouse on picture.I am work this of you context but have problem.When depict anything on picture ,all elements pixmap is zero.Here hook VI ,if you can look this,and can post me,in why is problem.
0 Kudos
Message 29 of 39
(10,656 Views)


TonP wrote:

One other question: could/can Darren's beloved VI Analyzer detect the forking of a event registration refnum (or ERF)?


Ask and ye shall receive...check out today's nugget.

-D

0 Kudos
Message 30 of 39
(10,628 Views)