LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
manu.NET

Handle activX or DotNet callbacks as events ... View callbacks in event structure !

Status: New

Hello,

 

Today, ActivX and DotNet events are treated as callbacks ... ( A vi is linked to a callback )

 

It should be nice to handle them as LabVIEW events, so the callback treatments could appear in the LabVIEW event loop structure.

 

I think there is certainly a difference between events and call back handling, but it should be a way to standardize the "event/callback" view.

 

=> It would be more "user friendly" ... and applications could be more "maintenable" ...

 

Manu.net

Manu.net
7 Comments
JackDunaway
Trusted Enthusiast

+1! The way I currently overcome this limitation is using a design pattern I dub “The Flaky Delegate” where a .NET callback brokers messages back as LabVIEW User Events. (Quoted from a recent blog post)

 

FlakyDelegate

 

Another reason to +1 this idea: .NET Callback VIs run in a context that does not support probes or even breakpoints. Presumably, the implementation of this idea would collaterally ease troubleshooting. (In the meantime, the "Flaky Delegate" minimizes amount of code in this difficult-to-debug context.)

kegghead
Member
Agreed. I love the term "flaky delegate." Used it many times myself.
GregR
Active Participant

In the beginning ActiveX and .NET events did use the event structure. We pulled them out because they present potentially incompatible functionality. The problem is a reentrancy issue. Both ActiveX and .NET present event models where events are immediate and synchronous. This means that if you are getting a value change event, the event handler will be called synchronously from the code that sets the value. If you use the event handler to implement range checking, you might reset the value in your handler. Since the value has changed again, this will call the event handler. You have now reentered your event handler.

 

The LabVIEW event structure presents a model where one event is handled at a time. If you are handling an event and something triggers another event, we wait until the current event is done to handle the next one. In the case above that means you can't handle the nested value change until completing the outer value change, but you can't finish the outer change until you complete the nested value change. Welcome to deadlock.

 

This can also happen between separate events in the same structure. When handling a button click, you could close a window. Closing the window might fire a window closing event. If the event structure handling the window closing event is busy handling the button click, then you are deadlocked.

 

LabVIEW avoids these issues by making most events asynchronous (notify events). We don't call you as the button is being clicked. We tell you that the button was previously clicked and this won't be handled until its event structure is idle. We only synchronously handle an event in cases where some information can be returned from the handler (filter events). For a mouse click, you can tell LabVIEW whether to continue processing the click or not. We never allow filter events to nest though.

 

In order to allow ActiveX or .NET events in the event structure, you must do one or more of the following:

  • Treat ActiveX/.NET events as asynchronous
    We could always treat these events as asynchronous in the event structure, or we could allow the user to choose (which would also require one of the other solutions). LabVIEW would record that the event happened in our own synchronous handler, then asynchronously trigger the event structure to run. Any event that relies on values being set during the handler would not work with this solution. The user could still use the callback VI mechanism for synchronous handling.
  • Detect when this reentering happens and report an error
    This would not be able to call the diagram so any expected side effects or return values would not happen, but we could return immediately from the inner handler and abort the outer handler when we got back to it. This would not be graceful, but could allow the user to fix their code to avoid the situation.
  • Change event structure to allow reentering
    This is something I've thought about a bit. The current event structure's semantics are that each time it executes, it executes exactly one of its subdiagrams and then finishes. This means you pretty much always wrap it in a loop and it can't support nesting diagrams. What if instead it was defined more like a loop? In fact what if it was defined more like a parallel loop? Its semantics could be that it executes the appropriate subdiagram for each event that is triggered until one of the subdiagrams says to stop handling events. This stop signal would stop new subdiagrams from starting and the structure would complete when all currently running subdiagrams complete. Shift registers would not be allowed since multiple diagrams could be active at the same time. Output tunnels might also be disallowed or might be defined to come from the frame that caused the structure to stop.

For any use case where the "flaky delegate" works, the first option would suffice. Beyond that the exposed complexity definitely increases. So particularly in the last option is the complexity too much to be warranted by the problem?

JackDunaway
Trusted Enthusiast

@GregR wrote:

<<a great response with insight "under the hood" of LabVIEW>>


To respond to your three bullet points:

  • Regardless of how .NET Callbacks are implemented (by the LabVIEW language developer), from my point of view (a LabVIEW language user) they are *already* asynchronous. Perhaps my point of view is due to lack of understanding, perhaps it's due to the fact that treating them as asynchronous has always fit my software model. Nevertheless, the Flaky Delegate has always sufficed as the intermediate "missing link" between the synchronous callback and asynchronous event handler. The spirit of Manu's idea is to incorporate the Flaky Delegate as a language feature, a built-in broker from Event to Event Handler Structure, relieving the developer from creating this user pattern again and again.
  • Eww, no thanks. Smiley Wink
  • As you say, most Event Handler Structures are probably wrapped in a While Loop, and most practically have Shift Registers. Precluding this type of state data (for the sake of parallel event handling) might be too undesirable (in addition to opening up more opportunites for user-created race conditions and deadlocks). Also, if the Event Handler Structure became the loop, it might be syntactically weird to incorportate code that executes on each "iteration" (which is currently simple to achieve with the Event Handler Structure wrapped in a While Loop). And it might be weird to try to handle in parallel "the same" event that has "piled up" (think "Val Change" on a slider). But that's just devil's advocate stance - in reality, a "Parallel Event Handler Structure" is a really cool idea, worth investigating as a top-level Idea.

In other news, this argument closely parallels the desire to see Asynchronous CBR Node broker it's output as an event, since the Flaky Delegate pattern also solves that pesky problem.

JackDunaway
Trusted Enthusiast

@GregR - could I solicit one more peek under the hood? Why can't you use breakpoints or probes in the context in which .NET Callbacks run?

GregR
Active Participant

.NET callback VIs are in fact invoked synchronously in the thread firing the event. To the .NET code, this is just like calling any method. On the LabVIEW side it is very similar to calling a VI through the wrapper functions created for built DLLs. We end up running the VI code directly in the calling thread (at least the beginning before it possibly switches to a different thread or uses multiple threads). As I eluded to earlier, this is necessary to handle cases where the event data contains values that are expected to be modified like a "handled" boolean that should be set to true.

 

This is related to Jack's question about debugging as well. Most .NET code (like most C code) is really just single threaded, so almost all .NET events end up being fired from the UI thread. In many cases the .NET code is in its own event handler (like mouse down) and fires its specialized event (like button click). We then enter the VI code. At this point in order for LabVIEW to show the user a breakpoint, we would need to have the UI alive (meaning pumping OS messages). Otherwise we may not even draw correctly much less be able to respond to you clicking on the state panel to say continue. Guess what, most .NET components don't respond well to pumping OS messages from inside of their OS message handler. It is bad .NET ettiquette to ever pump messages explicitly and doing it in under a handler will almost certainly crash. This is the same type of issue that caused us to create the advanced setting for building DLLs about whether to pump messages or not.

 

If we allowed treating .NET events as asynchronous in the event structure, we'll have to be really clear about the difference in behavior but it could work. It does make the entire system more complex though. LV filter events are synchronous in the structure and as callback VIs. LV notification events are asynchronous in the structure and as callback VIs. .NET events are synchronous as callback VIs but would be asynchronous in the structure.

 

I am still interested in a better way to expose a truly synchronous event model to a graphical language, but perhaps we can improve things in a shorter term.

kegghead
Member
Brilliant. Thank you very much, Greg, for taking the time to explain all of that so clearly.