Jump to content

Dynamic dispatch & Shared reentrancy


Recommended Posts

Hi everyone,

I'm loosing my mind trying to solve an issue about POO, dynamic dispatch and shared reentrancy. An example of my system's architecture is available in the Labview code enclosed.

 

gallery_53538_118_376.png

 

A "Main.vi" runs several parrallel loops (they can be considered as differents processes), each one calling a shared reentrant VI. The loops need to run independently (so I need clones) and shared reentrancy is required by utilization of POO and dynamic dispatch.

 

gallery_53538_118_8488.png

 

This subVI contains an uninitialized shift register to have a memory effect between calls of the loop (in the example the subVI is a simple counter).

 

gallery_53538_118_3833.png

When I run the Main.vi, the first (top yellow) and second (middle yellow) loops don't work properly (counters mix the data). The 2 instances of the shared reentrant subVI are randomly provided to the first or second loop during the execution.

I'm surprised of this behavior. Does that mean I can't use any of the NI signal processing VIs (which largely use uninitialized registers) in shared reentrant VIs as soon as several of them are allocated in the same time ?

Many thanks for your help,
Nicolas

 

Test Shared Reentrancy.zip

  • Like 1
Link to comment

It sounds like you are using the wrong kind of reentrant VI.  The NI signal processing VIs uses the Preallocate method of reentrancy which is less efficient than shared clones because a new copy must be created for each copy, instead of reusing already made copies.  The benefit is of course things like uninitialized shift registers work as expected because each copy is reserved for that call.

  • Like 1
Link to comment

You're right. You can't use un-initialized shift registers like you want inside a shared clone VI. This does mean that if you put any of NI's VIs which use pre-allocated re-entrancy inside your shared clone VI you will be screwed. And since you're forced to use shared clones with Dynamic Dispatch VIs, you're double screwed.

 

