01-20-2016 04:14 AM
@tst wrote:
Not the VI. The hierarchy (which is defined by a top level VI). That's an important distinction, because it means references do generally stay alive correctly.
You are right, this is something different. But the word "correctly" implies that there is some canonical programming scheme for references. Since the concept of a reference is used in a lot of different programming languages and programming paradigms this is hard to believe. "Correct" in the sense, that LabView is doing what LabView does is a tautology, and I would not ever dare to doubt this. At least there should be a best practice for LabView. I find sometimes the information provided by the documentation and the context help a bit poor. Often it would be sufficient if I could predict the behaviour from the online help. I spend much time with trying things out.
In my opinion a object should be initialised by a class method. Like this the caller neither needs information how an object is initialized nor what data is stored or how it is stored. For example a property node can contain information which is calculated on the fly, but the user works with it like it was stored in private data.
To leave the creation of data to the caller of a class breaks this principle.
I like your idea of identifying an instance by its DVR and find this nice although it does not solve my problem.
I solved by leaving the creator VI, which is called by the class constructor asychronously running (my previous idea). The class destructor destroys the reference and the creator stops.
If I create a DVR, destroy it , fetch the value and create the DVR again, do I get the same memory adress and is there a way to evaluate the numerical adress of a reference? This would be like the ampersand operator in C.
01-20-2016 04:19 AM - edited 01-20-2016 04:21 AM
@dkfire wrote:
To add to what tst wrote, remember that objects(classes) in LabVIEW are by value, not by reference like in c++.
Thank you, but this was clear for both of us. Even if we did not concern to a specific programming language.
01-20-2016 05:17 AM
@drjdpowell wrote:
LabVIEW doesn't have a "garbage collector" and I don't believe it is possible to add one easily like one can in an "everything has a reference" lanuage. Garbage collectors, I believe, increment over all object references and find objects not refered to by any other still-live object. That doesn't work in LabVIEW. The "die with your creating hierarchy" thing works well in the large majority of programs that don't asynchronously call VI's.
Why are you using a DVR, by the way? There are other ways in labVIEW to hold common data, such as an "Action Engine".
C is an ""everything has a reference" language". It is not easy to add a garbage collector to C because this requires (in my opinion) an asychronous thread. You can write an garbage collector in C, but this is not easy even for simple applications. At least you have to fork(); and you should have fork();ed also for other reason.
How and when objects are destroyed is subject of the memory managment strategy. One way might be to increment over the user of the object references. But there are other. If you have only this specific strategy in mind I admit that LabView hasn't a garbage collector.
I am using DVRs because I want to share information between groups of instances of a class. I have learned from the previous (first) question in this forum, that it is very easy to create a copy from a class.
In another part of my application I provide a registration service for classes based on DVRs. Like this the registration service can see immediatly if a class is idle, for example.
A global variable or a finite state machine can solve this too I think. But I don't use global variables at all, because I want to define for every data the minimum scope. In my opinion global variables mess everything up and have a high risk of unwanted side effects. Due to this FGVs are also an anti pattern for me, although I know that many better programmer than me use them. This can be a problem of DVRs too and I try hard to limit this.
Finite state machines are a nice invention, but I do not like the LabView design from 2007 and I would prefer a object oriented approach since my application is already to big to maintain single VIs.
Since I use a lot of asynchronous calls, mostly I do not even collect data from them (Flag 0x80), and the VIs I call asynchronously are determined dynamically, the garbage collection strategy of LabView does not work for me.
01-20-2016 05:36 AM
@mthimm1 wrote:
@tst wrote:
Not the VI. The hierarchy (which is defined by a top level VI). That's an important distinction, because it means references do generally stay alive correctly.
You are right, this is something different. But the word "correctly" implies that there is some canonical programming scheme for references. Since the concept of a reference is used in a lot of different programming languages and programming paradigms this is hard to believe. "Correct" in the sense, that LabView is doing what LabView does is a tautology, and I would not ever dare to doubt this.
Correctly means simply that the reference stays alive for the right amount of time. Similar to a wizard, it does not depart early, nor late. It's there when you need it and is cleaned up automatically when you're done. This has nothing to do with definitions, but with how programs written in LV actually behave. Of course, like I said, that's a generalization, and there are cases where this automatic cleanup is a problem, as you see, but those cases are (or at least used to be) relatively advanced.
mthimm1 wrote:At least there should be a best practice for LabView. I find sometimes the information provided by the documentation and the context help a bit poor. Often it would be sufficient if I could predict the behaviour from the online help. I spend much time with trying things out.
I don't have enough experience with other help systems to be able to tell for sure. I hear some people say that the LV help is really good and others who say it's bad. I can say that when I need language references for other languages (for instance, on how SQL Server will behave) I usually don't use the built-in help, but go to Google. I think that's generally the case with most languages, certainly when it comes to things like best practices, software architectures, etc.
mthimm1 wrote:
In my opinion a object should be initialised by a class method. Like this the caller neither needs information how an object is initialized nor what data is stored or how it is stored. For example a property node can contain information which is calculated on the fly, but the user works with it like it was stored in private data.
To leave the creation of data to the caller of a class breaks this principle.
Perfectly reasonable from an OO standpoint, but somewhat irrelevant here. I didn't say that the caller should create a reference used inside the class. The reference can certainly be created inside the class and kept private. However, LV is a dataflow language, and it has certain rules about the scope of resource allocation (scope in the lifetime sense, not in the access rights sense). This is one of those rules, and unless NI changes it, you need to account for it in your code, which means making sure the creating hierarchy doesn't disappear as long as the reference is needed.
The fact that LV objects are by-val and don't have explicit constructors is also relevant here, as there's no official point to initialize references internal to the object. That's where wrapping the object itself in a DVR comes in handy, if what you want is to actually have specific instances by ref.
@mthimm1 wrote:
If I create a DVR, destroy it , fetch the value and create the DVR again, do I get the same memory adress and is there a way to evaluate the numerical adress of a reference? This would be like the ampersand operator in C.
The question is basically meaningless. Memory and addresses aren't relevant terms in LV. You never see them. The relevant question can only be framed in terms of the API LV gives you for DVRs, and in that context, there is no way to access that data once the DVR has been destroyed.
01-20-2016 05:43 AM
mthimm1 wrote:I am using DVRs because I want to share information between groups of instances of a class.
Sounds like you want a static variable. The most direct equivalent is a global variable which is a private member of the class. This still has all of the issues of global variables, the worst of which in LV is the potential for race conditions, but it is basically a direct equivalent and it doesn't have the lifetime issue (as long as the class itself stays in memory). The same applies to private functional globals, which can get rid of the race condition issue if done correctly.
@mthimm1 wrote:
Since I use a lot of asynchronous calls, mostly I do not even collect data from them (Flag 0x80), and the VIs I call asynchronously are determined dynamically, the garbage collection strategy of LabView does not work for me.
You are not the only one who writes apps with async processes and with processes starting and stopping dynamically. It is certainly possible to write code which will not have the auto-cleanup issue and many people do.
01-20-2016 06:58 AM
@tst wrote:
@mthimm1 wrote:Since I use a lot of asynchronous calls, mostly I do not even collect data from them (Flag 0x80), and the VIs I call asynchronously are determined dynamically, the garbage collection strategy of LabView does not work for me.
You are not the only one who writes apps with async processes and with processes starting and stopping dynamically. It is certainly possible to write code which will not have the auto-cleanup issue and many people do.
This was a reply to another post of drjpowell:
@drjdpowell wrote:
LabVIEW doesn't have a "garbage collector" and I don't believe it is possible to add one easily like one can in an "everything has a reference" lanuage. Garbage collectors, I believe, increment over all object references and find objects not refered to by any other still-live object. That doesn't work in LabVIEW. The "die with your creating hierarchy" thing works well in the large majority of programs that don't asynchronously call VI's.
Why are you using a DVR, by the way? There are other ways in labVIEW to hold common data, such as an "Action Engine".
My application uses asynchronous calls. This is, what I wanted to say. Due to this my application does not belong to the "majority of programs" mentioned here, which can deal with the ""die with your creating hierarchy" thing".
Sorry that you misunderstood this, I don't now how to mark a reply as reply to a specific post.
Race conditions are not a problem for me, but unexpected death of my application due to synchronous access of the same portion of the memory. This can not happen as I have learned from crossrulz in one of the two replies which I marked as solution.
I do not want a static variable since a static variable is global for all instances of a class. I wanted to say this, when I wrote "between groups of instances of a class". Sorry that that was not clear enough. Surely there are other ways, but meanwhile my solution is already implemented and working perfectly. I want to keep it for the moment because the next task is already waiting for me.
Thank you for your assistance!
01-20-2016 08:11 AM
I understood you were replying to drjdpowell. You quoted his post which was enough to give the context. My point was that the majority of programs don't have to worry about this, because they function correctly with the existing rule, but even in the types of the programs where you do have to worry about this, there are many people who write them (drjdpowell is one, as am I and rolfk and I expect crossrulz too) and the way LV handles memory does work in those cases too, provided you know what you're doing.
mthimm1 wrote:I do not want a static variable since a static variable is global for all instances of a class. I wanted to say this, when I wrote "between groups of instances of a class". Sorry that that was not clear enough.
Ah, yes, if you have something that's only semi-global and I assume has multiple instances of itself (one for each group of objects), then you will need more complicated mechanisms to ensure you're interacting with the correct piece of data.
01-20-2016 09:24 AM
@tst wrote:
...drjdpowell is one...
I use asynchronous calls extensively, but I try to follow the "Actor Model", where one doesn't share by-ref data. Instead, one shares communication methods to send messages to the actors. Communication references are naturally owned by the actor that is the receiver of the messages, so "die-with-creator" works great. I actually use this behaviour to provide "watchdog" functionality, where the shutdown of one actor invalidates a reference which triggers notification of another actor. The usual use of this is to extend the "die-with-creator" behaviour to subactors created by other actors (built into actors in "Messenger Library").
08-24-2017 01:51 AM
I just got bit pretty bad by this behaviour. I now understand how it works but I have to admit I don't find it intuitive. Is there any mention of this in the documentation? Based on the introduction ("...advanced users who want more control over memory allocation can create references to data...") and the basic functionality and features of Data Value References (e.g. data lock/serialized access) I would expect it to go hand in hand with asynschronous code. The fact that the data is ultimately also managed by the top level creator VI is acceptable but learning about it only through error is very unfortunate.