Jump to content

Is it acceptable to use global VIs in a class if they are scoped private/community?


Recommended Posts

A topic that has never come up on LAVA or in any of the presentations/discussions I've been involved in in the last three years:

There is a general repudiation of global VIs among advanced LV programmers. Generally, globals are good until the first time you are sorely burned by them, and then you develop an allergy to them that gets stronger with time, especially as you discover techniques that give you other ways to share data in your programs.

The argument against globals is

  1. They allow any random VI any where in your system to change values, making it hard to write stable, proveably-correct code.
  2. They always create a data copy on read.
  3. They are subject to the read-modify-write race condition if used in two places in your code in parallel.

But the argument from the other side is that

  1. global VIs are so easy to write compared to [insert alternate mechanism here]
  2. You can search and find all uses of a global VI, which you cannot do with Data Value Reference (DVR) and single-element queues (1EQ). You can search for all uses of LV2-style globals (LV2), but most of the time, those are written with a simple get/set, which means they have the same data copy and read-modify-write danger. So why not just use a global VI?

The argument is one for the ages.

But now we have LV classes, or any library, really. With a library, we can create a global VI and make it private scope. Now we know that the global VI is only used by a finite set of VIs -- those that are members of the class. We can guarantee that any read-modify-writes are either done singly or can be guarded by semaphore locks. We can combine the global VI with a DVR, so that the global VI stores the refnum of the DVR for easy lookup, and let the DVR be used to avoid the data copies.

