* bin/check.hb
* config/*/*.mk
* contrib/gtwvg/wvgwing.c
* contrib/hbcomm/comm.prg
* contrib/hbfbird/tfirebrd.prg
* contrib/hbfimage/fi_wrp.c
* contrib/hbformat/hbfmtcls.prg
* contrib/hbformat/utils/hbformat.prg
* contrib/hbhttpd/core.prg
* contrib/hbnetio/utils/hbnetio/hbnetio.prg
* contrib/hbnetio/utils/hbnetio/netiomgm.hb
* contrib/hbsqlit3/hdbc.prg
* contrib/hbwin/win_bmp.c
* contrib/xhb/htmutil.prg
* contrib/xhb/thtm.prg
* contrib/xhb/xhbarr.c
* contrib/xhb/xhbtedit.prg
* ChangeLog.txt
* debian/control
* debian/copyright
* doc/*.txt
* LICENSE.txt
* package/harbour.spec
* README.md
* src/compiler/hbusage.c
* src/pp/hbpp.c
* src/rtl/memoedit.prg
* src/rtl/teditor.prg
* src/rtl/tget.prg
* src/rtl/version.c
* utils/hbi18n/hbi18n.prg
* utils/hbmk2/hbmk2.prg
* utils/hbmk2/po/hbmk2.hu.po
* utils/hbtest/hbtest.prg
* sync with 3.4 fork (no change in functionality)
CC3 -> CC4 license, copyright banners, some strings, minor
code changes, doc folder, TOFIX -> FIXME
121 lines
6.0 KiB
Plaintext
121 lines
6.0 KiB
Plaintext
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 <MethodName>
|
|
...
|
|
ENDCLASS
|
|
|
|
|
|
|
|
Przemyslaw Czerpak (druzus/at/priv.onet.pl)
|