Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Cleanup after Pre Launch Init?

Solved!
Go to solution
Solution
Accepted by topic author St3ve

I'm totally open to counterarguments and improvement suggestions. I'm just laying out my thought process behind the original.

The comparison of Pre-Launch Init.vi to a constructor is correct. But the use case you should be comparing against is not "constructor/destructor" but instead "constructor that throws an exception".  When an exception is thrown by a constructor in C++/C#/JAVA, the destructor is never invoked because the object was never properly constructed. If you acquire system resources there, you have to free them in your catch block (or they have to know how to free themselves).

With that comparison in mind, here's my argument against a shutdown counterpart for Pre-Launch Init.vi:

Pre Launch Init.vi allows you to acquire system resources before the actor launches because it is often desirable to sychronously know in the caller that the actor didn't even get started. The desire for this synchronous behavior drove the creation of Pre-Launch Init.vi, but it has the side effect of making Pre-Launch Init.vi the place where you can acquire system resources without any other actor trying to get them. That means an actor can acquire locks to its DAQ channels and files on disk and unique sequence counts and any other resources that only one actor may have at a time. There are other ways to do this (see the story below) but this is a convenient result. But if an actor tries to gather all N of the resources that it needs to run and succeeds with N-M of them and then fails M of them, it needs to release the first N-M resources back to the system. That needs to happen within the same lock. If it isn't within the same lock, you may have a failed actor take a DAQ channel and then leave the lock without returning the channel, causing a second actor also fail because it cannot get the necessary DAQ channel, when actually it was something totally unrelated that made the first actor fail.

In order to support this "release within the lock" behavior, if we were going to add a shutdown counterpart, the code for Launch Actor.vi would change such that it would call Pre-Launch Init.vi, then check the error, and ONLY ON ERROR call "On Error From Pre-Launch Init.vi". Thus the new method would still not have the symetry that you are looking for with a destructor. It would instead mimic the catch statement of an exception.

Still, we could add such a method, but I don't think it makes sense to have two methods that are always called back-to-back like that. Instead, just having the one method with the documentation that says, "If you're leaving here with an error, make sure you have returned any partially checked-out resources" makes more sense to me. That's especially true since in that other method, you might not know which resources you had successfully acquired and which you hadn't, leading to less performant code when unwinding. And although performance isn't usually a concern in an error case, it can be when you're holding a global lock like Launch Actor.vi.

Slightly horrific (to a software architect) story relevant to this: The other day, I was contemplating an actor design. I had this nice synchronous bottleneck during Launch Actor.vi and I found myself wanting a similar bottleneck during shutdown. Essentially, I was going to be giving back multiple resources to the system, and I didn't want my actor to give back one resource, have another actor snap it up, and then that other actor fail because my first actor wasn't fast enough releasing the second resource. Although I found a better option eventually, I did have this thought pass through my mind, "I could create a new actor class called The Gives-Back Resources Actor, which I stuff with a bunch of resources before I launch it. In its Pre-Launch Init.vi, it will release the resources and then it will ALWAYS return an error!" In other words, I would have an actor that never ever launches, just so I could use the Pre-Launch Init.vi bottleneck.

I was so excited to find a solution to my problem that I was already creating the new class when it occurred to me that I could just create a non-reentrant subVI to acquire and release the resources and just call that in both Pre-Launch Init.vi and in Stop Core.vi. You know, the *right* solution that most LV programmers would have leapt on in the first place. Even a global Semaphore would have been less of a hack! 😉

Message 11 of 13
(1,104 Views)

St3ve wrote:

Additionaly, as a LabVIEW programmer I am familiar with APIs that follow a  Configure, Operate, Cleanup (which runs on error) paradigm. As such, I still think that an addition of a cleanup VI as a mirror of <Pre Launch Init> would be of benefit.

With "Configure, Operate, Cleanup", you're talking about API that the user of the process sees.

Any initialization VIs (property write VIs for example) that you call before you call Launch Actor.vi is the analog of Configure.

Launch Actor.vi is the analog of Operate.

Send Normal/Emergency Stop.vi is the analog to Cleanup.

But in the "Configure, Operate, Cleanup" sequence, you really don't have an analogue to "Cleanup if Configure Failed". The big difference is that we're talking in this thread about an *internal* API of the operating process, rather than part of its public, callable API. There are many parts that aren't usually seen by a user of the API. And Pre-Launch Init. lies within that shadow.

0 Kudos
Message 12 of 13
(1,104 Views)

Thanks for taking the time to write such a detailed reply; I now understand your design intention with <Pre Launch Init> and to be honest I hadn't fully appreciated the synchronous behaviour when launching an Actor. I agree that the "release within the lock" behaviour is desirable and my suggestion would certainly break that!

I had thought about essentially putting a call to <Stop> in an error case structure at the end of <Pre Launch Init>. However, this felt a bit kludgy which is what lead to my request for a destructor after the loop that was not conditional on its execution. I will now go back to that approach!

AristosQueue wrote:

I was so excited to find a solution to my problem that I was already creating the new class when it occurred to me that I could just create a non-reentrant subVI to acquire and release the resources and just call that in both Pre-Launch Init.vi and in Stop Core.vi. You know, the *right* solution that most LV programmers would have leapt on in the first place. Even a global Semaphore would have been less of a hack! 😉

I think this is a good example of the well known problem that everything looks like a nail when you have a hammer, especially a shiny new one!!

0 Kudos
Message 13 of 13
(1,104 Views)