Jump to content

Dynamic Dispatching on User Events


Daklu

Recommended Posts

OH NO. This is VERY BAD. That's going to have to be fixed in LV2010. That's a severe bug and I had no idea it existed until today.

So I have to ask: Why did you ignore it when I posted similar examples using USER events (see previous posts)? Is it because you don't see the same issue with user event references?

I can see why this is BAD for other references (queues, etc), but I'm really trying to dissect whether it is the same for user events. Unlike queues, where it was quite easy to get a child control that contained parent data, I am having more difficulty doing this with user events. When you have an array of class user event refs used in an event structre, the event data is of parent type, so I can't see in that scenario how you'd get parent data on a child wire or control. Maybe I'm missing it, but the attached image appears to be a valid operation and it does work properly.

post-415-126393062171_thumb.png

If you see other issues, I could use the clarification. I have other thoughts, but I'll hold them for now.

-Scott

I can see why this is BAD for other references (queues, etc), but I'm really trying to dissect whether it is the same for user events. Unlike queues, where it was quite easy to get a child control that contained parent data, I am having more difficulty doing this with user events. When you have an array of class user event refs used in an event structre, the event data is of parent type, so I can't see in that scenario how you'd get parent data on a child wire or control. Maybe I'm missing it, but the attached image appears to be a valid operation and it does work properly.

Just answered my own question... I don't know if it's possible to get the "parent data on child" violation with a single event structure, but it's easily doable with two. Occurred to me right after my post.

post-415-126393128096_thumb.png

Link to comment

Just answered my own question... I don't know if it's possible to get the "parent data on child" violation with a single event structure, but it's easily doable with two. Occurred to me right after my post.

Give the child event a method and try to call it in your second loop. I'm curious what error Labview comes up with. ;)

Link to comment

My initial guess would have been a silent shutdown or lockup since that's what happens to me 96% of the time. But since Norm jogged my memory on the dynamic dispatching error I think he's probably right. I'm running into that error more frequently these days... (The error text is, "This dynamic subVI cannot execute because the needed member VI cannot be found.")

OH NO. This is VERY BAD. That's going to have to be fixed in LV2010.

Seeing that bug in '09 has me a bit concerned that what I'm doing may be violating the rules of 8.6. I think I'm okay, but it never hurts to check... Is there anything wrong with typecasting a child class user event into a parent class user event, aside from the fact that it's possible for the event producer to send the wrong EventData child class along with the event?

post-7603-126395844123_thumb.png

Link to comment
So I have to ask: Why did you ignore it when I posted similar examples using USER events (see previous posts)? Is it because you don't see the same issue with user event references?
I didn't notice what was going on. When I tried a couple of situations, they didn't allow the cast (notice, for example, it depends upon the order that wires are connected to the Build Array node whether it allows the bad behavior or not). I wasn't really delving into this whole thread because I expected the "you can't cast them" to kill the experiment. When it kept going, I thought, "That's strange, what did they find that I missed?" It wasn't until Norm explicitly called out what was happening that I realized what was up.
What error do you think it will come up with? shifty.gif

LV will likely crash. Eventually. If you're lucky, and only if you're lucky, you'll hit some emergency shutdown code that cleans up quickly. If you're unlucky, LV will do a lot of work messing up a lot of stuff before the problem gets detected or leads to a crash.

Consider: If I can get parent data onto a child wire, then I can get a LabVIEW Object onto a child wire, which, since any class is a valid value of a LabVIEW Object, then I can get *any* class onto *any* wire (you can pretty quickly adapt my queue example of the bug to demonstrate this). The first time that that screwed up object goes to an Unbundle or Bundle node, LabVIEW will be indexing who-knows-what piece of memory and crash hard, or overwrite something important, or interpret the first four bytes of some string as a voltage, etc. etc. etc.

Just answered my own question... I don't know if it's possible to get the "parent data on child" violation with a single event structure, but it's easily doable with two. Occurred to me right after my post.

:-)

Link to comment

My initial guess would have been a silent shutdown or lockup since that's what happens to me 96% of the time. But since Norm jogged my memory on the dynamic dispatching error I think he's probably right. I'm running into that error more frequently these days... (The error text is, "This dynamic subVI cannot execute because the needed member VI cannot be found.")

Well, depending on the circumstances, you're both right. And also partly wrong. It's all a matter of perspective.

I identified and tried the following four cases:

1) Dynamic dispatch where all members are available.

