Sockets and finalisation

Andreas Raab andreas.raab at gmx.de
Thu Jan 22 21:42:54 UTC 2004


Hi,

The thing to keep in mind for all of the places where finalization is
provided in the image is that finalization is ALMOST NEVER a replacement for
proper resource management. In short: The only reason why we provide
finalization support for some objects (such as files and sockets) is that
without them, any "forgotten" file/socket would leave to dangling OS
resources and therefore get problematic before too long. As with all
resource-containing objects, having proper resource-deallocation is critical
as well as application dependent. The only thing the system can do in
general is to make sure that it closes the appropriate resources when it can
prove that this object is no longer used (e.g., GCed). Finalization is
sometimes understood to be "sort of" resource management but in almost all
"real world applications" this won't work too well.

Therefore, if you write an app which uses resource-allocating objects, it is
YOUR task to make sure they are closed properly. Which is one of the reasons
why patterns such as:
    file := self openFile: 'foo.txt'
    [self doSomethingWith: file] ensure:[file close].
are used. IOW, the above ensures that the opened file is indeed properly
closed after we are finished with it, finalization or not.

(completely OT, but I have long thought that it might be wiser not to
provide the current finalization facilities as they typically lead to worse
code since people are relying on it as the default rather than the
exceptional facility that it actually constitutes)

> If I have an object aNode that holds onto aSocket then
> I can do all sorts of things with the socket.  If I forget about
> aNode, say because it was in an inspector, the socket is still
> around and survives any number of GCs.  I think this is because
> it is registered externally?

No. It is because finalization is only run if the object is GCed and your
inspector will hold a live reference to aNode and aNode will hold a live
reference to the socket and so the socket doesn't get GCed. (note that the "
chase pointers" feature can be misleading as it is assumed that for this
feature you are interested in "all but" the reference paths starting from
the inspector you hold).

> If I wanted the semantics that after dropping aNode
> I wanted to send closeAndDestroy to its socket I feel
> I might want to head in the area of finalisation but
> I don't know how to do this.

This depends on what you consider "dropping" aNode. If you mean "wait until
it is GCed" rather than somehow explicitly "shut down", then you _may_ want
to use finalization. If so, have a look at Object>>toFinalizeSend:to:with:.

> What is the best, or considered, way to handle processes
> in objects when you then forget about the object without
> cleaning up the process.  If you have a process blocking
> on a semaphore I assume that it will never be GCd because,
> at least, the scheduler has got a hold of it.  So should I
> also be looking at finalisation here?

It depends. A process blocking on a semaphore WILL get GCed if the
associated semaphore gets GCed. For example:

| process sema weakObserver |
sema := Semaphore new.
process := [sema wait] forkAt: Processor userInterruptPriority.
weakObserver := WeakArray with: process.
"No GC both semaphore and process"
process := sema := nil.
Smalltalk garbageCollect.
"lo and behold! it's gone"
weakObserver first.

So if you'd have a process blocking on a semaphore in a socket, all of
socket, semaphore and process are likely to "go away" at the same time.

Cheers,
  - Andreas




More information about the Squeak-dev mailing list