Jump to content

Event registration is destroyed too fast


Recommended Posts

I have a code construct where I sent a user event to a listener VI, after sending the 'Close' command I need to destroy the Event Registration Refnum, I do this directly after sending the event from the main VI:

post-2399-073338100 1278776040_thumb.png

Without the OpenG wait functionality (error wires connected of course) the listener VI does not get the Event triggered.

The only reason I can understand is that the listener VI's event queue is destroyed before the event structure reacts. And somehow the Event structure is too slow to empty the queue.

Ton

Link to comment

Ton, very nice you discovered this. I have seen a behaviour in LV 7.1 (so unsupported and I never tested it on newer versions + several bug-fixes in LV8 on events) that is similar:

Two user events directly fired one-by-one the later one was missed when I used some event structures (at least two in SubVIs, most likely another one in the main vi). Using an wait 0 between removed this problem.

I will see on monday if I still have the code and check it with a recent version. If you have, please post a CAR so I can refer to if my issue is the still persistent, 'cause it might be related (too slow event structures).

Felix

Link to comment

I don't think this is a bug at all. You've programmed a race condition. Even with the delay call, your code's behavior is undefined.

Signaling the event (or generating/broadcasting it in LV language) obviously won't wait for the event to be processed, but it's important to also realize that it doesn't even wait for the event consumer(s) to be invoked. The event is entirely asynchronous. Broadcasting simply queues the event up for each registered consumer of the event. With no wait, your VI will (usually) just blow through the broadcast, then go ahead an unregister the event, which will destroy the event queue, in the process neutralizing the uninvoked event (along with any other events in the queue). Add in a wait as you show, and your program will sometimes yield to a context switch allowing your consumer code to invoke, but you can't be certain it always will. The code behavior is by its very nature undefined.

To avoid this, you can handle event unregistration in the consumer portion of code. Once the consumer receives the event, it unregisters itself and finishes up.

  • Like 2
Link to comment

Hi, Ton

I support the answere of mje with one additional thought:

I suppose your application is running in two asynchonous loops, which results in multiple threads on your machine.

Multiple threads (multithreading) forces programms, which are not using the CPU at this moment to switch to background,

so another application could run for a couple of miliseconds.

If you execute your sender loop to send the "stop" command, it it's using the CPU completely by sending the command and

directly destroying the event, so there is no point for the computer to force the process to the background.

By adding a wait functionality, the thread will be halted for just a millisecond or less, even with a zero connected to the terminal.

Within this milisecond your listener loop is able to receive the event request to execute.

After that, the sender thread comes back and destroys the event.

So the behavior you've described is not a bug, but a multithreading dependend problem.

To solve this problem, you could force the sender thread to wait for just a milisecond as you did.

But this way of programming is not very good, because as mje already said, it results in undefined states.

You should consider to add some synchronisation functionality in your code as I would do, or just don't destroy the event,

because LabVIEW will take care of that on it's own (also not good style).

Greetings, LogMAN

Link to comment

To solve this problem, you could force the sender thread to wait for just a milisecond as you did.

But this way of programming is not very good, because as mje already said, it results in undefined states.

Indeed. But still realized that even if you insert a long wait, there is still no guarantee your consumer will execute. If the system is under load from another process (LabVIEW or otherwise), a few seconds still might not be enough. The behavior is by it's very nature undefinable.

As LogMAN said, you can also consider adding other synchronization functionality. Passing an occurrence or rendezvous as part of the stop event works, and then the code that trips the stop event can wait for the sync object to be properly signalled before destroying the event registration.

But I still believe letting the consumer handle the unregistration is the best way to do it. For the very reason your code is giving you problems, I try to always tie event unregistration with the same event structure that owns/uses the registration.

  • Like 2
Link to comment

But I still believe letting the consumer handle the unregistration is the best way to do it. For the very reason your code is giving you problems, I try to always tie event unregistration with the same event structure that owns/uses the registration.

Yes I think i will use an occurence with the OpenG wait with a timeout of 1000 ms, that should do the trick. I am reluctant to give the destroy to the child process since I don't have control over it, and the event registration is created in the main process, and I am a fan of the 'destroy what you create' philosphy.

Ton

Link to comment

Yes I think i will use an occurence with the OpenG wait with a timeout of 1000 ms, that should do the trick. I am reluctant to give the destroy to the child process since I don't have control over it, and the event registration is created in the main process, and I am a fan of the 'destroy what you create' philosphy.

Then let me suggest that the wrong process is creating the event refnum. ;-)

This is a problem that I ran into with a listener framework I've been working on lately. Alpha.VI dynamically launches Beta.vi and then gives Beta.vi a queue refnum so Beta can receive messages from Alpha. We had to turn that around -- Alpha launches Beta and then Beta creates the queue and gives the refnum to Alpha. That way the lifetime of the queue is the same as Beta, not Alpha.

In your case, it sounds like the correct behavior is for your consumer to create an event refnum and give it to the sender. That way the consumer can dispose the refnum after it handles the event and "destroy what you create" policy is preserved.

  • Like 1
Link to comment

Without the OpenG wait functionality (error wires connected of course) the listener VI does not get the Event triggered.