2) Dynamic dispatch where just the child method was available

3) Static dispatch that does nothing

4) Static dispatch that passes out data

All were tested with the parent data being dumped on a child wire:

1) Functions normally, maybe to the chagrin of AQ. Parent and child VI's dispatched appropriately depending on the type

2) Gives the "This dynamic subVI cannot execute because the needed member VI cannot be found" error.

3) Does essentially nothing. No errors. No crash.

4) Ahh... well the devil he no like this case. As a fine a lady as Miss LabVIEW is, well she try tado da dance on that high wire and she trip and fall flat on her face. And a big flaming pit open in the ground and she be dragged straight to hades.

Seeing that bug in '09 has me a bit concerned that what I'm doing may be violating the rules of 8.6. I think I'm okay, but it never hurts to check... Is there anything wrong with typecasting a child class user event into a parent class user event, aside from the fact that it's possible for the event producer to send the wrong EventData child class along with the event?

As expected you cand perform tha same cast in 2009. Strictly speaking, I am betting AQ will call this a "hole" that needs to be covered as I don't see that it is any different than what I tried above. So from that perspective I'd say that it is bad. But if you're careful to ensure that you don't send the wrong data, I'm betting it works fine.

So I think we're all in agreement that having parent data on a child wire or control is BAD. But instead of not allowing these typecasts or polymorphisms and causing broken wires, I wonder if there isn't a compromise. I would think that even though a child basd user event (or queue, etc) refnum has been cast to a parent type, LabVIEW would still be able to check the true type and throw an error from the primitive at runtime. This would allow this type of technique to operate and still provide type safety at runtime.

Admittedly, I haven't tried to think through all the implications of that, but hey, that's what Stephen get paid for... smile.gif

-Scott

Link to comment
Seeing that bug in '09 has me a bit concerned that what I'm doing may be violating the rules of 8.6.

The bug does not exist in LV 8.6. It was, as Norm guessed, introduced with the type propagation changes for data value references in LV 2009. The bug was hunted down and fixed for LV 2010 yesterday.

I think I'm okay, but it never hurts to check... Is there anything wrong with typecasting a child class user event into a parent class user event, aside from the fact that it's possible for the event producer to send the wrong EventData child class along with the event?
Short answer: Using the Type Cast node to change a refnum from one type to another is generally a bad idea. Rarely, it works, but more often LabVIEW beats you with an iron skillet and says, "No!"

Long answer: I don't know. My guess is there's something bad about it, but that's just because of the short answer -- probability is not in your favor when you do this. In fact, after seeing it crop up in another user's code last week, I asked Darren N to add a test to the VI Analyzer suite to check for such typecasting and warn about it. You can play around with the casting and let everyone else know what is safe and what isn't.

Assuming LV R&D has done its job correctly, there are exactly two situations that I know of where LabVIEW might crash and it's not a bug we can fix. The first is using a DLL node to call out to 3rd party code which does something abusive to the system. The second is using Type Cast on a refnum to a type that it isn't and then using that refnum. Some parts of the code have some guards to protect against this, but most don't for performance reasons (the compiler checks types, so having runtime checks just slows things down and the only reason for having the runtime checks is people using Type Cast to do things that can't ever work anyway).

You might ask, if its such a problem, why Type Cast even works on refnums. Answer: because sometimes it is useful. The entire GOOP Toolkit was built by using such tricks. When we added the new queue API in LV 6.1, we maintained the old queue API by using Type Cast. Casting a refnum to an int32 makes a great database lookup key, and then you cast that int32 back to the original refnum to use it. You can use Type Cast to add/remove typedef to a refnum wire to eliminate a coercion dot. Then there's some really weird scripting tricks that work, like casting the refnum of a string constant to a string control in order to set bold face. But it is always a crap shoot. If you'll notice, the new data value references don't allow most types of casting with Type Cast. All the "legitimate" casts are taken care of by To More Specific and To More Generic. If you want to abuse a DVR, you have to cast to int32 first and then cast to another type.

So go, play, see what you find, but check it out thoroughly before you use it in production code.

Link to comment

I got a chance to look over your framework. As I said earlier, I'm not familiar with 7.1 so I can't fairly comment on the implementation. I suspect that it is a very good solution given the tools that are available in that version. Personally, I don't like the idea of a message bus at the application level. It encourages direct communication between application components, which doesn't scale well at all. Direct communication creates a lot of communication paths to monitor. If I have an application with 5 components, I already have 10 communication paths to keep track of. Adding a 6th module to satisfy some new requirement leaves me 15 paths to keep in my head. Pretty soon it just gets too complicated and impossible to figure out what's happening when. Direct communication also leads to dependencies between those components, which limits reusability.