The original presentation of LVClasses mentioned the use of global VIs as a good way to implement data common to all objects of a class (called class static data by C++/C#/Java programmers). But the question of whether this is a good recommendation has never come up. Given all of this, I now formally ask the question: should a global VI that is given private (or community, since the same finite-set-of-VI-arguments apply) scope be considered acceptable LV coding practice? Would a VI hierarchy that used globals in that way be acceptable in the LAVA Code Repository?

Link to comment

A topic that has never come up on LAVA or in any of the presentations/discussions I've been involved in in the last three years:

There is a general repudiation of global VIs among advanced LV programmers. Generally, globals are good until the first time you are sorely burned by them, and then you develop an allergy to them that gets stronger with time, especially as you discover techniques that give you other ways to share data in your programs.

The argument against globals is

  1. They allow any random VI any where in your system to change values, making it hard to write stable, proveably-correct code.
  2. They always create a data copy on read.
  3. They are subject to the read-modify-write race condition if used in two places in your code in parallel.

But the argument from the other side is that

  1. global VIs are so easy to write compared to [insert alternate mechanism here]
  2. You can search and find all uses of a global VI, which you cannot do with Data Value Reference (DVR) and single-element queues (1EQ). You can search for all uses of LV2-style globals (LV2), but most of the time, those are written with a simple get/set, which means they have the same data copy and read-modify-write danger. So why not just use a global VI?

The argument is one for the ages.

But now we have LV classes, or any library, really. With a library, we can create a global VI and make it private scope. Now we know that the global VI is only used by a finite set of VIs -- those that are members of the class. We can guarantee that any read-modify-writes are either done singly or can be guarded by semaphore locks. We can combine the global VI with a DVR, so that the global VI stores the refnum of the DVR for easy lookup, and let the DVR be used to avoid the data copies.

The original presentation of LVClasses mentioned the use of global VIs as a good way to implement data common to all objects of a class (called class static data by C++/C#/Java programmers). But the question of whether this is a good recommendation has never come up. Given all of this, I now formally ask the question: should a global VI that is given private (or community, since the same finite-set-of-VI-arguments apply) scope be considered acceptable LV coding practice? Would a VI hierarchy that used globals in that way be acceptable in the LAVA Code Repository?

Nicely stated and wow no comments yet!!! I'll take a stab as I am amongst the knee-jerk "toss my cookies"-at-sight-of-global-variable crowd. That being said, my take is we need to know options around global variables, then we can come back to the strategic use of them.

I also admit, that at first I did not like the use for the static data. But you might sell me on this one (and for those that know me here in Colorado there was just a 8.6 tremor across the state as I typed those words).

Here is my take:

1. I will look for an opportunity to implement this in the near future.

2. This implementation will be completely transparent to the end user. If well constructed could be an elegant solution. End user just want the library of methods to get the job done and done in a clean fashion.

3. If I don't like the implementation, I can always change it as long as I've forced my end user to a public interface that will allow for that change.

4. Not for the novice LVOOP developer (thinking about the coupling with DVR when I make that statement).

5. I will still recommend learning to code with out the global variable.

6. Can't wait to hear everyone else's opinion while pondering this one.

Thanks for the idea!!!!

PS - Don't tell anyone about #1. Please keep it a secret. :rolleyes:

Link to comment

A topic that has never come up on LAVA or in any of the presentations/discussions I've been involved in in the last three years:

I think I've used a global variable once in my 3 years of Labview programming. For me this is clearly a case of out-of-sight, out-of-mind. Using a global in this way never even occurred to me.

Given all of this, I now formally ask the question: should a global VI that is given private (or community, since the same finite-set-of-VI-arguments apply) scope be considered acceptable LV coding practice?

I don't see why not. I like the idea -- as long as the class implements protections to prevent the problems you outlined. Global variables also have other advantages over a SEQ and DVR:

  1. No Init method required! thumbup1.gif
  2. Queue names, I think, exist in a global namespace. Though unlikely, it is possible two different code modules could attempt to use the same named queue. The global VI exists in the library namespace, so (I assume) you don't have to worry about stepping on another global with the same name.

On the flip side, I can build GlobalLock and GlobalUnlock commands into a functional global to prevent a parallel write while I'm doing my read-modify-write. This gives the class developer an easy way to prevent race conditions. I don't think there's a way to do that with globals.* You could write GlobalRead, GlobalWrite, GlobalLock, and GlobalUnlock VIs, but you have no way to enforce their use within the class. The class developer can still use semaphores to prevent the class user from encountering race conditions. It's just harder to verify there are no race conditions possible in the class code.

(*I suppose you could create a GlobalBase class with Lock and Unlock methods that set an internal flag. For each new global variable you would derive a new child class and implement ReadGlobal and WriteGlobal static methods. Once you've done all that you've essentially made a more complicated functional global, so I don't think there's any point in doing this...)

I think if the class/library is small enough for a single developer to fully understand and verify race conditions can't exist, globals are a very good option. As the code gets larger and more complex, manually verifying race conditions can't exist becomes harder and the argument for one of the more accepted techniques become stronger.

Link to comment

Here's my two cents:

[sidenote]

Its funny, I was thinking about possible ways to explain benifits of LVOOP techniques on the way into work this morning (for internal training) and one of the things I was coming up with was explaining the drawbacks of functional global VIs (aka Action Engines), with particular reference to maintainability (ie what happens when you need a second "thing")... In that case the answer to me seams clear - functional globals may be easy to implement and provide a clear bottle neck for debugging and testing but can cause all kinds of maintenance nightmares, such as the example I just mentioned, or even remembering which inputs should be connected for a given action (although this can be mitigated by creating "wrapper" VIs, but by that point the only thing separating your action engine and a class is that you data is on an uninitialized shift register instead of a class wire).

[/sidenote]

Now a global within a class I'm more torn over... I personally think that a functional global VI could be a valid technique for static class data (say, for example, a settings/state repository or whatever) but that said, there are probably better ways of achieving that same functionality, for example, an aggregation class. I guess that even with classes one still needs to think carefully before making any data element global (and if it can be avoided, all the better), however a private/community global at least has the advantage of being able to limit the global's scope and add the necessary protection.

One example where I did use a global within a class was to keep track of serial ports in use - we had a "motor framework" that allowed the system to use motors from different manufacturers over a variety of different interfaces. What we wanted to do was make sure that only one kind of motor driver could use a serial port at a given time but if that driver supported daisy-chaining then there could be multiple instances of that class sharing the port. The solution was for the Motor Framework class to have a registry global, keeping track of what motors were initialized (with port names, device IDs, and driver names), that way when a motor class attempts to initialize it has to request a port from the motor's global (through a properly typed accessor VI).

Shaun

Link to comment

A topic that has never come up on LAVA or in any of the presentations/discussions I've been involved in in the last three years:

...But the argument from the other side is that

  1. global VIs are so easy to write compared to [insert alternate mechanism here]
  2. You can search and find all uses of a global VI, which you cannot do with Data Value Reference (DVR) and single-element queues (1EQ). You can search for all uses of LV2-style globals (LV2), but most of the time, those are written with a simple get/set, which means they have the same data copy and read-modify-write danger. So why not just use a global VI?

...

1) Easy is not better.

2) A simple get set does have the same trouble as globals, yes. This is why I recomend AEs since their structure brings with them implied resource locking. I would not use a simple get-set LV2 for the same reason i would not use a global.

