Jump to content

Trying to understand reentrant


Recommended Posts

QUOTE(TG @ Jul 31 2007, 11:43 AM)

Correct. Data stored in a static (non reentrant) VI is static.

The VI has a single existence and as such there is an implied semaphore mechanism in static VI's in that only one call at a time can be made to it.

Correct except it is based on what you drop on the block diagram. I believe the instances already exist before run time.

However if you use VI server to spawn the VI using reentrant option then I (think) a clone is created.

Excuse me as I make up my own jargon. I'm trying to learn the language of picture programming.

OK so non-reentrant = static (and is easier to type)

Is the VI's single existence in the context of the calling VI, or the top panel and all of it's children?

And the spawn term is is more appropriately used in the context of the VI server?

Link to comment

QUOTE(HChandler @ Jul 31 2007, 02:23 PM)

Excuse me as I make up my own jargon. I'm trying to learn the language of picture programming.

And excuse me because I've been using LV for so long that I've forgotten the jargon of text-based programming.

QUOTE

OK so lets say I have a sub-vi that is used to poll multiple devices. Because of the peculiarities of bus arbitration, there are long latencies in accessing these devices. What if it is desired that several devices be queried in parallel using the same vi. Say my vi has has an input control node to direct inquiries to different buses and/or devices. Do they still have to wait for completion? Can this behavior be changed either modally or programmatically?

A lot of this has to do with how the VIs are written and what type of interface you are talking about. Instrument drivers provide an example. Usually instrument driver VIs are not reentrant, but that doesn't mean you can't communicate with several of the same instruments at the same time. Look at the structure of the 34401 instrument driver that ships with LabVIEW. Instrument drivers are designed to be very modular but at the very bottom level are VISA Reads and VISA Writes. These bottom-level API functions can be set to be asynchronous or synchronous, but this is a feature of the underlying driver-level code in the VISA DLL. Most of the lower-level VISA functions are meant to do their operation and return immediately rather than sit and wait indefinitely. So a higher-level program that calls multiple 34401s won't spend much time in any of the subVIs or functions and it is rare that these calls collide with each other.

The only time reentrancy really helps is when you are storing data in uninitialized shift registers and you need to access multiple calls to that subVI in parallel.

Describe your application in more detail. What kind of devices are you controlling? What bus? What is considered a long latency? You can use things like occurances, notifiers, user events, dynamic events, etc. to signal and otherwise trigger something to happen as opposed to sitting in a particular subVI polling or otherwise waiting.

Link to comment

QUOTE(xtaldaz @ Jul 31 2007, 04:18 PM)

... The only time reentrancy really helps is when you are storing data in uninitialized shift registers and you need to access multiple calls to that subVI in parallel ...

You're going to confuse all of us :o . Reentrantcy is helpful in many instances, namely any time you have an operation/subvi that is used in multiple locations and/or in parallel data paths. Let's say for example you are performing the same operation on each bit of 16-bit data. Your operation will be in one vi that you make reentrant. This vi is placed on your block diagram and copied, i.e. cloned in 16 locations. 16 clones of the reentrant vi will be in 16 memory locations.

The most common reason to make sure a VI is NEVER reentrant is in a functional global where you store data in uninitialized shift registers.

Link to comment

QUOTE(xtaldaz @ Jul 31 2007, 03:18 PM)

Describe your application in more detail. What kind of devices are you controlling? What bus? What is considered a long latency? You can use things like occurances, notifiers, user events, dynamic events, etc. to signal and otherwise trigger something to happen as opposed to sitting in a particular subVI polling or otherwise waiting.

The devices I'm dealing with are really funky serial based devices used for years (and still unfortunately used) by the folks who measure how high the water is in a stream. Admittedly, this is low bandwidth data, but ... anyway.

They use a 1200 baud serial based bus architecture called SDI-12. Each device has a unique address 0-10. You typically issue a measure command !M, the device responds either with the data, or with a time that it will take to send back the data. You have to wait for the data to be ready, and then issue a !D (acquire data) command. This can take up to several seconds to complete. Typically, I use a serial (as in RS232) device to interface an SDI-12 bus. For the typical application of these devices, all of this waiting is not a problem since there are not many of them and all that happens is that the data gets logged.

So at the VISA level, I'm talking serial out a com port into a device that transfers my commands through the bus to addressable devices on the SDI-12 bus.

I'm wanting to test a bunch of these in a lab setting, and also use the data to control certain lab processes. I have devices on multiple sets of these buses, and I've written a set of utility functions to talk to these devices. I would like to make it so that for all devices on the same bus, the different requests for data are handled sequentially FIFO so that there are no collisions. However, I would like to use the same set of routines to access devices on other busses and where possible acquire data from these devices in separate threads, asynchronously (at the bus level).

I'm also trying to understand the the persistence of objects and how object (data and program) space is partitioned, how to create and free instances of objects, the scoping of objects, thread and memory management, etc.

