GregFreeman Posted April 21, 2020 Report Share Posted April 21, 2020 I have been going back and forth on the best method for handling shared data between two processes which may or may not exist at the same time. Assume you have processes A and B, and process A is running, B is not. At some point in the future when B begins running, A needs to register for some events from B. I see two ways to handle this. The first way is aggregation. Some parent process that has visibility of both A and B is responsible for creating the user event refnum and passing it to both processes if/when they are created. This is a simple solution but can be a bit ugly in my opinion, since the refnum will exist even in scenarios where it is unused, among some other issues with encapsulation and the like. The second option is to use composition and create the refnum in process B which it is launched, and pass it to process A. This would be done by sending some message to process A to give it the refnum in order to register. This seems to be the cleanest method from an encapsulation standpoint, but also seems to add a lot additional code to the framework as compared to the aggregation method above. There are of course other pros and cons to each of these but those are the most generalized ones I see. I just want to get a feel for what other people out there are doing to solve this. Quote Link to comment
drjdpowell Posted April 21, 2020 Report Share Posted April 21, 2020 (edited) Go with the second option. In general, the communication channel should be owned by the receiver. Edited April 22, 2020 by drjdpowell 2 Quote Link to comment
pawhan11 Posted April 22, 2020 Report Share Posted April 22, 2020 Problems I have with event structure is that it exposes back userevent. Nothing stops user from self generating events or closing this reference. Quote Link to comment
GregFreeman Posted April 23, 2020 Author Report Share Posted April 23, 2020 (edited) On 4/22/2020 at 4:41 AM, pawhan11 said: Problems I have with event structure is that it exposes back userevent. Nothing stops user from self generating events or closing this reference. Yes, and not only this but you are essentially required to expose a refnum to a non-owner in order for it to register, which has never sat correctly with me. I think I should just live with it since I've revisited this what seems like hundreds of times. I've tried wrapping the logic up in a class with register methods exposed, but it becomes more difficult if a VI is launched asynchronously. In this case you want the parallel VI to create its references so they are tied to the lifetime of that VI, not the caller. Because classes are by value, if you do this you have to pass back all the created references, a-la actor framework and its queues. AF does this well, but I find the overhead of implementing this paradigm with multiple user events in child classes a lot more tedious. In some old thread I saw the idea pitched for "registration only" user event refnums which would I think be a fine compromise and something I could live with. Edited April 23, 2020 by GregFreeman Quote Link to comment
Francois Normandin Posted April 24, 2020 Report Share Posted April 24, 2020 I, too, have been uneasy about this for a long time, but the convenience of the mechanism is such that I generally hope that the user is going to use it properly. There are two alternatives that I know of: - Use the mediator pattern and make sure the user event is unique for your event consumer (if the consumer closes the reference, the mediator can just assume it doesn't want to be notified again and can destroy the thread) - Use Actor Framework or another message-based framework that does not expose the queue/event/notifier publicly. Quote Link to comment
GregFreeman Posted April 24, 2020 Author Report Share Posted April 24, 2020 16 hours ago, Francois Normandin said: I, too, have been uneasy about this for a long time, but the convenience of the mechanism is such that I generally hope that the user is going to use it properly. There are two alternatives that I know of: - Use the mediator pattern and make sure the user event is unique for your event consumer (if the consumer closes the reference, the mediator can just assume it doesn't want to be notified again and can destroy the thread) - Use Actor Framework or another message-based framework that does not expose the queue/event/notifier publicly. Thanks! The mediator pattern may be a good option. The main drawback I have with any sort of command pattern is that I can't leverage a huge benefit of user events which is the fact that you can have multiple processes register, and only have to send the message out once for them all to receive it. It's great to be able to send a single message and have 3 different windows receive it, my logger receive it, etc and all handle it differently. The command pattern I believe would require you to dynamic dispatch on the messages Do.vi to get a concrete message type, and then immediately pass that concrete type into an Actor's dynamic dispatch "Handle <xyz> Message" VI, which you could override for each child actor. The problem here is you're bumping the "Handle Message" into the base class of the Actor which may not be applicable to many of its children. It seems the correct answer is that everything has its tradeoffs, and it's just a matter of which you want to work with. Quote Link to comment
drjdpowell Posted April 24, 2020 Report Share Posted April 24, 2020 Just because Actor Framework and DQMH are the two most popular LabVIEW frameworks, doesn't mean there is a binary choice between multiple User Events and the Command Pattern. Personally, I wouldn't recommend either of them for general use (unless you are a scripting wizard, perhaps). And a single message to multiple registered processes is not hard to do without User Events. Quote Link to comment
GregFreeman Posted April 27, 2020 Author Report Share Posted April 27, 2020 (edited) Quote doesn't mean there is a binary choice between multiple User Events and the Command Pattern I agree and I didn't mean to imply they are mutually exclusive. Quote Personally, I wouldn't recommend either of them for general use (unless you are a scripting wizard, perhaps). I assume you mean the fact that you have to create many messages or many refnums which results in a lot of extra classes/code? If so, I do somewhat agree with this. If not, maybe you can provide some more detail into the "why?" Quote And a single message to multiple registered processes is not hard to do without User Events. I also completely agree with this, I am just partial to leveraging built in mechanisms where I can, and I think it makes for clean, readable code with native structures. That's not to say there aren't other viable, valuable solutions. But it is the reason why I tend to lean towards user events. I guess if I were to summarize, the thing I have trouble with can be boiled down to two points: 1) I want to limit the amount a developer has to know about how to create additional user events for a particular process as a regular part of a framework, and how to pass them back to the caller when necessary. I just used the AF as an example here because I think it handles the ownership of the queues nicely. But if you do want to add in additional queues or events, for example, you have to write that same boiler plate to create the communication mechanism and pass it back to the caller. I want to enforce this, with say a must override VI to create child events and a must register VI in the caller to register for them, but the strict typing of events makes this difficult due to connector pane differences. I have tried leveraging malleable VI's to limit some of the boilerplate code but haven't come up with an ideal solution yet. 2) I don't like that I have to expose raw refnums to other processes in order to register for events. But I suppose this is something I have to live with for now. Edited April 27, 2020 by GregFreeman Quote Link to comment
drjdpowell Posted April 27, 2020 Report Share Posted April 27, 2020 The choice you're making is to consider only methods that require lots of boilerplate, methods where one needs to write lots of code that basically duplicates templates with different types, be it user events or Do VIs. That is a problem that can be attacked by scripting tools, but scripting itself is a significant effort. An alternative is to use generic messages, even something as old school as a text-variant cluster. Then you can write subVIs for communication that you can reuse. Writing a subVI to handle generic messages is a lot easier than writing a scripting tool to write the same subVI for many strict-type messages. Or hand coding all the boilerplate yourself. Your original post is basically asking if you should do the simple but ugly shortcut rather than the correct but hard way. Your only asking the question because you've chosen techniques that are expensive without scripting tools. To me, who uses generic messages, everything you have been describing seems trivial. Because I wrote reusable communication components that handled these things years ago. They weren't trivial to make, but they're easy to reuse. Just today I wrote code for a new loop, which requested from another loop the User Event of a third loop, then registered with that third loop for several notifications (preexisting notifications also registered for by other loops) to be sent to the User Event of the first loop. A couple of the notifications get sent by the third loop before the first loop even exists, yet the notification is still received. Not desimilar to what you're descriping. I had to write no classes and zero new subVIs. Oh, and the first loop exists in a different exe from the third loop and they are actually communicating though a mediating TCP client-server connection, though both loops just receive User Events and aren't written in any special way to support this (the third loop was written before this app had TCP added as a requirement). Internally, the TCP client server is rather complicated, but for this application it is just two subVIs, which I wrote years ago. To do a similar TCP connection with multiple application-specific User Events would require a very large amount of boilerplate (or truly impressive scripting ninja skills). Quote Link to comment
GregFreeman Posted April 27, 2020 Author Report Share Posted April 27, 2020 (edited) Quote The choice you're making is to consider only methods that require lots of boilerplate, methods where one needs to write lots of code that basically duplicates templates with different types, be it user events or Do VIs. ... Your original post is basically asking if you should do the simple but ugly shortcut rather than the correct but hard way. Yes, basically. The deeper I get into this I think what I need to solve my problems are generics, to get generality and combine it with type safety, and I simply don't have them, so I have to work within the tools I am given. Edited April 27, 2020 by GregFreeman Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.