Add to your list of reasons to not use a global "Global use can thow off determinism!" See this thread.

http://forums.ni.com...75255&jump=true

where I swore off the globals once and for all.

Regarding when it would be appropriate to use globals in LVOOP... Q

Wouldn't a global restrict the proper operation of a class to single instance? If I wanted two instances of that class, isn't there a possiblity that the two instance could be hitting the same global in differnet ways?

Ben

Link to comment

Whoops, I guess I did not read the opening statement very carefully... Global VI's not Globals.

Well I do still need to clean up access to my globals anyway!

When we are talking about globals are we referring to old style 'free' globals or project based shared variables?

Shared variables are always part of a project and thus have access scope protection which is helpful.

I use NSV's alot (I have 100's of them in my cRIO application) mostly are used to to bind IOV's upward to the PC HMI.

But I also use them for interprocess signaling and as command response FIFO's.

I suppose I should do a better job of encapsulating their access in classes, thanks alot for giving

me more work to do :)

Link to comment

Wouldn't a global restrict the proper operation of a class to single instance? If I wanted two instances of that class, isn't there a possiblity that the two instance could be hitting the same global in differnet ways?

Ben

I think this is the intended behavior of a class static member - all objects instantiated from the class share the same data space for the static member. This is useful in some design patterns like keeping track of the number of instances which might be useful if you're trying to manage a limited number of physical resources. The constructor increments the static counter and the destructor decrements it.

Also, I use a single element named queue for this type of behavior. The data is in a cluster and the queue name is the class name. If any member of the class needs to read or write the static member they get a queue ref (using the name) and then read or write as required. The SEQ forces mutex-like behavior since the one must intentionally flush the queue before a write so any VI that gets a ref at this point is blocked until the writer is finished and vice versa - once a read operation pops the element everyone else waits until the queue is repopulated. The only problem with this is that VIs outside the scope of the class can "steal" the data by using the class name and getting a reference so protecting the data as private is problematic but unless a developer really wants to "break the rules" this works.

Mark

Link to comment

Wouldn't a global restrict the proper operation of a class to single instance? If I wanted two instances of that class, isn't there a possiblity that the two instance could be hitting the same global in differnet ways?

Ben

I think the point is that each instance of the class would have access to that one "class" or "static" variable. Endevo has a "class attribute" which is scoped to all instances of the class. And I think that is the goal here as well.

Link to comment

I think the point is that each instance of the class would have access to that one "class" or "static" variable. Endevo has a "class attribute" which is scoped to all instances of the class. And I think that is the goal here as well.

If my ears are working right this is the first time I have had a chance to speak with you. Nice to meet you and thanks for the reply.

So these globals would be read-only? i can see how the single-element qeue has built-in protection but if I am crating two instances of the class at teh same time, SOMEBODY has to write to them and who will win with protecting the global with semaphores?

Ben

Link to comment

The only problem with this is that VIs outside the scope of the class can "steal" the data by using the class name and getting a reference so protecting the data as private is problematic but unless a developer really wants to "break the rules" this works.

