Analytica Decision Engine Reference/Release ADE Objects BEFORE deleting Analytica objects
Important programming Note
When using ADE, it is critical that your code releases any ADE objects before Analytica objects used by your code are deleted. Failure to do so can result in a crash, and often the crash occurs much later so that it is hard to associate the crash with the original source of the problem.
Analytica objects may be deleted by
- Closing your model, CAEngine::CloseModel
- Deleting the object, via CAEngine::DeleteObject
- Deleting a module object that contains the object, via CAEngine::Delete
- Using a typescript command, such as delete, to delete the object.
It is fairly obviously which object CAObject, CAIndex instances point to. These should be released BEFORE deleting the underlying object. CATables also point to an underlying object, the result or definition object, but also to index objects. If you delete any index object that belongs to the table, you need to release the CATable instance first!!
The following code demonstrates (non-.NET):
Good code:
CAObject obj = ade.Get("Va1") ... obj = Nothing ade.DeleteObject("Va1") obj = ade.Get("Va2") ...
Bad code
CAObject obj = ade.Get("Va1") ... ade.DeleteObject("Va1") obj = ade.Get("Va2") ...
Good .NET code
CAObject obj = ade.Get("Va1") ... System.Runtime.InteropServices.Marshal.ReleaseComObject(obj) ' In .NET, you must explicitly release obj = Nothing ade.DeleteObject("Va1") obj = ade.Get("Va2") ...
Closing models
When you load a model, operate on it with ADE for a while, odds are that you'll have several ADE objects instantiated -- perhaps some CAObjects, CATables, etc. If you close that model and open another, it is very important that you release all the objects that were in use for the original model.
Here is a common mistake that can lead to a crash (much later):
CATable tab = obj.ResultTable ... ade.CloseModel( ) ade.OpenModel( "newModel.ana" ) ... tab = ade.Get(name).ResultTable ' Reuse tab
The CATable pointed to by tab is released in the last line, but this release occurs after the result object and indexes used by the CATable have ceased to exist. The correct way is to release tab before closing the first model, and set tab to Nothing, e.g.:
CATable tab = obj.ResultTable ... ReleaseComObject(obj) ' If in .NET tab = Nothing ade.CloseModel( ) ade.OpenModel( "newModel.ana" ) ... tab = ade.Get(name).ResultTable ' Reuse tab
.NET notes
The whole COM release thing is a complete pain. The reason for having to call ReleaseComObject is because the garbage collection architecture leaves the ADE object laying around until GC happens to run. Instead of worrying about calling ReleaseComObject every time, you can just ensure that you've released the pointers by setting them to null, and then force a garbage collection before closing a model or otherwise deleting objects. Code would look more like this:
CATable tab = obj.ResultTable ... tab = Nothing ... System.GC.Collect() ade.CloseModel( ) ade.OpenModel( "newModel.ana" ) ... tab = ade.Get(name).ResultTable ' Reuse tab
If you follow this practice of garbage collecting before any operation that may delete an object, your .NET code can be much cleaner, especially in C# or C++/CLR (using deterministic destruction in the latter).
In C#, when you instantiate an object such as CATable, wrap it in a using block:
Using ( CATable tab = obj.ResultTable ) { // .. Do something with tab ... }
If you delete an object, or close a model, that would occur outside the lexical scope of tab. So, the logical flow would look like this:
Using ( CATable tab = obj.ResultTable ) { // .. Do something with tab ... } ... System.GC.Collect(); ade.CloseModel( );
The sequence of things that happens here:
- tab is instantiated
- Operations inside the using occur using tab
- When the end of the Using scope is reached, the interop wrapper tab is released by .NET. At this point it is still in the managed heap, ready to be collected. But it still points to the CATable COM object, which still lives inside ADE, which still points to the Analytica objects.
- When System.GC.Collect() executes, the tab interop wrapper is garbaged collected. At this time, ReleaseComObject completes, and the the CATable is released and freed inside ADE. Now it is safe to delete objects.
- CloseModel deletes Analytica objects.
Religious use of the Using construct in C# will keep you from making an error that could result in a problem -- as long as you also remember to garbage collect prior to closing your model or deleting objects.
Note that the need to release, and techniques discussed here, are not limited to ADE. You should do the same for any COM component used in a .NET application -- especially if it is used out-of-process.
Enable comment auto-refresher