Link to comment

Lets see if I can spin this for you.

Re-entrancy has its plus and minuses as implemented in LabVIEW.

To allow a VI to be reentrant, LV will make unique copies of the VI for each occurence on diagrams. These unique copies have (aside from being clones) nothing to do with each other. They have their own code and data. This allows them to execute at the same time. But, you duplicate all of the data space as well. The means you can "leave" information laying around for one of the other calls to pick-up.

So if your VI does not have any internal storage like shift registers and they operate on independent widgets then making them re-rentrant can keep the VI call from one thread interfering with another call in another thread.

If you are using less than LV8 troubleshooting was tricky because you could not step in and watch in execution highlighting.

As mentioned earlier FG's are of limted use, but if you want to do something like keep a running average of acquired data, and you can ensure the incoming data will always be passed to the same instance (on the diagram) of a re-entrant VI can be used to maintain the history for that channel. So the local storage in each instance of the "Running average" VI is still useful but not shared.

Opening a reference to a VI allows you to specify how you going to open the VI. I beieve options = 8 prepares the VI for re-entrant use. Each time you open a ref as re-entrant a new copy is loaded (watch hiarchy screen, do ctrl-a to refresh).

Entrant VI can be called by re-entrant etc.

If you want to open a re-entrant VI to control one your widgets that explicitly call a entrant VI to check for channel assignments and then cache the device ID (in that VI's data space) which is latter used to call a re-entrant that queries your widget....

That should work fine. Just think about were your data is.

Done spinning.

Ben

Link to comment

QUOTE(PaulG. @ Jul 31 2007, 04:48 PM)

I disagree. I've written LOTS of LV code (instrument drivers, APIs, internal dialogs and tools, and now full-blown control apps) and very rarely needed to make anything reentrant. Maybe it's a personal LV style thing, but I think subVI modularity and use of the synchronization tools are easier to understand and maintain.

QUOTE(dsaunders @ Jul 31 2007, 05:28 PM)

Look in the wiki entry of
for some more discussion on reentrancy uses. Feel free to fill it out a bit more, as it is a bit sparse in that section. Sharing data across several VIs would require using the VI Server method.

Yes, these are great sources of information. In the sake of brevity, I eliminated the second practical use of reentrancy because the discussion was about device communication and mentioning recursion would open up a whole other can of worms.

HChandler, back to your device VIs - Have you tested your VIs in a practical setting? I'm asking because LV is by nature multithreaded and the VISA interface is asynchronous by default. Since these details are already handled, you don't need to change that default behavior. I do similar applications where I'm communicating with a couple dozen RS485 temp controllers and I've never had any problems with timing, collisions, etc. No need for reentrant VIs, functional globals, or anything other than the standard defaults.

Link to comment

QUOTE(xtaldaz @ Jul 31 2007, 04:27 PM)

HChandler, back to your device VIs - Have you tested your VIs in a practical setting? I'm asking because LV is by nature multithreaded and the VISA interface is asynchronous by default. Since these details are already handled, you don't need to change that default behavior. I do similar applications where I'm communicating with a couple dozen RS485 temp controllers and I've never had any problems with timing, collisions, etc. No need for reentrant VIs, functional globals, or anything other than the standard defaults.

I have to agree with this. I can understand how you could easily get into "analysis paralysis" as a new LabVIEW user but as xtaldaz mentioned, the defaults should work out of the box. To assure FIFO response, create a single point of entry (with associated sub-vi's) for all your communications functionality and make the VI non-reentrant. Call the VI to perform the communication whenever you require it.

Link to comment

QUOTE(Michael_Aivaliotis @ Aug 1 2007, 04:10 AM)

Actually, the application has been working fine for some time. After a few false starts I finally ended up doing what you suggest. I really only used the example because I was familiar with it, I am trying to understand what goes on behind the pictures, and how I might be able to improve on what I've got.

I appreciate the tip of watching how the hierarchy grows in response to programming and option choices. I'm still not sure I got all of my questions answered. I did find a website that looks like it might answer many of my questions though

http://www.ni.com/labview/power.htm''>http://www.ni.com/labview/power.htm' target="_blank">http://www.ni.com/labview/power.htm

Here's a another question that comes to mind. There are a great many properties and options that one might set (such as the reentrancy prop.) using the LV development environment (I'm inventing lingo again). This level of functionally can easily get lost in the woods as it were- hidden from the programmer behind the picture. Is it better code practice to not use the development system to set the properties, but explicitly do so in code (pictures?)? This sort of thing might become messy on the page but at least it's out there where you can see it. Just a thought.....

Thanks

Link to comment

QUOTE(PaulG. @ Jul 31 2007, 03:48 PM)

The most common reason to make sure a VI is NEVER reentrant is in a functional global where you store data in uninitialized shift registers.

At the risk of confusing everyone, I do have many functional globals that are reentrant. That's how I can create separate databases -- the core VI is reentrant and contains the uninitalized shift registers. Then I create a series of wrapper VIs that are not reentrant that will be my entry points. Each wrapper VI is thus a single shift register database.

Link to comment

QUOTE(Aristos Queue @ Aug 1 2007, 06:00 PM)

At the risk of confusing everyone, I do have many functional globals that are reentrant. That's how I can create separate databases -- the core VI is reentrant and contains the uninitalized shift registers. Then I create a series of wrapper VIs that are not reentrant that will be my entry points. Each wrapper VI is thus a single shift register database.

I create dynamically references to reentrant VIs that use functional globals. Each reference refers to separate instance of the reentrant VI then and each instance has its own functional globals to store the state of the instance.

Link to comment

QUOTE(PaulG. @ Aug 1 2007, 06:48 AM)

:thumbup: True for single-layer functional globals.

QUOTE(Aristos Queue @ Aug 2 2007, 01:00 AM)

At the risk of confusing everyone, I do have many functional globals that are reentrant. That's how I can create separate databases -- the core VI is reentrant and contains the uninitalized shift registers.

Right - that's a neat implimentation of an object repository functional global, not a single-layer functional global, which is what I think the original thread (and PaulG.'s post was getting at).