Would a SEQ whose datatype is a private cluster effectively block external code from gaining a reference to the Q?

Link to comment

Would a SEQ whose datatype is a private cluster effectively block external code from gaining a reference to the Q?

I haven't tried that - it would certainly make it much more difficult if the user has to re-create his own datatype that matches the private type to get a valid queue ref.

Mark

Link to comment

I am one of those people who do use globals occasionally, so I think that classes only make the use cases where they're legitimate even more legitimate.

Before I continue, let me say that I am perfectly aware of the issues with globals. If you need to write in multiple places, you have no way of preventing race conditions short of using some kind of locking mechanism like a semaphore, which is still problematic from a dataflow perspective and can cause deadlocks. So no globals in that case. I'm not really worried about the copy issue, since I use globals for small things.

The most legitimate use case is a case where you have a single writer and multiple readers. You could use a notifier with previewing for this (and I would if I need to generate N copies), but if you only need one copy, a global is the easiest method.

I wouldn't use a global for something like ref-counting (an LV2 glob is better suited for that due to the race conditions issue), but I would be happy to use read-only globals as constants. I even posted an idea to that effect here.

The only problem with this is that VIs outside the scope of the class can "steal" the data by using the class name

The solution to that is to make the queue unnamed and put its reference into the class data cluster. An equivalent in 2009 is the references to data feature, which doesn't even allow you to name the reference, partly to avoid this issue.

  • Like 1
Link to comment

The solution to that is to make the queue unnamed and put its reference into the class data cluster. An equivalent in 2009 is the references to data feature, which doesn't even allow you to name the reference, partly to avoid this issue.

That would work as long as you could create all instances at the same time and load all the data members from the same default (including a "live" queue ref) - I'm talking by-val classes since that is where I use this. Otherwise, you still need some globally available LabVIEW construct (like named queues or globals) so each new instance can access the class static members. Or put the queue ref in a LV2 global - but that gets kind of circular.

Mark

Link to comment

If my ears are working right this is the first time I have had a chance to speak with you. Nice to meet you and thanks for the reply.

So these globals would be read-only? i can see how the single-element qeue has built-in protection but if I am crating two instances of the class at teh same time, SOMEBODY has to write to them and who will win with protecting the global with semaphores?

Ben

Ben - it's a pleasure to meet! Hoping my schedule will permit more time here!

First, I'm thinking primarily about the basic native by-ref class (then I will extend my thoughts to wrapping that with a reference). And I must admit that I'm historically a by-Ref gal that is now digging by-Val.

Let's consider two use cases: The first does not require this class global variable, the second does.

Let's say that I have a parent class that is LogFile. The child classes could be TDMS LogFile or XML LogFile. In this case, the parent data could include the path for the directory to the files that will be logged. Yes each instance of the child would have a unique wire and each wire would contain a unique path.

Now let's change the use case. Let's assume there is only the TDMS LogFile class and we have multiple instances of that class. However, there will always be one and only one path for the directory for all of the TDMS LogFile objects. And all objects will need to be able to read this information. This is the kind of data that might be stored in that static/class global variable. And the developer would simply create the method to set the value and that method would likely be called at the beginning of the application. And because the global variable is private to the class, it is protected from any spec of code, other than the associated method, being able to set it.

Let me know if you are tracking with me here or if I need to clarify further, then let's wrap with references and chat more!

Link to comment

Ben - it's a pleasure to meet! Hoping my schedule will permit more time here!

First, I'm thinking primarily about the basic native by-ref class (then I will extend my thoughts to wrapping that with a reference). And I must admit that I'm historically a by-Ref gal that is now digging by-Val.

Let's consider two use cases: The first does not require this class global variable, the second does.

Let's say that I have a parent class that is LogFile. The child classes could be TDMS LogFile or XML LogFile. In this case, the parent data could include the path for the directory to the files that will be logged. Yes each instance of the child would have a unique wire and each wire would contain a unique path.

