Destructors =========== Destructors are special methods executed just before the object will be destroyed. It means that a programmer _has_to_ pay special attention to the destructor's code and _NEVER_ store the reference to SELF object in external items. The piece of memory where the instance of class (object) is held will be freed when the destructor finishes, so any references to SELF object will point to uninitialized memory or memory allocated for other structures. Sooner or later (probably in a next GC pass) these references will be accessed, causing GPFs or some other unpredictable problems. Harbour implementation ====================== General destructor activation ----------------------------- Each object item has a reference counter. When it reaches 0 the object is destroyed and if it has destructor message then this message will be executed just before freeing the memory. After executing destructor HVM checks if a programmer didn't store the reference to the object being destroyed somewhere, and if he did then RT error is generated. It's possible to detect such situation by simply checking the reference counter. Such situation is not dangerous for HVM integrity because the memory is not freed - instead the object is converted to an empty array. Though it does not mean that the application is valid. A programmer stored a reference to SELF object somewhere and when he will try to access it as an object, some other RT errors will be generated. For sure such programs have to be fixed. It's the not the only one situation when destructors can be executed. It's possible to create cyclic references between some complex items so the reference counters will never reach 0 even if the items are not longer accessible by application. To avoid memory leaks, such items are destroyed by Garbage Collector in a special way. GC scans all items known to HVM and marks them as used, then destroys all items which are not marked. The reference counters in such items are greater then zero and cannot be directly used to detect bugs in a user code. So GC collects all inaccessible items and then executes cleanup functions for each of them, and finally checks if reference counters reached zero before it will free the memory blocks. If they didn't then RT error is generated for the first memory block. All items which are still accessible, are not freed and if GC can recognize a type of an item then it will also try to convert it to some empty form (f.e. empty array). The destructors are executed from cleanup functions so they all will be executed and then, if there is something wrong, RT error will be generated for the first memory block which was copied to some external structures. Please note that the order in which destructors are executed by GC can be different then some logical order defined by an application. HVM does not know anything about programmer's ideas so a programmer has to create a code which will be safe for such situations. HVM only guaranties that destructors will be executed only once for each object. This also cannot break HVM integrity for standard object items which are represented as arrays. But if the problem is inside cleanup function of a GC POINTER item, which has a structure unknown to HVM, then any further behavior can be unpredictable if a programmer, who created such pointer items, doesn't support such situation himself in his C code. It's a good practice to add some type of marker to body of memory allocated by hb_gcAlloc() to detect bugs in .prg code destructors which may keep pointers to freed POINTER item (these could be destructors of differ object items). In such case GC will not free the block so cleanup function (not object destructor) will be executed second time when the buggy reference will be cleared. Such marker can help to make clean-up function safe for such situation. It's a programmer's implementation decision if such pointer item should be still valid and work like before or drop its capacities after first cleanup function execution. Anyhow the code should expect such situation. In summary, Harbour destructor implementation should be able to detect bugs in destructor and keep HVM integrity. But we are not able to guarantee that nothing wrong will happen with 3rd party code which uses POINTER items scanned by GC, which are not safe for .prg code bugs in destructors and repeated cleanup function execution. Exiting the application and HVM closing --------------------------------------- When HVM exits all items on HVM stack (local variables and parameters) are cleaned. Then HVM clears all memvar items. After these two steps HVM executes GC and all items which are not longer accessible will be freed. Then HVM closes RDD system. It's the last moment when object destructors can be executed, because in next steps, the classy subsystem is closed and all static variables cleared. So for all items which still exist as STATIC variables or in some other structures, the object destructors will not be executed. Clearing STATIC variables before closing classy subsystem will not help because STATIC variables are integral part of this subsystem. Anomalies and exceptions ------------------------ In some situations HVM may clear items when exception appear, f.e. BREAK or QUIT request. In such case executing the exception type is stored and destructors are executed and finally the exception restored. But in destructors code new exception can appear. In such case HVM will give higher priority to QUIT request. If both exception are BREAK then the one from destructor is taken because it could overwrite the error object created before destructor. Inheritance ----------- If class has more then one destructor inherited from other classes then all destructors are executed in reverted order. First current class destructor (if any) and then super class destructors. Defining destructors in CLASS definition code --------------------------------------------- CREATE CLASS ... ... DESTRUCTOR ... ENDCLASS Przemyslaw Czerpak (druzus/at/priv.onet.pl)