sfk@otter.hpl.hp.com (Steve Knight) (04/24/91)
I've been reading the most recent draft standard I've got (Nov89) and can't determine the answer to the following question. When a port is garbage collected, is it automatically closed on your behalf? Also, when a port is created, will every attempt (including garbage-collection) be made to free unused ports? If this isn't a question answered by the draft standard then I'd appreciate knowing the general implementation strategies. Steve
kend@data.UUCP (Ken Dickey) (04/26/91)
sfk@otter.hpl.hp.com (Steve Knight) writes: >I've been reading the most recent draft standard I've got (Nov89) and can't >determine the answer to the following question. When a port is garbage >collected, is it automatically closed on your behalf? ... This is unspecified in the standard. >If this isn't a question answered by the draft standard then I'd appreciate >knowing the general implementation strategies. Here are a couple: Maintain a table of open ports. At collection time, scan the table and close any ports which have been collected. Implement finalization objects. I did this in a local copy of the Gambit runtime (v1.5) by implementing weak-pairs (the car goes to nil when collected). The finalization procedure registers each object to be finalized with a thunk which does the cleanup. The object is in the car of a weak pair, the thunk in the cdr. After a collection, the weak-pairs are scanned and any who's car is nil is thrown away after the finalization thunk is invoked. {The rules are that such a thunk does not cons and does not invoke random continuations.} ========== A finalization service is also helpful for freeing storage for dumb allocators like C's malloc. Weaks/populations, etc. are helpful in a variety of ways. Weak-pairs are easy to implement in a stop-and-copy collector, by the way. There is a very clear description of this in Jim Millers thesis: "MultiScheme", MIT/LCS/TR-402, September 1987. I can send you code if that is interesting. -Ken Dickey kend@data.uucp
news@argosy.UUCP (USENET News System) (04/26/91)
In article <6260006@otter.hpl.hp.com> sfk@otter.hpl.hp.com (Steve Knight) write\ s: >I've been reading the most recent draft standard I've got (Nov89) and can't >determine the answer to the following question. When a port is garbage >collected, is it automatically closed on your behalf? Also, when a port is >created, will every attempt (including garbage-collection) be made to free >unused ports? In Pixie Scheme (Macintosh Shareware) the heap object for a "port" is a tagged pointer to another heap object that contains more information. Thus if you do (define foo (open-output-file "stuff")) (define bar foo) then foo and bar will connect to the same file, so that alternate writes to foo and bar will appear in that file in alternating sequence. The unnamed "other heap object" contains, among other things, a pointer into a table of non-heap data structures that contain the real definition, status and so forth for each open port (Pixie Scheme imposes a fixed limit on how many ports may be open at once, so a non-heap table is reasonable), and which also contains a back pointer to the (unique) heap object which referenced it. On garbage collection (Pixie Scheme uses two heap spaces with a variant on mark-and-copy), Pixie Scheme updates each back pointer appropriately. After the copy, Pixie Scheme looks through the non-heap table of ports to see which ones have had their back pointers updated to point into the new space. The others are garbage, and are closed if still open. Thus in the example given, if "foo" becomes garbage but "bar" is not, then the heap structure associated with it will still have one pointer to it from non-garbage (bar), so that heap structure will be non-garbage, so it will get copied to new space, so its back pointer in non-heap will be updated to point to new space, so the garbage collector will know not to close the port. But if foo and bar are both garbaged, then the heap structure will not be copied, so its back pointer will still point into old space, so the garbage collector will close the port if the user has not done so. The implementation of "open-output-file", etc., knows to look through the table of non-heap port data structures for one which is not in use, and fails if there are none. At the moment, these implementation routines do not force a garbage-collection if there are no ports free, though they could be made to do so, and probably I will get around to making that happen some day. (I haven't done so merely because it is clumsy to force garbage collection from any place other than the memory allocator in my particular Scheme implementation, not because it is a bad idea.) I have no idea how other implementations do it, and would be curious myself about the answers. -- Jay Freeman <canonical disclaimer -- I speak only for myself>
mikpe@ida.liu.se (Mikael Pettersson) (04/27/91)
In article <1262@argosy.UUCP> freeman@cleo.UUCP (Jay R. Freeman) writes: > [describes the approach taken in Pixie Scheme] >I have no idea how other implementations do it, and would be curious >myself about the answers. Here's how my Scheme handles garbage ports: * A port object contains, among other things, a C file-pointer and a weak pointer to another port (or NIL). * When a new port is allocated (by open-{input,output}-file), it's added to a "list" (using the weak "next port" ptr) of all ports. * After each collection, this list is scanned. Surviving ports are (re-)linked together by updating their "next port" pointers. Dead ports are closed (unless close-{input,output}-port did that before) and then forgotten. A disadvantage of this approach is that the last scanning phase could exhibit poor locality, touching random pages both in the old and new heaps. On the other hand, the average number of ports is usually less than 10 (3-6 seems to be the average in my system) so this is probably not a serious problem. The advantages or having safe and fast deallocation of a scarce OS resource are much more important. (I have seen Lisp/Scheme implementations (mark-sweep based) that would check, for each dead object, whether it was a port, and if so do the cleanup action. Not quite what I would call "efficient"..) /Mike -- Mikael Pettersson, Dept of Comp & Info Sci, University of Linkoping, Sweden email: mpe@ida.liu.se or ...!{mcsun,munnari,uunet,unido,...}!sunic!liuida!mpe
gyro@cymbal.reasoning.COM (Scott Layson Burson) (04/30/91)
Date: 26 Apr 91 15:40:30 GMT From: Ken Dickey <data!kend@bloom-beacon.mit.edu> [Answering a question about whether garbage ports are closed automatically] Implement finalization objects. I did this in a local copy of the Gambit runtime (v1.5) by implementing weak-pairs (the car goes to nil when collected). The finalization procedure registers each object to be finalized with a thunk which does the cleanup. The object is in the car of a weak pair, the thunk in the cdr. After a collection, the weak-pairs are scanned and any who's car is nil is thrown away after the finalization thunk is invoked. {The rules are that such a thunk does not cons and does not invoke random continuations.} A finalization service is also helpful for freeing storage for dumb allocators like C's malloc. Weaks/populations, etc. are helpful in a variety of ways. Somebody on this list recently alluded to "the well-known difficulty of defining UNWIND-PROTECT in the presence of firstclass continuations". I have long thought that what UNWIND-PROTECT ought to mean is that the cleanup actions are performed when the continuation in which they were established becomes garbage. As Ken points out, this really isn't all that hard to implement. -- Scott Gyro@Reasoning.COM
jinx@zurich.ai.mit.edu (Guillermo J. Rozas) (04/30/91)
In article <9104291751.AA10911@cymbal.reasoning.com.> gyro@cymbal.reasoning.COM (Scott Layson Burson) writes:
Somebody on this list recently alluded to "the well-known difficulty of
defining UNWIND-PROTECT in the presence of firstclass continuations".
I have long thought that what UNWIND-PROTECT ought to mean is that the
cleanup actions are performed when the continuation in which they were
established becomes garbage.
That is not clear at all.
The standard example of UNWIND-PROTECT (from CLtLII) is
(unwind-protect
(progn (start-motor)
(drill-hole))
(stop-motor))
I don't think that waiting until the continuation is garbage to stop
the motor is what the user intended.
Sometimes you want it to happen on any exit from the protected action.
Sometimes you want it to happen on "final" exit, whatever that means.
As Ken points out, this really isn't all that hard to implement.
The issue is not implementation, but what the correct behavior should
be.
kend@data.UUCP (Ken Dickey) (05/02/91)
gyro@cymbal.reasoning.COM (Scott Layson Burson) writes: >I have long thought that what UNWIND-PROTECT ought to mean is that the >cleanup actions are performed when the continuation in which they were >established becomes garbage. >As Ken points out, this really isn't all that hard to implement. A point of clarification. I was referring to the implementation of a finalization service on top of weak-pairs. Weak pairs are very easy to implement in a stop-and-copy (semi-space) collector. Your mileage may vary with other collection strategies. Also, finalization is related to lifetime/extent. Unwind-protect deals more directly with control flow. {I still think its a good idea to turn off the motor (8-D)}. -Ken kend@data.uucp
sfk@otter.hpl.hp.com (Steve Knight) (05/10/91)
Thanks for the responses. I'm somewhat disappointed that the standard doesn't specify that files are closed at both port creation & finalisation times. I think it's an issue on the same order as tail-call optimisation (although less important.) Steve