Now let's change the use case. Let's assume there is only the TDMS LogFile class and we have multiple instances of that class. However, there will always be one and only one path for the directory for all of the TDMS LogFile objects. And all objects will need to be able to read this information. This is the kind of data that might be stored in that static/class global variable. And the developer would simply create the method to set the value and that method would likely be called at the beginning of the application. And because the global variable is private to the class, it is protected from any spec of code, other than the associated method, being able to set it.

Let me know if you are tracking with me here or if I need to clarify further, then let's wrap with references and chat more!

Have YOU been looking at my Library? (Smiley-wink)

I follow you right up until I have two seperate threads running that both try to set the method when they start. From what I read in your reply, the developer is responsible for ensuring only one entity tries to do use the Set method.

I don't see how a global will protect against this issue without introducing semaphores etc, and by the time we go there we might as well use the SEQ.

Thank you,

Ben

Link to comment
I follow you right up until I have two seperate threads running that both try to set the method when they start. From what I read in your reply, the developer is responsible for ensuring only one entity tries to do use the Set method.
First Call primitive can take care of that... just put all the code in a case structure that only executes if First Call is true. Thus even if multiple calls are made, the later calls are ignored. Does that solve the problem?
Link to comment

First Call primitive can take care of that... just put all the code in a case structure that only executes if First Call is true. Thus even if multiple calls are made, the later calls are ignored. Does that solve the problem?

From where I sit, yes, provided the developer knows that fact and uses the First Call. But a global by itself can have a problem. So returning to your orignial Q, a global by itself could be dangerous.

Now on the other hand...

If LVOOP only allowed us to use a NEW flavor of global maybe called a WORM (Write Once Read Many) Global, taht had the first call built in, I would stop pick at this idea. The other plus of the WORM Global is that I would be able stand behind Yair's age old idea of using Globals for defining constants.

Ben

Link to comment

That would work as long as you could create all instances at the same time and load all the data members from the same default (including a "live" queue ref)

You're right. I haven't considered that the data needs to be shared between all instances.

If LVOOP only allowed us to use a NEW flavor of global maybe called a WORM (Write Once Read Many) Global, taht had the first call built in, I would stop pick at this idea. The other plus of the WORM Global is that I would be able stand behind Yair's age old idea of using Globals for defining constants.

What you want aren't constants. You want variables which are written to the first time you use them. This is something I also want, but the problem is that I never figured out a good mechanism for this. Presumably, you want to get these values from a file, but you might also wish to get them from other places (DB, OS, etc.).

If you can guarantee a single write, you can actually sort of do this today fairly easily - all you need to do is open a reference to the global VI at the init phase, get a reference to all the controls and then load values into them (e.g. using the config file VIs). You can flatten and unflatten all the values into strings, or use something like the OpenG VIs or the code you wrote to produce a human-readable file. The only problem with this is that there's no way to guarantee a single write and no way to guarantee that the write will occur before all reads.

Link to comment

You're right. I haven't considered that the data needs to be shared between all instances.

What you want aren't constants. You want variables which are written to the first time you use them. This is something I also want, but the problem is that I never figured out a good mechanism for this. Presumably, you want to get these values from a file, but you might also wish to get them from other places (DB, OS, etc.).

If you can guarantee a single write, you can actually sort of do this today fairly easily - all you need to do is open a reference to the global VI at the init phase, get a reference to all the controls and then load values into them (e.g. using the config file VIs). You can flatten and unflatten all the values into strings, or use something like the OpenG VIs or the code you wrote to produce a human-readable file. The only problem with this is that there's no way to guarantee a single write and no way to guarantee that the write will occur before all reads.

Alas, a lowly WORM Global is not enough. The read of a non-written WORM would have to hang, Timeout, or return an error (a WORM with a options?). Htat is too much scope creep for me.

Ben

Link to comment

Actually, at one point I had the idea of adding a block diagram to a WORM global VI. The diagram would execute once when the VI was changed to run mode, so you could put whatever code you wanted in there. In any other VI, the globals would be read-only.