All and all, this means that data that needs to be stored between calls needs to be placed in the object's private data. It will now live on the shift register and the clone will just use the data, instead of storing the data. If you were writing the code from scratch, this would be no big deal, but since you're using NI's code, it becomes a pain. What I ended up doing is writing my own version of NI's VIs (typically started by coping and pasting) that strips out all of the shift registers. I think (I've not tested this way) you could also open a VI reference to the VI, then use a call by reference, and store the reference in the objects private data. I'd have to play around  with this way though.

 

It's annoying, and honestly, I think that NI should go back and give us a version of their VI's that don't have shift registers in them. Obviously this is a lot of work without much return, so I don't blame NI for not doing it.

 

And that kids, is why you don't use globals unless you're 100% sure you need them and there's no other way.

Link to comment

It sounds like you are using the wrong kind of reentrant VI.  The NI signal processing VIs uses the Preallocate method of reentrancy which is less efficient than shared clones because a new copy must be created for each copy, instead of reusing already made copies.  The benefit is of course things like uninitialized shift registers work as expected because each copy is reserved for that call.

 

Hi hooovahh,

Yes I know NI VIs have to be called as preallocated reentrant VIs, that is what I actually do. The "relation" between my shared clone and the NI VI called inside is correctly maintained. My problem comes from the relation between the main VI calling shared clones. My question was more about the architecture of the system.

 

You're right. You can't use un-initialized shift registers like you want inside a shared clone VI. This does mean that if you put any of NI's VIs which use pre-allocated re-entrancy inside your shared clone VI you will be screwed. And since you're forced to use shared clones with Dynamic Dispatch VIs, you're double screwed.

 

All and all, this means that data that needs to be stored between calls needs to be placed in the object's private data. It will now live on the shift register and the clone will just use the data, instead of storing the data. If you were writing the code from scratch, this would be no big deal, but since you're using NI's code, it becomes a pain. What I ended up doing is writing my own version of NI's VIs (typically started by coping and pasting) that strips out all of the shift registers. I think (I've not tested this way) you could also open a VI reference to the VI, then use a call by reference, and store the reference in the objects private data. I'd have to play around  with this way though.

 

It's annoying, and honestly, I think that NI should go back and give us a version of their VI's that don't have shift registers in them. Obviously this is a lot of work without much return, so I don't blame NI for not doing it.

 

And that kids, is why you don't use globals unless you're 100% sure you need them and there's no other way.

 

Hi QueueYueue,

OK that is what I was afraid of. I started a development from scratch so I used to store all my custom code data in the objects. But for the needs of the project, the client asked then to use some NI VIs in shared clones. As you say, I'm double screwed.

I have tried to store a VI reference in the object, but Labview does not allow to open a shared reentrant VI with a call by reference (which stands to reason since Labview manages clones and not my code).

gallery_53538_118_10352.png

Calling NI VIs by reference and storing this references in the object works but I am afraid of the number of references I will need...

Thanks for your answers, I'm installing last version of LV to see if there is any difference.

Link to comment

Labview does not allow to open a shared reentrant VI with a call by reference

You can work around that.   Have your classes use static preallocate-clone methods, and have a dynamic method that returns a prepared reference to one of these static clones.  You store that reference in your object on creation.  Then use a single call-by-reference in your loop, and all preallocate-clone NI subVIs should work correctly.

 

— James

Link to comment

Welcome to LVOOP... this is one of my biggest gripes about using it for certain things.  The work arounds listed above help, but using LVOOP design patterns can make some of the simplest things so heavy.  LIke these guys mentioned before, you can make a new class that contains the shift register data of the NI method you want to use and write a custom method for the action want.  Then include this class in your parent and pass it around in each child.  

 

 

You can work around that.   Have your classes use static preallocate-clone methods, and have a dynamic method that returns a prepared reference to one of these static clones.  You store that reference in your object on creation.  Then use a single call-by-reference in your loop, and all preallocate-clone NI subVIs should work correctly.

 

— James

 

This sounds like an interesting approach, but lately, LV2013 SP1, i keep getting burned by hacking around the call-by-reference nodes to try and fix issues like this.  There seem to be some weird deep labview crashing bugs that i keep stumbling upon when i do things like this.  

Link to comment

Ohh man, lots going on here. I made an example using references.

 

Hi hooovahh,


Yes I know NI VIs have to be called as preallocated reentrant VIs, that is what I actually do. The "relation" between my shared clone and the NI VI called inside is correctly maintained. My problem comes from the relation between the main VI calling shared clones. My question was more about the architecture of the system.

 

 

Hi QueueYueue,


OK that is what I was afraid of. I started a development from scratch so I used to store all my custom code data in the objects. But for the needs of the project, the client asked then to use some NI VIs in shared clones. As you say, I'm double screwed.


I have tried to store a VI reference in the object, but Labview does not allow to open a shared reentrant VI with a call by reference (which stands to reason since Labview manages clones and not my code).

gallery_53538_118_10352.png

Calling NI VIs by reference and storing this references in the object works but I am afraid of the number of references I will need...

Thanks for your answers, I'm installing last version of LV to see if there is any difference.

 

Calling NI's VIs by ref is what I'm talking about. There's no difference in newer versions of LabVIEW. The reason you cannot call a dynamic dispatch VI by reference is because a reference points to one VI. Dynamic Dispatch, however, means you don't know what VI you're calling until runtime (and by runtime I mean right before the VI is called). What if you open a reference to  Class A's Implementation of a method, then you wire Class B to the input? They both have the same connector pane. What if you open a reference to the parent's implementation, but then wire in a child? You need to call the Child's method, but you have a reference to the parent's. There's a bunch of situations where you'd be trying to call the wrong implementation on the wrong class, so that's why you get a broken run arrow.

 

In response to the number of references: Who cares? Store all of the references inside the class data, and then give that class a "cleanup" method. Done. Opening a lot of references is no big deal as long as you close them.

 

You can work around that.   Have your classes use static preallocate-clone methods, and have a dynamic method that returns a prepared reference to one of these static clones.  You store that reference in your object on creation.  Then use a single call-by-reference in your loop, and all preallocate-clone NI subVIs should work correctly.

 

— James

It'll work, but I'm pretty sure you'd have to do some casting to get the control panels to line up. See my posted example for a cleaner (IMO) implementation that's pretty similar to what you're suggesting.

 

Welcome to LVOOP... this is one of my biggest gripes about using it for certain things.  The work arounds listed above help, but using LVOOP design patterns can make some of the simplest things so heavy.  LIke these guys mentioned before, you can make a new class that contains the shift register data of the NI method you want to use and write a custom method for the action want.  Then include this class in your parent and pass it around in each child.  

 

 

This sounds like an interesting approach, but lately, LV2013 SP1, i keep getting burned by hacking around the call-by-reference nodes to try and fix issues like this.  There seem to be some weird deep labview crashing bugs that i keep stumbling upon when i do things like this.  

 

This has almost nothing to do with LVOOP. If you called a pre-allocated VI inside of a shared clones VI in any instance you'd have this problem. This is an advanced feature that needs some extra thought by the programmer.

 

About the example:

Open "Test VI.vi" and run it with "Call By Ref?" set to False and run the VI. Notice string 1 and string 2 are changing. They will flip flop based on which instance they happen to get (it's a race!)

 

Stop the VI by clicking the stop button. Now set "Call By Ref?" to True and run the VI. String 1 and String 2 will now get their own values that stay the same.

 

Take a look at the code, most of the magic happens in "Test Class.lvclass:Test VI.vi" (note: This is the re-entrant shared clones VI). On first call (for that object) it opens a new ref to the pre-allocated VI, and on subsequent calls it just uses the reference it already has open.
 

Let me know if you have questions.

Pealloc Testing.zip

Edited by QueueYueue
Link to comment

You're right. You can't use un-initialized shift registers like you want inside a shared clone VI. This does mean that if you put any of NI's VIs which use pre-allocated re-entrancy inside your shared clone VI you will be screwed.

 

Why is that?

Link to comment

It'll work, but I'm pretty sure you'd have to do some casting to get the control panels to line up. See my posted example for a cleaner (IMO) implementation that's pretty similar to what you're suggesting.

Can’t open the example ‘cause I’m still on 2013, but there is no “casting†required in the method I’m thinking of.

Link to comment

Why is that?

You're in a race-condition. You get shared clones instantiated as needed. That means if you have two objects that both call the same VI, sometimes they'll get the first instantiation of the VI, sometimes they'll get the second. LV decides which. Take a look at the example it illustrates it.

 

Can’t open the example ‘cause I’m still on 2013, but there is no “casting†required in the method I’m thinking of.

The VI who's ref you create is going to have a common connector pane across all classes. This means that if you have a child class, the input terminal will have to be of the parent's type. so you'll have to do a cast if you want to access any of the child's data. Either that or you'll have to use "Set control by name" invoke nodes, which I avoid at all costs.

 

Attached is a 2012 version of my example.

Pealloc Testing 2012.zip

Link to comment

Could you not just create a static dispatch VI in your parent class that gets used in the ACBR node, and that VI then simply contains a single dynamic dispatch VI - the VI you originally wanted to call from the ACBR node?

 

Dynamic dispatch VIs won't be correctly linked to static VI callers, as QueueYueue has said : Labview decides of which instance to use at every step of the loop / call of the shared clone.

 

The VI who's ref you create is going to have a common connector pane across all classes. This means that if you have a child class, the input terminal will have to be of the parent's type. so you'll have to do a cast if you want to access any of the child's data. Either that or you'll have to use "Set control by name" invoke nodes, which I avoid at all costs.

 

Attached is a 2012 version of my example.

 

I have chosen to use the parent class indeed. One of my colleagues advised me to try with variants and compare performances / security of the 2 solutions.

 

This sounds like an interesting approach, but lately, LV2013 SP1, i keep getting burned by hacking around the call-by-reference nodes to try and fix issues like this.  There seem to be some weird deep labview crashing bugs that i keep stumbling upon when i do things like this.  

 

I plan to do some tests with my full system. Do you have any details about these errors errors odoylerules or was it a traditionnal simple Labview crash ?

Link to comment

Calling NI's VIs by ref is what I'm talking about. There's no difference in newer versions of LabVIEW. The reason you cannot call a dynamic dispatch VI by reference is because a reference points to one VI. Dynamic Dispatch, however, means you don't know what VI you're calling until runtime (and by runtime I mean right before the VI is called). What if you open a reference to  Class A's Implementation of a method, then you wire Class B to the input? They both have the same connector pane. What if you open a reference to the parent's implementation, but then wire in a child? You need to call the Child's method, but you have a reference to the parent's. There's a bunch of situations where you'd be trying to call the wrong implementation on the wrong class, so that's why you get a broken run arrow.

 

Hi QueueYueue,

 

More I'm reading this thread and less I'm understanding the reentrancy problem hidden behind the dynamic dispatch. Maybe you can help me to understand how things work under the hood.

I agree that you cannot know @ edit time which instance of VI that has to be launched when using dynamic dispatch VI, but you know it @run time ! Let's assume that you are using a standard dynamic dispatch VI (reentrancy set as 'shared'), what happens the very first time that child VI is called ? I guess that, at the time of executing the VI code, LV allocates the memory needed for this specific child... No ?

If LV can do it once in 'shared' mode, why it cannot do it every time you would call a 'pre-allocated' child VI ?

Reentrancy does not concern the way you launch an execution, but how you manage the memory allocated with previous launch.

 

When using a 'standard' VI, you call it using VI server by path. You can even build the path at run-time, open a reference on the VI w/option 0x40 and launch the execution of an unknown VI at runtime as reentrant in 'pre-allocated' mode...

Edited by Zyl
Link to comment

Zyl,

 

You're close to understanding it correctly. 

 

Here's how I think about re-entrancy. Pre-allocate -> VI is allocated during compile time. Shared -> VI is allocated during run time. So.....

 

Let's assume that you are using a standard dynamic dispatch VI (reentrancy set as 'shared'), what happens the very first time that child VI is called ? I guess that, at the time of executing the VI code, LV allocates the memory needed for this specific child... No ?

 Correct.

 

 

If LV can do it once in 'shared' mode, why it cannot do it every time you would call a 'pre-allocated' child VI ?

I don't understand what you mean by this.

 

 

 
Reentrancy does not concern the way you launch an execution, but how you manage the memory allocated with previous launch.

Re-entrancy does concern the way you launch an execution AND how you manage the memory allocated with the previous launch.

 

Shared clones are launched from a clone pool. So when execution needs a clone, it asks the clone pool for one. If there is one available, it gets it. If there isn't one available, the clone pool creates one for you. When you're done you return the clone to the pool. You have no control over which instance you get from the clone pool, it decides. This is why the uninitialized shift registers don't work as expected. You're expecting to get the same clone back every time, but the clone pool manager gives you back the first available clone.

 

With pre-allocate you're linking to specific instance of the clone. There is no clone pool stuff because you're saying "I know I'll need this here so create it up during compile to save some execution time"

 

So why can't you pre-allocate a dynamic dispatch VI? Because to pre-allocate you need to link to a specific instance of the VI right? To know the specific instance you need to know the runtime data type of the class coming in, which we don't know. To make pre-allocated work you'd need to allocate (at compile time) memory for every possible VI that could be called. This means that if you had a 2 child classes you'd need to allocate memory for 3 VIs (one for the parent and one for each child). Now what do you do if one of the child VIs calls the parent's implementation? maybe that should be allocated up front? Now what happens if you dynamically load a class in a plug in architecture? You're code would need to be recompiled to add the memory for that new class? See how we're running into all of these weird situations, all of which would required allocating a bunch more memory? Maybe it's possible, but it's not efficient, and it'd possibly introduce a whole slew of other complicated design decisions. So that's why you're not allowed to do it.

Link to comment

 

 Quote

If LV can do it once in 'shared' mode, why it cannot do it every time you would call a 'pre-allocated' child VI ?

 
I don't understand what you mean by this.

 

I can guess that the idea behind the question is : Labview can load in memory an instance of the correct child VI at runtime with Shared Reentrancy. Why couldn't it be possible to load, not a PREallocated, but an RUNTIMEallocated VI ?

 

 

So why can't you pre-allocate a dynamic dispatch VI? Because to pre-allocate you need to link to a specific instance of the VI right? To know the specific instance you need to know the runtime data type of the class coming in, which we don't know.

 

As Zyl has said in his last post : Labview allows you to launch a VI with pre-allocated option based on its only path, which can be edited at runtime. As far as I can imagine, Labview doesn't know anything about the VI until runtime but its connector pane which gives no informations about the memory or anything else.

 

 

To make pre-allocated work you'd need to allocate (at compile time) memory for every possible VI that could be called. This means that if you had a 2 child classes you'd need to allocate memory for 3 VIs (one for the parent and one for each child). Now what do you do if one of the child VIs calls the parent's implementation? maybe that should be allocated up front? Now what happens if you dynamically load a class in a plug in architecture? You're code would need to be recompiled to add the memory for that new class? See how we're running into all of these weird situations, all of which would required allocating a bunch more memory?

 

If allocation is done at compilation time I agree with your arguments, but it becomes pointless to use POO, inheritance an polymorphism. Linking resolution should be performed at runtime based on the type of the object, which obviously knows what object it is and which methods to call.

 

Maybe it's possible, but it's not efficient, and it'd possibly introduce a whole slew of other complicated design decisions. So that's why you're not allowed to do it.

 

This highlight my initial interogation and the reason of this topic : So we don't have any leeway beetween efficiency and feasability ? How is it possible to integrate both (real) POO and NI toolkits together ?

 

 

Since last week I have a pending ticket with NI technical support. I will keep you in touch as soon as I get an official enlightenment from NI.

Link to comment

I get what you're saying, and I think it is neither pre-alloc or shared re-entrancy. You want a hybrid.

 

I think there would be a lot of design decisions to be made with what you're suggesting. I feel like this would end up with new clones being allocated when you didn't expect it, sometimes a new clone wouldn't get allocated when expected, etc. Rather than forcing us to learn there rules (it's already hard enough with two re-entrancy types) it's just not available. You can do this on your own however. You can make all of the somewhat-arbitrary design decisions on your own, just follow drjdpowell's advice from earlier: 

 

You can work around that.   Have your classes use static preallocate-clone methods, and have a dynamic method that returns a prepared reference to one of these static clones.  You store that reference in your object on creation.  Then use a single call-by-reference in your loop, and all preallocate-clone NI subVIs should work correctly.

 

— James

 But i'm telling you, you're setting yourself up for confusion and memory leaks. I think my pattern is better.

Link to comment

 But i'm telling you, you're setting yourself up for confusion and memory leaks. I think my pattern is better.

 

That may be true as a "work-around" but having local variable storage like this, in a class, is an anti-pattern to begin with. It is the equivalent of declaring a static var in an accessor. The issue here is thread safety and that method of local storage is not thread safe.

 

There used to be a time when LabVIEW programmers didn't have to worry about memory leaks and thread safety-that was an "other languages" affliction.[queue wavy, flashback sequence]

Link to comment

There used to be a time when LabVIEW programmers didn't have to worry about memory leaks and thread safety-that was an "other languages" affliction.[queue wavy, flashback sequence]

 

I'm not sure what you think changed. With LV you still don't have to worry about memory leaks (unless there's a bug in LV or you allocate something and don't release it, but that's nothing new), nor do you have to worry about thread safety any more than you had to before. LV still guarantees safe reading and writing of data, but if you create race conditions, that's a bug in your code. Surely you're not suggesting that race conditions didn't exist before the addition of this reentrancy mode around 8.5...

 

And yes, it wasn't that hard to get similar issues with reentrancy in the past (for instance, by calling a reentrant VI in a loop, resulting in all iterations calling the same instance, because preallocation goes by diagram location).

Link to comment

I'm not sure what you think changed. With LV you still don't have to worry about memory leaks (unless there's a bug in LV or you allocate something and don't release it, but that's nothing new), nor do you have to worry about thread safety any more than you had to before. LV still guarantees safe reading and writing of data, but if you create race conditions, that's a bug in your code. Surely you're not suggesting that race conditions didn't exist before the addition of this reentrancy mode around 8.5...

 

And yes, it wasn't that hard to get similar issues with reentrancy in the past (for instance, by calling a reentrant VI in a loop, resulting in all iterations calling the same instance, because preallocation goes by diagram location).

 

Not so.

 

Memory leaks were introduced with the queue, notifier etc primitives. Before then there was no way to leak memory programmatically, only allocate huge amounts to get the bulldozer.

 

The thread safety issue was introduced with pooling of re-entrant VIs. Prior to that there was only pre-allocation re-entrancy and no you don't get similar issues with pre-allocated since it is lack of control over thread execution order of an instance that causes the issue which is non existent with pre-allocated.To replicate in classical LabVIEW you have to call a pre-allocated, re-entrant from a shared clones VI.

 

If you wan to go back further, there was only a single virtual context so you could have executables share global variables :)

Edited by ShaunR
Link to comment

No one is forcing you to use all these newfangled features like queues or VI refnums. You could stick to simple globals, or you could use LV 3, if you manage to load it :throwpc: .  I know I use shared reentrancy relatively rarely (mainly because if I use reentrancy, I usually want state).

 

I'm not saying the issue I mentioned is the same. I meant that shared reentrancy is not unique or first in requiring you to understand certain details about the way LV code functions under specific conditions. I'm sure that like me, you learned some of these details by writing and running code which then failed because "oh, I didn't realize that X".

 

Is it possible to leak memory? Sure, create a queue, push a bunch of data into it and then ignore it and keep the hierarchy running. I don't see any way having queues would not allow you to create memory leaks of this type. LV does have its rules for when it will release the memory, so I wouldn't consider it a real memory leak (it's not like calling malloc in C and then ignoring the pointer). Whether LV actually releases the memory or hangs onto it like someone with abandonment issues is another story.

Link to comment

LV does have its rules for when it will release the memory, so I wouldn't consider it a real memory leak (it's not like calling malloc in C and then ignoring the pointer). Whether LV actually releases the memory or hangs onto it like someone with abandonment issues is another story.

It could totally free that memory if it really wanted to. It doesn't have a problem. :(

 

 

 

The thread safety issue was introduced with pooling of re-entrant VIs.

Only if you store state inside of your VIs, which I personally think is a bad idea with a few exceptions (FPGA, action engine operating on a singleton resource, etc). Pure math functions are certainly not on the list.

 

 

Also, staab posted up this suggestion a while ago: http://forums.ni.com/t5/ideas/v2/ideapage/blog-id/labviewideas/article-id/19226

Didn't seem to be popular for whatever reason, I think he probably just didn't explain how big the issue was. Either way, my hope is that enough people know this limitation by now that they'll stop making functions with internal state, but we'll see.

Link to comment

(mainly because if I use reentrancy, I usually want state).

 

You see? That's why  this is such a problem for people. Re-entrancy is meant for non-blocking parallel execution. Using it as a memory storage method is just an abuse of its apparent behaviour. I think people looked at LV2 style globals and thought they would be clever and use re-entrant VIs ina similar way. There is one huge difference with a LV2 global though. It is a singleton! When I use re-entrancy it is because I want things to be non-blocking. I use storage methods for memory storage like the class private data which was intended for that.

 

That doesn't help the OP, though since it is a NI toolkit that has this so the only sane recourse is to not set class method to re-entrant and put a huge note in the method diagram of why.

 

 a real memory leak (it's not like calling malloc in C and then ignoring the pointer)

 

It is exactly the same as malloc for memory leaks. Obtaining a queue reference creates a copy of the ref so the programmer is forced to reference count and twin each obtain with a release.Keep obtaining and miss one release and you will run out of memory eventually. This was such a problem wherever I went to work, I created the Queue action engine that encapsulates the queue primitives to ensure there is only ever a maximum of 1 reference. Download any of my software and you will see it where queues are used.

It could totally free that memory if it really wanted to. It doesn't have a problem. :(

 

Only if you store state inside of your VIs, which I personally think is a bad idea with a few exceptions (FPGA, action engine operating on a singleton resource, etc). Pure math functions are certainly not on the list.

 

 

Also, staab posted up this suggestion a while ago: http://forums.ni.com/t5/ideas/v2/ideapage/blog-id/labviewideas/article-id/19226

Didn't seem to be popular for whatever reason, I think he probably just didn't explain how big the issue was. Either way, my hope is that enough people know this limitation by now that they'll stop making functions with internal state, but we'll see.

Amen. But I would argue they are not exceptions. The maxim is "never use re-entrant memory storage" and all those you describe are not re-entrant (well, FPGA might be-I don't really know what you are referring to there). I wouldn't go as far as say "never store state in a VI" otherwise you are back to global and local variables and those you list were invented to address the issues with those memory storage methods.

Edited by ShaunR
Link to comment

 Re-entrancy is meant for non-blocking parallel execution.

 

a) Citation needed.

b) I don't care even if it is. In my code I relatively rarely need parallel execution and more commonly want copies which will maintain state (some dynamic and some static). Classic preallocate reentrancy does that (with some exceptions). Both use cases are valid.

 

 

It is exactly the same as malloc for memory leaks. Obtaining a queue reference creates a copy of the ref so the programmer is forced to reference count and twin each obtain with a release.

 

That's if you do obtain by name. I usually don't. Even so, the API does give you the option of force destroying the queue. In any case, you know that LV can't magically release the memory because it has no way of knowing when you're done with it unless you tell it. The exception is when the creating hierarchy goes idle, but people usually only notice that when it causes their code not to work because it did release something they think is still active.

 

If you have a suggestion for how LV can otherwise manage this memory, I'd be interested in hearing it.

Link to comment

Sure. Obtain returns the actual queue reference rather than a copy.

 

Like I said, I rarely use queues by name, but that will certainly break the existing behavior where LV can keep the queue alive in different hierarchies because it has separate references. Doesn't sound like progress to me. If all you care about is cleaning up, why not just use the force destroy input?

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.