I was debugging an app recently that used direct communication and one module was receiving a message that was causing it to go into an unexpected state. It was very difficult not only to figure out where the message was coming from, but what state the application as a whole was in that caused this bug. These days if I'm going to have (or event think I might end up with) more than 3 modules I use a mediator to centralize the communication. It's much easier to control what's going on in the app.

I guess it's a matter of taste.

I havn't run into issues of scaling, as I connect modules over the bus that have very little communication (like a temperature monitor that publishes the latest reading over the bus and some GUIs are displaying it and in some situations the GUI VIs themself send a new setpoint.

Because of the loose coupling due to the string-type commands, the modules are pretty reusable (I've done it).

An older app was using queues to communicate between 3 GUIs with a central 'handler'. That made 3 queues to send commands to the modules and 1 shared queue for the modules to call the handler (this in turn sends commands to the other modules). I feel that this architecture is on the limit with 3 modules, as one more queue would ask for one to many terminals on the ConPane of the central handler.

Using a huge ComCluster to keep all kinds of queues, notifiers, events and event refnums resulted in spaghetti with a lot of stupid manual updating when I changed some of the Com Types.

But from your posts I could read that you prefer several wires and not a bus as I do.

Maybe LVOOP makes a big difference (I have just seen one OOP app using this bus so far).

Felix

Link to comment

...

In fact, after seeing it crop up in another user's code last week,

...

Some parts of the code have some guards to protect against this, but most don't for performance reasons (the compiler checks types, so having runtime checks just slows things down and the only reason for having the runtime checks is people using Type Cast to do things that can't ever work anyway).

...

So go, play, see what you find, but check it out thoroughly before you use it in production code.

Now if we could only play as fast as y'all develop....

I strongly agree with omiting the checks for performance reasons.

Type casting is a powerful tool and as my brother demonstarted* last year with a nail gun, powerful tools should only be used with great caution.

Ben

* Bruce: Oh no, I Fu#$ed up!"

Ben: Are you OK?

Bruce: No.

I meet my brother in the hallway and he is holding a 2X4 in an odd manner.

Ben: You hold your finger and I'll pull the board

.

.

.

Such is life in the Rayner Family.

Link to comment

I guess it's a matter of taste.

I havn't run into issues of scaling, as I connect modules over the bus that have very little communication (like a temperature monitor that publishes the latest reading over the bus and some GUIs are displaying it and in some situations the GUI VIs themself send a new setpoint.