I don't remember if I ever made the suggestion publicly, but I do remember that at the time I saw a serious drawback to that idea, although I don't remember what it was at the moment. I like the idea, but I still see a few drawbacks to it (like what happens if the diagram doesn't finish running, what happens if you need some other code to run to get specific info like config file paths, etc.).

Link to comment

Just a step in from the side. Is the reverse of a 'WORM' a valid design. I confess, that I use globals as duct tape when I need to fix something 'right now'. So if I go with the WORM design, I reduce the risk of that duct tape falling off after delivery of the code. Is the same ok if I code Write Many Read Once?

To give a practical scenario: I have various abort conditions (Panel Close, Stop Button, Error) and one measurement routine that monitors the abort flag.

Ok, it is read 3 times: Init (Default value), Measurment routine and Reset (after routine stopped).

Or is there a way to make this a reusable 'Abort' classs on it's own...

Felix

Link to comment

For something like that, the easiest thing to do is to use a small action engine - the first call primitive resets the flag (a USR) to F, then it monitors the input. If the input or the SR is T, it changes the output SR to T. You then place the VI on the wire leading to the stop condition. If any of the callers inputs T into the VI, it changes the flag to T and causes all the callers to have T as the output. Clear?

This doesn't handle places where you need a notification (such as loops driven by event structures or dequeue primitives), but those also have solutions.

Link to comment
If LVOOP only allowed us to use a NEW flavor of global maybe called a WORM (Write Once Read Many) Global, taht had the first call built in, I would stop pick at this idea. The other plus of the WORM Global is that I would be able stand behind Yair's age old idea of using Globals for defining constants.
If you want write-once-read-only-thereafter, you *don't* want a global VI. My suggestion of the global VI as class static data [aka data common to all instances] is specifically for the cases where it would be modified from time to time. The list of methods of any given class should be trivially short enough to confirm that none of them is abusing the global VI, something not possible with a truly open global VI.

If you want a "set once and read only thereafter", just write an LV2-style global with the First Call primitive. That's trivially done in LV today. Here's an example:

post-5877-125555570296_thumb.png

The input terminals are Recommended, not Required.

Link to comment

I think this is the intended behavior of a class static member - all objects instantiated from the class share the same data space for the static member. This is useful in some design patterns like keeping track of the number of instances which might be useful if you're trying to manage a limited number of physical resources. The constructor increments the static counter and the destructor decrements it.

Also, I use a single element named queue for this type of behavior. The data is in a cluster and the queue name is the class name. If any member of the class needs to read or write the static member they get a queue ref (using the name) and then read or write as required. The SEQ forces mutex-like behavior since the one must intentionally flush the queue before a write so any VI that gets a ref at this point is blocked until the writer is finished and vice versa - once a read operation pops the element everyone else waits until the queue is repopulated. The only problem with this is that VIs outside the scope of the class can "steal" the data by using the class name and getting a reference so protecting the data as private is problematic but unless a developer really wants to "break the rules" this works.

Mark

You could combine this technique with a class global that defines the name of the SEQ. If would be written to by the very first instantiation of the class. the name could be in the form of the class name with a random number appended to the name. All other methods would access your global data using the SEQ and the randomly generated name to obtain your queue reference. Using this method it would be very difficult for someone outside the class to get a reference to the queue.

Link to comment

You could combine this technique with a class global that defines the name of the SEQ. If would be written to by the very first instantiation of the class. the name could be in the form of the class name with a random number appended to the name. All other methods would access your global data using the SEQ and the randomly generated name to obtain your queue reference. Using this method it would be very difficult for someone outside the class to get a reference to the queue.

Or, more simply, just get an *unnamed* ref and put that into the into the class global, rather than the name. Now any member of the class can get the SEQ refnum from the global, without having to create and manage a refnum themselves, and there is no way for anyone outside of the class to get the reference, no matter how much guessing of names that they try.
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
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.