If I don't remember wrong, by adding a normal wait=0, that will cause LV to switch thread, and that's probably why you get it working.

//Mikael

Link to comment

We had to turn that around -- Alpha launches Beta and then Beta creates the queue and gives the refnum to Alpha. That way the lifetime of the queue is the same as Beta, not Alpha.

This is what most of my VIs do with the various sync objects (including dynamic event regs).

The problem is this: Alpha is going to dynamically launch Beta, and needs some kind of sync object to communicate with Beta. It needs to know that once that object exists, it can be used and Beta will (eventually) receive all signals sent to it. Two solutions present themselves to me.

  1. Alpha creates the object and passes ownership of it off to Beta. I dislike this because it violates the destroy what you create practice, but it is the simplest way to solve the problem. Even if it's a dynamic event reg, if Alpha creates it, the reg is capable of receiving events even before Beta is ready to receive them.
  2. Alpha creates a temporary sync object to pass to Beta (usually a notifier to hold the final sync object reference), starts up Beta, then blocks on the temporary notifier. Beta starts, creates the permanent object which it owns, and signals Alpha using the temporary object, then continues on its way. Meanwhile, Alpha receives the permanent object reference, takes note of it, and continues on it's way, passing signals as appropriate.

The second way is by far the more extensible of the two, but there is that added layer of complexity.

Of course the whole situation is made much easier if we just don't care if Beta misses a few signals. In that case the ownership of the sync object can be managed by Alpha.

  • Like 1
Link to comment

Alpha creates a temporary sync object to pass to Beta (usually a notifier to hold the final sync object reference), starts up Beta, then blocks on the temporary notifier. Beta starts, creates the permanent object which it owns, and signals Alpha using the temporary object, then continues on its way. Meanwhile, Alpha receives the permanent object reference, takes note of it, and continues on it's way, passing signals as appropriate.

This is exactly what we have thus far. The extra complexity is entirely hidden within the subVI that does the launching, so it doesn't have to be repeated each time.

In general, we might be ok if Beta misses some signals, but the key signal to Shutdown is one we cannot afford to miss and that's the one most likely to be missed without this set up.

Link to comment

I do this directly after sending the event from the main VI:

Why do you need to destroy the event refnum right away?

This is a problem that I ran into with a listener framework I've been working on lately. Alpha.VI dynamically launches Beta.vi and then gives Beta.vi a queue refnum so Beta can receive messages from Alpha. We had to turn that around -- Alpha launches Beta and then Beta creates the queue and gives the refnum to Alpha. That way the lifetime of the queue is the same as Beta, not Alpha.

"Listener framework" == Observable/Observer?

Another approach is to wrap Beta.vi in an active object class, keep the queue internal, and create public vis for each message Beta.vi can process. This has the nice advantage of eliminating the possibility of run-time type conflicts between Alpha.vi and Beta.vi when the message data is unflattened and (imo) makes the code much easier to read and test. It also avoids the problem of somebody accidentally destroying the queue when Alpha_2, Alpha_3, or Alpha_n, is created and linked to Beta.vi, a potential bug which must otherwise be checked for via testing or inspection whenever code changes are made. As always, the additional protection comes at the cost of additional complexity, so it may not make sense in all situations. Still, I prefer frameworks to be object-based as it allows more and better options for building app specific code on it.

I've attached my version of an Observable Active Object Template, based on SciWare's active object framework posted several months ago. Take a peek. Maybe there's something in there that is useful for you. (Like how not to do it. :D ) Feel free to post a critique (assuming you can make any sense of it. :blink:) This is an open invitation to all.

ActiveObjectTemplate.zip

[Disclaimer: This code drop is ~2 months old, is highly experimental, and is constantly evolving. My goal has been to create a very robust and flexible Observable framework, then go back and simplify it for situations where all the flexibility isn't needed. This code is the start of the simplification process. It's not for the faint of heart--I wrote it and I still get confused by it sometimes. It's not intuitive code either. There is a (woefully inadequate) text document describing the framework, but there currently aren't enough details to guide anyone other than me through the process of building their own Observable Active Object from the template. The whole event managment mechanism can be particularly confusing.]

Link to comment
"Listener framework" == Observable/Observer?
More of a message passing interface between a server and client, but either one of those parties may essentially implement an events registration mechanism on top of the message passing interface (i.e., I pass you a message that says "next time X happens, let me know." When X happens, you send me a message to that effect.)

To give you a clue how similar what I've been assembling is to yours, the name of my project file is "Actor Objects.lvproj". :-)

PS: You clearly have a larger monitor than I do. All the VIs are saved just barely on the screen.

Link to comment

To give you a clue how similar what I've been assembling is to yours...

I hope yours is easier to understand.

PS: You clearly have a larger monitor than I do. All the VIs are saved just barely on the screen.

Naw... I use a second, external monitor set up to the right of my laptop and do all my main dev work on that. I don't know if it's Labview or Windows that repositions the windows, but I see that too when I'm working without the external monitor.

[Edit - I realized I also see this when I'm working on stuff at home, where my second monitor is to the left of my primary monitor. It's kind of annoying, but not nearly as annoying as having the VIs open up entirely off screen.]

Link to comment
  • 4 weeks 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.