03-11-2015 02:55 AM
Ah, good point. I actually did check to see whether this happens without the error ring and it didn't, but that was probably because I took the error wire from the clear errors VI, which apparently does force some dependency. That's what you get when you only have a couple of minutes to do something. I figured the code was enough for NI to analyze the problem.
03-11-2015 06:18 AM
Indeed good point by Jeff. I fail to see though why you say it doesn't have to do with the error ring. As tst says (I did the same too) some other constant wires do enforce the dependency and so prevent the error from happening.
So imagine, the dependency is enforced when I use a simple error constant but not if I use an error string. Both are static data so the behavior should be the same. That is why I think that the bug is releated to the error ring.
Am I missing something?
Peter
03-11-2015 07:51 AM
As I see it there are two problems that set up what you saw. Improper code migration of the no error error chain, and the parallel DVR branch not throwing at least a warning. Better to keep that DVR private in an AE or a class.
03-11-2015 10:08 AM
Hmm, what is the point of a DVR if it's not "branched"? Data a DVR points to is shared among all entities that hold a copy of the DVR.
Note that the bug we talk about renders a problem not only in case of destroying a DVR. Any two (concurrent) accesses to a DVR is a race condition. So if the destroy DVR VI in my example is replaced with another in-place reading/writing the DVR, then the race condition depends on whether or not I use an error ring.
Plese see my new example (LV14 code attached).
Is such a usage an antipattern? I don't think so.
03-11-2015 10:59 AM
@Bokor wrote:
Hmm, what is the point of a DVR if it's not "branched"? Data a DVR points to is shared among all entities that hold a copy of the DVR.
Note that the bug we talk about renders a problem not only in case of destroying a DVR. Any two (concurrent) accesses to a DVR is a race condition. So if the destroy DVR VI in my example is replaced with another in-place reading/writing the DVR, then the race condition depends on whether or not I use an error ring.
Plese see my new example (LV14 code attached).
Is such a usage an antipattern? I don't think so.
I would suggest that this is an anti-pattern. My preference would be to create and destroy the DVR for each IPE structure. Branching DVRs only copies the refnum just like creating two DVRs. The idea here is you will help prevent race conditions that access the data concurently Obviously, avoiding race conditions is something to avoid anyhow. Then lets think of what happens with non-fixed width datatypes!
Most likely the second IPE will cause the memory manager to reallocate the buffer's memory location (or more liklely through an out of memory error) Imagine then what happens without data depencance between the IPEs ~~~~~~~Scary stuff.
03-11-2015 11:04 AM
Jeff, what I mean is intended data race, e.g., semaphores. How would you implement in LabVIEW a data structure that is shared among threads?
03-11-2015 11:25 AM
Create the DVRs explicitly for each user rather than branch one reference. Protect concurrent access via semaphore to prevent race conditions.
03-11-2015 12:17 PM
@JÞB wrote:
I would suggest that this is an anti-pattern. My preference would be to create and destroy the DVR for each IPE structure. Branching DVRs only copies the refnum just like creating two DVRs. The idea here is you will help prevent race conditions that access the data concurently Obviously, avoiding race conditions is something to avoid anyhow. Then lets think of what happens with non-fixed width datatypes!
Most likely the second IPE will cause the memory manager to reallocate the buffer's memory location (or more liklely through an out of memory error) Imagine then what happens without data depencance between the IPEs ~~~~~~~Scary stuff.
Sorry, but WHAT?! I'm having trouble making sense of anything you've written here. The entire purpose of a DVR is to provide serialized access to a single piece of data from multiple locations. It's like a safer single-element queue (SEQ). Branching doesn't create 2 DVRs (and branching a queue wire doesn't create 2 queues). Whether the value is fixed-size or not is irrelevant. As with a SEQ, where you can dequeue a string of one length and enqueue one of a different length, you can substitute a different length value into a DVR. The major difference versus a SEQ is that with a DVR, the dequeue-modify-enqueue is guaranteed to be atomic.
There's nothing scary at all about the situation you describe, and you absolutely won't get a memory error from it (or if you do, it's a bug).
03-12-2015 11:56 AM
One more thought about this. Splitting a DVR wire is definitely not an anti-pattern. One common use of a DVR is with a LabVIEW Object, where you want to call several different methods (which may modify data) on the same object in different locations. There isn't necessarily a race condition - the methods may work on different pieces of data within the object, so the resulting state of the object is the same regardless of the order in which the methods execute - but during method execution, the method needs exclusive access to the object (you can't have multiple methods modifying the same object concurrently). The DVR provides that locking. Only one IPE has access to the underlying data a time; all other IPEs that need access to the DVR will wait until it becomes available.
The only thing that's odd about your code here is that you could chain the DVR wire through the while loop and IPE, but you chose to route around it.
Jeff - let's say that the underlying data was actually an object that contains a string, and inside the IPE the code called an accessor that wrote a new string to the object. Would you still think that was "scary"? If not, what's the difference between that and a string as the wrapped data type, given that objects are by-value?
03-12-2015 04:03 PM
Nathan, I actually trust the LabVIEW memory manager to properly dereference the DVR Data. (and yes, it took me a while to trust it ) However, You run the risk of some other developer hacking on code over-there deciding to destroy the DVR you were depending on still existing. There is not method to enforce the opener of the DVR is the Closer of the DVR except a style check. (and your own VIA Test) One reason I tend to use them only as needed and for what nothing else can do.
With a Class, I probably would not use a DVR unless I wanted to take certain related actions in multiple IPEs with multiple DVR Accesses in each IPE (Deadlock prevention and auto serialization) Example Do Foo1 on Bar1 and Foo2 on Bar2, Doo Foo3 on Bar 3 and Foo1Prime on Bar1 Do Foo3 on Bar3 and Foo2Prime on Bar2. Without the IPEs you can deadlock attempting to get exlusive access to both resources in three places. The IPEs will grab them all or block untill they are all available.
in an example of 1 DVR and serial access defined I'd stick to wiring the IPE DVR output if I used a DVR.