LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

When are DVRs automatically deleted?

Solved!
Go to solution

Hi Folks,

 

I am experiencing a strange behavior with data value references in LV OOP:

 

I would like to equip a class with a data value reference. When the first instance of this class ist created, I create a new data value reference and a semaphore and put it in the private data cluster of the class.

 

Like this, after I create a new branch the data in both instances should be the same (i.e the dvr adresses the same part of the memory) and to avoid data access problems, the semaphore is used, to ensure, that the instances do not interfere, when they access the data. 

 

After a while the access vi throws error number 1 on the semaphore and error number 1556 on the reference. It seems that both, the reference and the semaphore are freed.

 

Ist there some garbage collector, that destroys references automatically after the constructor vi has finished, although there still exists a class which stores the reference?

 

Thanks in advance

 

Edit: If I read the data value references immediatly after I have called the class constructor and if I put a probe on the semaphore, I get the "invalid reference" as value, although the memory address has not changed. Strange!

0 Kudos
Message 1 of 19
(8,522 Views)
Solution
Accepted by topic author mthimm1

mthimm1 wrote:

 

Ist there some garbage collector, that destroys references automatically after the constructor vi has finished, although there still exists a class which stores the reference?


Yes there is! All LabVIEW refnums are garbage collected as soon as the tope level VI in whose hierarchy the refnum was created goes idle. So if you have an initialize VI that is started as top level VI and which creates your DVR and semaphore and that initialize VI is started dynamically (which actors are as far as I know) and then it ends its merry task and goes idle, the refnum is destroyed and your other actors won't see a valid refnum object anymore.

 

So it is important to create those refnums inside the hierarchy that is going to use them or alternatively have a top level VI that stays active for the duration of the entire program executation and delegate the creation of those refnums to that VI or some subVI which runs in that hierarchy.

Rolf Kalbermatter
My Blog
Message 2 of 19
(8,476 Views)
Solution
Accepted by topic author mthimm1

mthimm1 wrote:  and to avoid data access problems, the semaphore is used, to ensure, that the instances do not interfere, when they access the data. 

There is no need for the Semaphore.  The In Place Element Structure, which you use to access the DVR data, acts as a block (only 1 process can access it at a time).

 

References are destroyed when the creator of the reference is released from memory.  Maybe you should have your top level VI create the reference.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 3 of 19
(8,468 Views)

Thanks a lot!

 

This was a really weird problem for me, because the reference seemed to disappear at random times, which was really difficult to locate.

 

Do you know if there any way to turn garbage collecting off? Like this the adult programmer can decide himself, when which reference should be deallocated. This would be nice for dynamic creation and deletion of  data references in concurrent threads...

 

I have to admitt that I am new to LV but the way the problem of garbage collecting is solved here seems to me some kind of an anti pattern because there is also an explicit destructor for data value references.

 

Thanks also for your hint regarding semaphores. I am sure that this will save me some convas time in the future!

 

I will continue to use them in this case anyway, because I want to extend the blocking time to perform some other operations.

0 Kudos
Message 4 of 19
(8,419 Views)

Nope, garbage collection for refnums is not generally possible to disable, except for VISA refnums where you have an option in the LabVIEW Option dialogs to not automatically garbage collect them.

Rolf Kalbermatter
My Blog
Message 5 of 19
(8,407 Views)

@mthimm1 wrote:

I have to admitt that I am new to LV but the way the problem of garbage collecting is solved here seems to me some kind of an anti pattern because there is also an explicit destructor for data value references.

Not so much an anti-pattern as a reasonable rule which fails under specific circumstances. AFAIK, this behavior comes from long before automatic memory management and parallel processes were common, but because LV does do automatic memory management, NI did have to decide on when to clear up resources automatically. They chose a fairly reasonable rule of "clean up whenever the creator goes idle". "Adult programmers" aren't really relevant here, as LV does set things up automatically, so it shouldn't be surprising that it also cleans up automatically. If the adult programmer wants the responsibility of handling memory, they can use C. A more encompassing rule for LV would be "clean up automatically only when all your users are idle", but that would require more tracking.

 

The bottom line, as already suggested, is that in this case the adult programmer needs to understand how LV loads and unloads VIs and create the references in a hierarchy which will stay in memory.


___________________
Try to take over the world!
Message 6 of 19
(8,377 Views)

I am not against memory managment.

 

And, since I am new to LabView I did not want to critizise NI or the decisions they have made in order to provide a memory managment at all.

 

But this memory managment seems strange to me, since it limits the use with no reason. There are a lot well known and easy to implement strategies which provide a conservative garbage collection and do not destroy objects still in use. The aquisition of data refs is something I would like to delegate to the constructor of a class. Due to this reasons, it is difficult to understand for me, why the DVRs die with the Vi that created them.

 

As a first approach I am now planning to implement the following workaround:

 

Two VIs, one which is started asynchronously and creates the desired data reference and runs an while loop which stops if the reference has been destroyed externally. The other VI creates a queue wich transports a data reference. The queue is parameter for the first VI which sends the created DVR to the calling vi. Since the creating VI does not die, my reference lives until it is destroyed by the class destructor.

 

Do you think this is a resonable way to deal with my issue or is there something more simple?

0 Kudos
Message 7 of 19
(8,344 Views)

@mthimm1 wrote:
... it is difficult to understand for me, why the DVRs die with the Vi that created them.

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. Like I said, there are other alternatives, but this is the mechanism LV has had for a very long time, and in most cases, it works fine. Where it breaks down is in the case where you run VIs dynamically (making them top level VIs) and obtain references there and those VIs stop before other VIs which use those references.

 

The easiest solution is generally to create the reference in a hierarchy which will stay around as long as that reference is used and usually there is no need to run a special VI just for that. In your case, it's unclear from your description whether the DVR belongs to an instance or to the class (like static variables in C++), but I assume you want it per instance. Often, the more readable option is not to put the DVR in the object, but to make a DVR of the object itself and use that to identify different instances of the class. This is a useful way of making by-ref objects in LV. In either case, you would want the code where the DVR is created to be in a hierarchy which keeps running (most likely the main VI).


___________________
Try to take over the world!
Message 8 of 19
(8,311 Views)

To add to what tst wrote, remember that objects(classes) in LabVIEW are by value, not by reference like in c++.

0 Kudos
Message 9 of 19
(8,304 Views)

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".

0 Kudos
Message 10 of 19
(8,289 Views)

@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. 

0 Kudos
Message 11 of 19
(4,369 Views)

@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.

 

0 Kudos
Message 12 of 19
(4,361 Views)

@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.

0 Kudos
Message 13 of 19
(4,345 Views)

@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.


___________________
Try to take over the world!
0 Kudos
Message 14 of 19
(4,326 Views)

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.


___________________
Try to take over the world!
0 Kudos
Message 15 of 19
(4,322 Views)

@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!

 

 

0 Kudos
Message 16 of 19
(4,308 Views)

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.

 


___________________
Try to take over the world!
0 Kudos
Message 17 of 19
(4,292 Views)

@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").

Message 18 of 19
(4,274 Views)

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.

Message 19 of 19
(3,399 Views)