12-12-2021 10:24 AM
Hello,
I'm somewhat new to LabVIEW and especially to OOP in LV and have a bit of trouble understanding access scopes. Maybe you can help me clear up my confusions.
Take the following situation:
I have a single ressource that needs to be accessed from multiple locations throughout my vi.
So far, so good. Just use your typical (non-reentrant) FGV.
Now let's say I want to add another another layer to my application and use multiple instances of my (previously top level) vi as module in this app. I'm sure this is not an uncommon use case.
Now each module needs it's own copy of the FGV. And this is where the confusion starts.
It's my understanding, that even though the module itself is reentrant, the FGV will still be unique and shared between all modules in the application. Is this correct?
I know I can fix this by making the "FGV" reentrant and calling it by reference. But then I have to wire the reference to all locations where the FGV is accessed which can add a lot of clutter and potential for bugs.
Is there a simpler or more elegant way to solve this problem?
Would it be a good solution to wrap the module in a class and use the class private data to hold the contents of the FGV?
This wouldn't really save any wiring effort, it would just be a class wire instead of the VI reference.
If I didn't use the class privite data but instead kept the FGV as a non-reentrant private method, would that give me the desired result of one separate clone per class instance?
How about using a lvlib with the FGV as a private member instead of a class? Would that make any difference?
Would there be any difference in access scope depending on how I lauch the instances of my module (call by reference, start async call, or simply drag it on the block diagram)?
Solved! Go to Solution.
12-12-2021 02:59 PM - edited 12-12-2021 03:13 PM
That's a few questions. Maybe this will help clear things up a bit:
Namespacing - this is how VIs are uniquely identified. If a VI is within a project library (lvlib) or the specialized version called a class (lvclass) then the VI name includes that container. So FGV.vi is not the same as MyLib.lvlib:FGV.vi, nor MyClass.lvclass:FGV.vi and all three can be in memory at the same time.
LabVIEW classes - these are simply a definition for a cluster of data (always private), a series of common VIs to operate on them along with metadata of the "type" to enable polymorphism and inheritance (which just comes back to which VI is being called at runtime). A LabVIEW object is just a copy of the class private data along with some metadata describing that class "type".
Re-entrancy - this describes how many versions of a VI can be in memory at the same time. If a VI is re-entrant, then there can be more than one clone of that VI in memory. Note I said "clone" - again, VIs must be uniquely named so when you add a static call to the VI you are actually calling a uniquely named copy with a different memory space. If a VI is non-re-entrant, then there can be only one copy of this VI in your Application Instance; calls to the VI use the same VI memory space. To prevent the obvious risk of corruption in a multi-threaded environment, LabVIEW helpfully ensures that a call to a non-re-entrant VI must complete before another caller can start - its this feature which makes an FGV possible.
Lastly - Software is foremost about data and ways of operating on it to achieve some outcome.
It's my understanding, that even though the module itself is reentrant, the FGV will still be unique and shared between all modules in the application. Is this correct?
Based on how I defined non-re-entrant above, then yes this is correct (by definition - there is only one namespaced "FGV.vi" with a unique set of data). You can use the CBR node and re-entrant FGV trick (which lets you determine which clone to use dynamically) or update your FGV to store more than one set of data. Both are effectively the same thing (you still have a "reference" to contend with) but the block diagram for multi-data FGV might be cleaner (my humble opinion).
Would it be a good solution to wrap the module in a class and use the class private data to hold the contents of the FGV?
Based on your description, you were using an FGV to get around not having "wires" within your module's block diagram but otherwise the FGV content was internal to the module. So this option is possible since each object will have its own copy of private data. A factor will be whether your module code was dependent on the "blocking" nature of a non-reentrant VI - in that case you may need some re-structuring. Can't really say more without "the code". But this is probably the best option if you want to be able to share the data in a controlled way.
How about using a lvlib with the FGV as a private member instead of a class? Would that make any difference?
The reason you want to have more than one module is that you want to have more than one instance of data, even though you have the same ways of operating on it (be it the same thread of multiple). Having the FGV within an lvlib wouldn't help you directly since the data part is still unique (albeit within the lvlib).
Would there be any difference in access scope depending on how I lauch the instances of my module (call by reference, start async call, or simply drag it on the block diagram)?
No, not really. Access Scope is something else entirely and I suspect independent from your original questions.
12-13-2021 10:39 AM
Thank's for the comprehensive answer.
This helps a lot.
12-13-2021 01:48 PM
I personally have stopped using FGV's and the First Call? primitive because of this issue. It would be nice if there was a simple way to namespace a new FGV or to "reset" a reentrant subVI but it's bitten me more times than it's helped. Now I just use references from the outset since it's WAY easier to do that than it is to re-implement references in existing code.
There COULD be a way to use the Call Chain function to se who's calling and maintain the references internally, but that sounds like it would be a giant pain to debug.