08-12-2023 10:46 AM
@SteenSchmidt wrote:
@Taggart wrote:
@SteenSchmidt wrote:
The two clear error functions are here to explicitly show that the close will be done regardless of error input, and will never output an error.
Why shouldn't Close output an error? What if the closing failed? Templating can lead to these kinds of oversights.
🙂
As to close outputting an error, ok great, but what do I do with that?
As with all other errors, you either:
1) Fix it yourself and don't tell anybody.
2) Output the error and let your caller decide what to do. You do not know who your caller is and therefore know nothing about their requirements or their capabilities to fix stuff.
In this particular case there could be different errors out of Close, what you do with them would depend on the specific error:
- Refnum was invalid. You probably should tell your caller that they invalidated your refnum while you used it.
- Refnum was already closed. Perhaps your job is already done and everybody is happy, but that depends on what happened upstream: If you received a valid refnum perhaps now is the time to tell your caller that you experienced a runtime error so they know their software has problems. It's not great to have refnums close themselves at random times.
- It could be an upstream error, so close performed fine but something before that may need your attention, some of that fixable by you other things need to be told to your caller.
Its been my experience that in LabVIEW, most (there may be some exceptions) close reference functions only generate an error when the reference is invalid, and all they typically do is close the actual reference. They generally don't have any side effects. If I'm trying to close a reference and someone else already has - is that a problem? In almost every case I've encountered the answer is no. Since it is generally not a problem and there is no action to take, what is the point of capturing that error?
Ok so maybe some other piece of code closed the reference prematurely. fine, but capturing this error only tells you it was closed somewhere else first. It doesn't help you track down where that is happening. And closing it prematurely is only a problem if you are actually still using that refnum somewhere and If you are that should generate an error, which should get captured somewhere, so if closing it prematurely is a problem, you'll still know about it.
So in general capturing outputs of close reference vis is pointless. I'm sure there are exceptions and I'm sure someone will point one out, but I stand by my statement in 99% of cases.
08-12-2023 10:52 AM
@SteenSchmidt wrote:
@PragmaTest wrote:
Regarding a word report, I would wire error flow of the save as function normally, but would not wire error in and error out for the close function.
In the case of Close Config save is part of the close, but ok, let's focus on close only;
If it doesn't matter if it got closed or not, why do it in the first place? Remove the close then. If your counter argument is "If close failed because the refnum was already closed, THEN it doesn't matter". Well, now you're already looking at the different errors coming out of close, and deciding which error cases to treat in which way. Exactly as I say.
Yes you just pointed out an exception to what I wrote just now in that the close config does have a write to file input. I think that is horrible design choice. Those 2 functions should not be combined at all. There should be a write changes to file function and a close function and the close function should simply release the memory. If the close function fails there is no point in capturing that error - again it is not actionable.
08-12-2023 11:24 AM
@Taggart wrote:Its been my experience that in LabVIEW, most (there may be some exceptions) close reference functions only generate an error when the reference is invalid, and all they typically do is close the actual reference. They generally don't have any side effects. If I'm trying to close a reference and someone else already has - is that a problem? In almost every case I've encountered the answer is no. Since it is generally not a problem and there is no action to take, what is the point of capturing that error?
"In general" is the problem. Our software should work all the time, within the limits of its design contract. When you call a function such as Close Config Data its design contract is its public API. The errors it can output is part of its public API for the reasons we discuss here: If a function's possible error output changes, the assumptions made by the function's users (you as caller of Close Config Data) will have new output which you couldn't have tested for or known prior. Therefore changes in your error output are breaking changes of your public API (I've touched on this several times elsewhere over the years).
So, if Close Config Data informed you of its complete range of possible error output, fine, you can choose to ignore all those (because you trust that info and have considered it) and only reconsider if you later choose to step Close Config Data to a different major version (now it has breaking changes, possibly new error codes it can output).
In lack of clear error out definition, it's dangerous to make assumptions. You are an avid TDD advocat, I guess you can follow me here 🙂 Therefore, the least you should do is inspect the error out: Ignore if it's "Refnum already closed" (which will probably be 100% of the time, no harm done), but throw a "General runtime error: original error code %d" to inform your caller of an unexpected situation. If the latter ever happens, you can patch your code with support for this newly encountered error case from Close Config Data.
If you don't cover 100% or the error cases, perhaps just with a catch-all, then your code would occassionally fail silently. Now we're talking about Close Config Data, which is probably quite safe: It's old and has had many miles on it. It's very well known. But it's the general templating I'm objecting against: If people tend to do this with all Closes, you get blindsided sometimes by a Close, or a Release, that can actually tell you that it couldn't release the resource. These are failures that hang applications, leak memory, expose vulnerabilities, and in general are darn hard to debug when silent.
My objection began with: Please don't make these kinds of "I always wrap close in a silence-wrapper". Damn no, be very aware and judgemental when programming please 🙂
08-12-2023 11:44 AM - edited 08-12-2023 11:46 AM
Maybe Darren or someone will correct me if I am wrong, but my understanding is that all the following LabVIEW primitives only return an error if the refnum is invalid.
Destroy Queue
Destroy Notifier
Close Refnum
Unregister for Events
Destroy Events Refnum
I don't have LabVIEW in front of me, so I might have got the names slightly wrong.
Every single one of these is not worth capturing the error out. It's not actionable and doesn't give you any useful information. It's just noise.
Granted, you do have a point in that if NI does decide to change that behavior in the future it could definitely cause problems.
08-12-2023 12:03 PM
@Taggart wrote:
@SteenSchmidt wrote:
@Taggart wrote:
@SteenSchmidt wrote:
The two clear error functions are here to explicitly show that the close will be done regardless of error input, and will never output an error.
Why shouldn't Close output an error? What if the closing failed? Templating can lead to these kinds of oversights.
🙂
As to close outputting an error, ok great, but what do I do with that?
As with all other errors, you either:
1) Fix it yourself and don't tell anybody.
2) Output the error and let your caller decide what to do. You do not know who your caller is and therefore know nothing about their requirements or their capabilities to fix stuff.
In this particular case there could be different errors out of Close, what you do with them would depend on the specific error:
- Refnum was invalid. You probably should tell your caller that they invalidated your refnum while you used it.
- Refnum was already closed. Perhaps your job is already done and everybody is happy, but that depends on what happened upstream: If you received a valid refnum perhaps now is the time to tell your caller that you experienced a runtime error so they know their software has problems. It's not great to have refnums close themselves at random times.
- It could be an upstream error, so close performed fine but something before that may need your attention, some of that fixable by you other things need to be told to your caller.
Its been my experience that in LabVIEW, most (there may be some exceptions) close reference functions only generate an error when the reference is invalid, and all they typically do is close the actual reference. They generally don't have any side effects. If I'm trying to close a reference and someone else already has - is that a problem? In almost every case I've encountered the answer is no. Since it is generally not a problem and there is no action to take, what is the point of capturing that error?
Ok so maybe some other piece of code closed the reference prematurely. fine, but capturing this error only tells you it was closed somewhere else first. It doesn't help you track down where that is happening. And closing it prematurely is only a problem if you are actually still using that refnum somewhere and If you are that should generate an error, which should get captured somewhere, so if closing it prematurely is a problem, you'll still know about it.
So in general capturing outputs of close reference vis is pointless. I'm sure there are exceptions and I'm sure someone will point one out, but I stand by my statement in 99% of cases.
I'll risk straying off topic to present my thoughts.
Simply don't expose the reference when you can easily hide it! Then development can proceed without any debugging of closed resources from the wrong location.
How do I handle this? Ben's famous Action Engine Nugget is always a good read
Still the post with the most kudos outside the Idea Exchange.
We can review the thread and look at the later suggestions about Wrapping up the AE. When we take that to the next logical step we:
In essence the lvlib(p) becomes a resource driver where a "Resource" might be a "Solution project or project template" implementation of a DAQmx Task, File of any type, VISA/IVI I/O Session, GUI Controller, Data Value, Named Synchronization references (Queues, Notifiers User Events...)etc... ad infinitum.
You'll see a few of those lvlibs in my code.
08-12-2023 01:07 PM - edited 08-12-2023 01:08 PM
@Taggart wrote:
[...] Every single one of these is not worth capturing the error out. It's not actionable and doesn't give you any useful information. It's just noise.
If there is no good reason within your own code that the refnum should suddenly go invalid, I definetely don't think it insignificant that a refnum you use became invalid. It would be a major concern for me that someone closed a reference they had handed me. Something is wrong somewhere within this application then, I'll let them know about breach of contract.
Why do you think 'Close Reference' can output at least 4 different error codes for refnum invalidity? If you want a function to do 'Close IFF open', then by all means use the ordinary Close Reference and suppress those 4 error codes. Else, if you expect that reference to be live, it better be live when you close it. Code smells.
Now, you have considered 'Close Config Data' (and the others you mention), so there you have made a conscious choice. That's perfect, but it's way different from "I always silence Close functions", which is what I objected against. I guarantee you that it would bring users into trouble if they always ignored errors out of my Close/Release/Destroy VIs. From what I read here some may do just that.
08-12-2023 05:59 PM
@JÞB wrote:
I'll risk straying off topic to present my thoughts.
Simply don't expose the reference when you can easily hide it! Then development can proceed without any debugging of closed resources from the wrong location.
How do I handle this? Ben's famous Action Engine Nugget is always a good read
Still the post with the most kudos outside the Idea Exchange.
We can review the thread and look at the later suggestions about Wrapping up the AE. When we take that to the next logical step we:
- create a LVLIB with the Core AE.vi and the Action/Method.ctl type def marked Private.
- Create the Public API
- Add Library Documentation, Example Code and Unit Tests as desired by requirements to the lvlib development lvproj to show how to use the API and track bugs, regression tests and all that happy stuff.
- Optional, pack the lvlib into a lvlibp
In essence the lvlib(p) becomes a resource driver where a "Resource" might be a "Solution project or project template" implementation of a DAQmx Task, File of any type, VISA/IVI I/O Session, GUI Controller, Data Value, Named Synchronization references (Queues, Notifiers User Events...)etc... ad infinitum.
You'll see a few of those lvlibs in my code.
You hit on a great point in that reference lifetime and exposure and managing it very careful, is something that often doesn't get paid enough attention to.
08-13-2023 03:05 AM
What about Close File and Close VISA ?
08-13-2023 09:37 AM - edited 08-13-2023 09:40 AM
@SteenSchmidt wrote:
Why do you think 'Close Reference' can output at least 4 different error codes for refnum invalidity?
hmm... didn't realize it gave off 4 different error codes. Will have to look into that. As I mentioned before my understanding was the only time that fails is when the refnum is invalid (ie it was never valid to begin with or something else closed it.)
08-13-2023 09:47 AM
@PragmaTest wrote:
What about Close File and Close VISA ?
Don't know about either of those. The file one I would be slightly careful with (and maybe VISA in the same way) in that closing it may actually do some flushing of buffers and actually writing to the file. Some of that may depend on OS settings. My understanding of file writing (which could very much be flawed) is that when you write to the file, it doesn't immediately get written to disk, but gets put into a buffer which then gets periodically flushed to disk. This buffer is also flushed when closing. I think it is some kind of OS performance thing. IIRC Network streams do something similar with buffering. One solution is to decouple those and manually flush the buffer when needed and specifically before you close it