Subject: Re: testing for open-ness (was Re: conservative gc sucks)
From: Erik Naggum <erik@naggum.no>
Date: 11 Jan 2003 03:57:25 +0000
Newsgroups: comp.lang.lisp
Message-ID: <3251246245614991@naggum.no>

* lord@emf.emf.net (Tom Lord)
| Consider the example I posted a while back: a computation that
| involves a graph whose nodes may refer to streams which are network
| connections.  When part of the graph become inaccessible, _that_ is
| when the stream needs to be closed.  In that example, it is my
| program, not the stream peer, who marks the end-of-stream.  My
| program has to make that decision based on the lifetimes of nodes in
| my graph.  The garbage collector is already there, computing those
| lifetimes for me -- it is the right tool for the job.

  Tom, which war are you fighting?  I am trying to show you that the
  way you see things has contributed to, if not produced, conclusions
  that you stick by as if they were The Exclusive Truth, which they
  are not, and that if you opened up a little bit, you would find that
  you can look at the same evidence in different ways and arrive at
  very different conclusions.  If you have a problem, we will help you
  find a solution.

  But there is, I suspect, a desire on your part to prove your initial
  hunch, and to do this you construct ever more problems when your
  "challenges" have been solved, but to anyone who listens to such
  "argumentation", the conclusion is fairly inevitable: You construct
  more "problems" in order to support a foregone conclusion.

  But here's how to implement garbage collection of file descriptors:
  Scan the heap for streams and look into them for file descriptors.
  When you have collected all the file descriptors this way, and maybe
  accounted for system streams, loop over the file descriptor space
  and close all that are not supposed to be open.  It would not hurt
  if you did this after a garbage collection, of course, but it can be
  done at any time with no ill effects (other than a slew of system
  calls that result in harmless error returns).

  Please note that I said "garbage collection of file descriptors" and
  not "garbage collection of streams".  This difference is important
  only because you have to be slightly more careful about caches and
  buffers.  In a programming style that depended on this scheme, you
  would naturally want to force writing buffers to disk before you
  dropped the stream.  This means that "but it would not work, because
  I want garbage collection to flush the stream buffers" is not a very
  smart counter-argument.  You would make it only to strongly affirm
  my conjecture that you are making up problems as you go in order to
  defend your desire for a change in design.

| That's a wrong solution because closing and opening a file may
| return you different file from the one you started with.  The
| solution you've proposed changes the semantics of the program.

  Tom, listen.  You do this trick when it /preserves/ the semantics of
  the program.  You really do understand this and only pretend to be
  stupid, don't you?  If the file referenced by a filename changes
  from open to open, you are not really working with /files/, which
  you said you were, so this is specious at best.  It really looks as
  if you are making up problems, now.

| There is a kind of reductio against my argument if you think I meant
| to say "strictly not impelementable".  One could write a graph
| tracer in lisp itself and, at the application level, garbage collect
| for streams.  So if you think I said "strictly not impelementable",
| that reductio should give you a clue that you've misread me ;-)

  All very good, but do you pick up clues that you have misread
  others?  The above, for instance, is just plain silly.  We all know
  that with sufficient work, everything is strictly implementable in
  any language.  So I do not think you meant to say anything.  Unlike
  you, I do not entertain specious arguments for their own sake, so it
  is quite foreign to me that you you have to defend yourself against
  something you think I think you meant to say.  Really.  So, please,
  clean up this mess and try to write better if you have such fears.

  I believe, but not very strongly, that the reason for your failure
  to be happy with the language as it is, is that you brought with you
  a model of perfect language design before you came to Common Lisp
  and now you think Common Lisp ought to conform to it.  I think the
  reason that I tend to learn quickly and "side" with authorities, is
  that most of the time, I attach no personal identity to the models I
  bring with me when I am relatively ignorant.  When I occasionally do
  (and the first example that comes to mind is Perl), I also fail at
  learning it.  Back in 1993/4, I worked with C++, and the whole
  experience was exceedingly painful because the desire to redesign
  and fix the language was so strong that I had to fight it all the
  time to get any work done.  Every now and then, I see people who
  come to various newsgroups with a "question" that is really a scream
  of despair that reality is not what they wanted it to be.  I used to
  have a .signature line that I took from SF Gate columnist Mark
  Morford (which he may well have taken from somewhere else and that
  joke was lost on me), which pissed off a lot more people than I sort
  of intend my .signature to do, reading "if this is not what you
  expected, please alter your expectations", but I think it is
  actually a really good epitaph to life in general.

  The question is not whether Common Lisp (qua language) guarantees
  garbage collection to close streams cleanly, the questions are what
  you would do differently if it did and whether you can do the same
  things even if it does not.  I realize that this is more problem-
  solution-oriented than many people appear to appreciate, but some
  times, the only available solution is not as stunningly beautiful as
  you had dreamt of, because it has to make space for something more.

  So, since Common Lisp does not guarantee orderly closure of streams
  when the stream object is garbage collected, /but/ implementations
  tend to offer finalization because it is sometimes exceedingly nice
  to have a safety net, /and/ the mechanism is non-portable, so you
  need to build some sort of abstraction on top of it, anyway, you
  might as well implement something even cleaner on top of it all.
  When you do this, there is very little danger that you will need to
  worry about how the system cleans up after you, because you will do
  it yourself.  I have suggested two ways to do this that work very
  well for their inherently limited set of circumstances, and you
  should be able to think of some more of your own if you can only
  come to peace with the fact that you have to do /something/ on your
  own to get the behavior you desire.

-- 
Erik Naggum, Oslo, Norway

Act from reason, and failure makes you rethink and study harder.
Act from faith, and failure makes you blame someone and push harder.