| |
| Posted by John Nagle in reply to Walter | PermalinkReply |
|
John Nagle
Posted in reply to Walter
| Walter wrote:
>
> John Nagle wrote in message <3BBB4600.C382802@animats.com>...
> > I've proposed a way to tighten up C++ using reference-counted
> >allocation plus compiler support for temporary, immutable raw pointers. See
> >
> > http://www.animats.com/papers/languages/index.html
> >
> >This turns out to be a tough retrofit to C++, but might be worth looking at for D.
>
> Thanks. I did something similar in C years ago with the __handle pointers.
It's easy to do. There have been many (too many) smart
pointer libraries for C++ The problem is doing it safely and
efficiently. That's what I've been trying to address.
The key idea is introducing local references which can't
outlive the object they reference. Once such a local reference
has been created, its use requires no further reference
counting. That's what the "auto pointer" scheme I proposed
is all about.
From a compiler perspective, it's also worth considering the
automatic introduction of such references, as a way of hoisting
reference counting out of loops.
> I never liked the resurrection possibility, either.
Other troubles with finalizers involve locking (finalizers
introduce concurrency, since they can in be called from another
thread) and exceptions (who catches an exception from a finalizer?).
But the big problem is that finalizers are called late, from GC,
and so can't be used for holding open descriptors, locks, and
other non-memory assets which must be released when unneeded.
Having both finalizers and destructors in the same language is
even worse. If you have finalizers only, you need some way to
have actions take place at scope exit, like Common LISP's
(WITH_OPEN_FILE file block) construct.
> Reference counting has its own problems, like inability to deal with cycles.
Cycles are annoying, but at least have comprehensible semantics.
Weak pointers (as used in Perl, not as used in Java) help deal
with cycles. True cycles of peers are rare; the most common case
is a back pointer to a superior object. Such back pointers should
be weak pointers. That deals with the most common case effectively.
When you truly have interlinked peers, the proper approach is
to have them all owned by a collection in some superior object,
and then have all the inter-object links be weak.
Weak and strong pointer semantics are worth thinking about.
First, you can't dereference a weak pointer; you have to take
a strong pointer from it first. This may be done through an
implicit conversion, but it has to be done, so that the target
object can't go away while referenced. Attempts to dereference
invalid weak pointers must throw.
If you do it that way, destructor sequencing is automatically
correct. If A has a strong pointer to B, and B has a weak
pointer to A, then removing the last strong reference to A
causes A's destructor to be called. A's destructor can make
calls to B's methods, but B's weak pointer back to A is invalid
(because A's strong reference count is 0) so B can't call A's
methods while A's destructor is running. So you can't get
accidental re-entry into an object during destruction.
So reference counted weak and strong pointers can be
efficient, safe, and have clean semantics.
John Nagle
Animats
|