Because of the loose coupling due to the string-type commands, the modules are pretty reusable (I've done it).

An older app was using queues to communicate between 3 GUIs with a central 'handler'. That made 3 queues to send commands to the modules and 1 shared queue for the modules to call the handler (this in turn sends commands to the other modules). I feel that this architecture is on the limit with 3 modules, as one more queue would ask for one to many terminals on the ConPane of the central handler.

Using a huge ComCluster to keep all kinds of queues, notifiers, events and event refnums resulted in spaghetti with a lot of stupid manual updating when I changed some of the Com Types.

But from your posts I could read that you prefer several wires and not a bus as I do.

Maybe LVOOP makes a big difference (I have just seen one OOP app using this bus so far).

Felix

Felix,

I actually do this exact thing using LVOOP within LVSpeak. Each command is a child class of the parent command class and they send commands through the LVx pipe as the class and it requires no casting whatsoever. Well I suppose you need to go to more specific class or rely upon dynamic dispatching after you get the class data in the event structure.

The basic idea (which has nothing to do with using your voice)is that each node (client or server) that needs to respond to a command, and potentially return a response, registers for a user event w/ the parent LVx class as the data type (so not exactly the concern of this thread). After which you just need to await the user event to be fired and handled appropriately. And the data type of the event is of LVx parent command type, and the specific child class defines the command to be executed, and the data inside of the class is the parameters and also eventually the return data which gets passed back to the requester within the LVOOP object itself.

The reason this is of relevance to this thread is that without doing this technique, casting to another data type (like string)would be required. For things like the Simple TCP Messaging (STM) reference design (on ni.com), it does exactly that. Flattens to string, send across tcp, and then hope that you package it up and cast it back appropriately. LVx uses VI server and takes all that casting away from the programmer and lets the wire type stay smart (and allows you to talk across the internet,LAN, or application contexts too)

Link to comment

I don't see that it is any different than what I tried above.

The difference is in what data is being sent with the user event. Your example puts a parent object in a user event typed with a child object. I'm restricting myself to putting a child object on a user event typed with a parent object.

I think the key is in the invisible link between the Generate User Event prim and the event structure. Notice the for loop in your example. The Generate User Event prim *thinks* both queues are typed with the parent object, so it doesn't object to sending a parent object down them. The data type in the event structure is defined by the wire sent into the Reg Event prim at design time, so at runtime the parent object is automatically cast as a child, which is can't fully support.

In my example there's a coercion dot on the Generate prim, indicating the child object is being upcast to the parent, which is perfectly legal. The silent typecast at the event structure is simply casting the child object from a parent back into the child--also legal. Putting the wrong child class or a parent object does cause problems, so I've tried structuring the framework to prevent the event producer from doing that.

Short answer: Using the Type Cast node to change a refnum from one type to another is generally a bad idea. Rarely, it works, but more often LabVIEW beats you with an iron skillet and says, "No!"

Thanks for the heads-up. I'll hide the cookware.

Maybe LVOOP makes a big difference (I have just seen one OOP app using this bus so far).

Yes, I think it does. In my system each component encapsulates it's message queue internally so the mediator (central handler) isn't even aware of them. I keep the component object's data in a private SEQ so there's no need for a response queue; any sub vi requesting data just grabs it off the queue. From the mediator's perspective it drops a vi asking the component to do something and wires it up. The sub vi returns the result. Simple.

The complexity of passing messages between threads is hidden in private class methods. More importantly, I only have to think in that level of detail for a single component at a time. I'm not constantly faced with managing multiple queues. Once the components are developed and tested to my satisfaction I can 'forget' about that level of detail and focus on application logic.

If you can, I'd strongly suggest you take the jump into OOP. I have yet to meet anyone who has really learned OOP decide that the 'old' way is better. (Aside from specific high performance applications where c or assembly are needed.)

Link to comment

The difference is in what data is being sent with the user event. Your example puts a parent object in a user event typed with a child object. I'm restricting myself to putting a child object on a user event typed with a parent object.

I debated with myself on whether it was worth it to reply or not and lost. I stand by my comment that "there is no difference", as I believe (correct me if I'm wrong) you missed the point of my statement. Of course there is a difference in what data was actually sent in our examples, but that isn't what I was referring to.

I think the key is in the invisible link between the Generate User Event prim and the event structure. Notice the for loop in your example. The Generate User Event prim *thinks* both queues are typed with the parent object, so it doesn't object to sending a parent object down them. The data type in the event structure is defined by the wire sent into the Reg Event prim at design time, so at runtime the parent object is automatically cast as a child, which is can't fully support.

Yep. Now looking at your example, notice the "Generate User Event prim *thinks* the queue is typed with the parent object". Sure, there is a difference in data being sent. I explicity chose to send a parent object, while you say you are restricting yourself to sending a child object. It doen't change the fact that both examples are the same in that a child event refnum has been cast to a parent. Yours was explicit with a type cast. Mine was implicit with the refnum coercion "bug" in 2009. But the result is the same.

In my example there's a coercion dot on the Generate prim, indicating the child object is being upcast to the parent, which is perfectly legal. The silent typecast at the event structure is simply casting the child object from a parent back into the child--also legal. Putting the wrong child class or a parent object does cause problems, so I've tried structuring the framework to prevent the event producer from doing that.

Again, no disagreement in principle, but I could have sent a child object and I would have the coercion dot as well and it all would work fine. That would defeat the point of the experiment.

In your example, you could also send a parent object (consciously or accidentally) and you wan't get any coercion. You'll end up sending parent data to a child object. It will 1) wire up correctly 2) compile 3) run and 4) CRASH... probably hard!

If you look back at the rest of my statement, I say: But if you're careful to ensure that you don't send the wrong data, I'm betting it works fine.

That's what you're doing. Being as careful as you can with the structuring of your framework to ensure that doesn't happen.

Conclusion-

Technique: SAME

Data: DIFFERENT

I'll split the pot with you, how about that...

If you can, I'd strongly suggest you take the jump into OOP. I have yet to meet anyone who has really learned OOP decide that the 'old' way is better. (Aside from specific high performance applications where c or assembly are needed.)

Ditto that!

-Scott

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.