QUOTE(dsaunders @ Aug 2 2007, 01:29 AM)

http://wiki.lavag.org/Functional_global_variable'
target="_blank">Functional Global Variable

wiki?

Yeah - I think that this thread has certainly highlighted that our wiki page needs expansion. That said, next week is NI-Week - so who's got the time at the moment to do it? :)

Link to comment
  • 2 months later...

QUOTE(Aristos Queue @ Aug 1 2007, 03:00 PM)

QUOTE(TG @ Oct 14 2007, 10:25 PM)

I'm totally confused now but quite entertained
:)

Please riddle me

Opps I guess I better keep reading first ...

QUOTE

QUOTE(Aristos Queue @ Aug 2 2007, 01:00 AM) At the risk of confusing everyone, I do have many functional globals that are reentrant. That's how I can create separate databases -- the core VI is reentrant and contains the uninitalized shift registers.

QUOTE

QUOTE crelf Posted Aug 1 2007, 07:19 PM Right - that's a neat implimentation of an object repository functional global, not a single-layer functional global, which is what I think the original thread (and PaulG.'s post was getting at).

Just to let you guys know this is good stuff from where Im sitting..

I'm gonna use that technique for myself. These little tricks are quite impressive. I guess it would be great to somehow catagorize all this stuff

in Wiki but how? :unsure:

Thanks anyway ;)

Kudos LAVA!

Link to comment

I said that I will have a core functional global that is reentrant and then a wrapper VI that is not reentrant. Thus each wrapper VI instantiates the reentrant VI once, and thus I have multiple globals to work with. Tomi mentioned that he just uses VI Server to instantiate another reentrant clone, rather than statically allocating, as is done in my scheme.

The reason that I have used my scheme, from time to time, is that the core functional global is frequently written in terms of some generic datatype (either LabVIEW Object, LV Variant or flattened strings). The outer wrapper VI is then my type-specific wrapper. So the outer VI may take a double as an input, translate it into my generic type, pass it into the functional global, and then translate the generic back into the double. It saves me from having to do the type conversion code at every call into my generic functional global.

I also hate debugging reentrant clones that don't come from a specific subVI -- it's hard as nails to get to the diagram for a dynamically created clone VI and set a breakpoint before that VI executes. With static instantiation in non-reentrant wrapper VIs, I can find the specific clone I'm looking for much quicker.

(PaulG, don't read this next bit until you've digested the foregoing because it may really confuse you.)

Of course, my wrapper idea can be combined with Tomi's idea... have the generic functional global core be reentrant. Have the type-specific wrappers also be reentrant. Then use VI Server to instantiate type-specific instances whenever you need them.

The repetition can get dizzying: wrappers around meta-layers around pointers to pointers, recursing ad infinitem...

Long ago a computer scientist said, "Most problems of computer science can be solved by adding another layer of indirection." It may be true, but it can be hard to grasp all the layers at once.

Link to comment
  • 1 month later...

I'm a little late to the party on this one, but I found this to be an enlightening thread. Great stuff by all. I agree that clones are a pain, but I love them LV2 style globals. Attached is my demo of a classic LV2-style global VI, and it's behaviour if when reentrant and not. RichardQUOTE(BrokenArrow @ Nov 29 2007, 05:42 PM)

I'm a little late to the party on this one, but I found this to be an enlightening thread. Great stuff by all. I agree that clones are a pain, but I love them LV2 style globals.Attached is my demo of a classic LV2-style global VI, and it's behaviour if when reentrant and not. Richard
oops. attached are sub-vi's for that demo
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.