
Three bad things: threads, garbage collection, and nondeterministic destructrors - bensummers
http://apenwarr.ca/log/?m=201008#10
======
jmillikin
The author spends a lot of time claiming garbage collection is bad because it
can't solve problems it wasn't designed to solve. Garbage collectors are for
freeing memory allocations. If you're using them to manage scarce resources
(file descriptors and sockets, especially), your design is already broken.

Ditto destructors -- you should write your code such that a destructor closing
a resource is considered a programming error. assert(FALSE) in a debug build,
log it in production, but don't just let such a bug creep silently by. The
only exceptions are C++ RAII wrapper classes, which are designed to have
magical destructors.

C#'s using(), Python's with statement, Haskell's with* functions -- they're
all designed to solve the problem that a generic garbage collector doesn't and
shouldn't, which is "I need precise control over the lifetime of this resource
allocation". Java doesn't have such a mechanism, but then, Java doesn't have
much of anything for a supposedly high-level language. C doesn't have such a
mechanism, but that's OK, because it also doesn't have exceptions.

Additionally, programming with threads is _much_ less complicated than the
author is making it out to be. Sure, it's hard if you're manually (un)locking
mutexes like a damn caveman -- but every major high-level language available
today (except, again, Java) provides better abstractions. Some, like Erlang
and Haskell, make concurrent and parallel programming especially easy.

Finally, if you can get by with a single-threaded, refcounted, deterministic
program -- go ahead. Have fun. The rest of us, here in the real world, have
applications more complicated than a high-school midterm to work on. And
customers won't pay you if the user interface is sluggish because you were too
ignorant to write a proper thread pool.

~~~
tbrownaw
> you should write your code such that a destructor closing a resource is
> considered a programming error.

Not disposing your resources properly is an error. Your language should make
it as easy as possible to make this impossible (or at least highly unnatural).

> The only exceptions are C++ RAII wrapper classes, which are designed to have
> magical destructors.

They are the (only?) correct form of resource management: automatically clean
it up when you're done with it.

> C#'s using(), Python's with statement, Haskell's with* functions -- they're
> all designed to solve the problem that a generic garbage collector doesn't
> and shouldn't, which is "I need precise control over the lifetime of this
> resource allocation". Java doesn't have such a mechanism, but then, Java
> doesn't have much of anything for a supposedly high-level language.

using() is the same as try{}finally{}, which Java does have. The problem with
these is that you can forget them.

The problem with garbage collection is that it seems to always come with the
baked-in assumption that memory is the _only_ resource that really matters, so
handling of other resources is a bolted-on afterthought. GC impeded
correctness, not because of anything fundamental, but because it is seen to
substitute for proper resource management.

~~~
aidenn0
The "With" style construct is the correct way to handle this. Embedding
resources like file-descriptors in objects is a Bad Idea.

Python has added the "with" construct so it can move away from ref-counted
implementations, because the amortized time for refcounting is slower, and
even if you don't care about that, there is still the circular reference
problem.

[edit] I should make it clear I think tying an FDs lifetime to that of an
object is a Bad Idea; I'm not suggesting a blanket ban on ever having a FD
stored in an object.

------
j_baker
"Then you can create a file, write to it, and close it, all in one line. The
closing is implicit, of course; it happens right away as soon as there are no
more references to the file. Because, obviously, nothing else would make
sense."

This behavior isn't even guaranteed in CPython (and never has been as far as I
know). There's a problem with deterministic destructors and refcounting: if
there's a reference cycle, which destructor gets called first?

    
    
        import weakref
        import gc
    
        class Something(object):
             def __init__(self, other):
                self.other = other
    
             def __del__(self):
                print '__del__ called!'
    
        s1 = Something(None)
        s2 = Something(s1)
        s1.other = s2
        del s1
        del s2
        gc.collect()
        print gc.garbage
    

Even the cyclic garbage collector won't pick this up. These kinds of issues
come up more and more if you rely on python's __del__ methods (aka
destructors). The solution? Quit complaining and just use a with block. It
isn't _that_ bad.

~~~
btilly
With reference counting, those issues are _never_ caught.

~~~
j_baker
It's not just about reference counting though. It's a weakness of
deterministic destructors. When you have a reference cycle, it's impossible to
call destructors deterministically even if you _aren't_ using reference
counting.

------
antareus
D seems to be more sensitive to these concerns than other languages, at least
as of late. Classes can be marked as "auto", which indicates they are to be
destructed deterministically. I think this is a more elegant solution than
requiring users to opt-in to proper semantics, though requiring the auto
specifier at the use-site would make things more explicit.

Also, most languages have some sort of thread-based tasking framework that can
make it easier to implement async message-passing based concurrency.

~~~
acqq
It's still bad, since it will lead to the effect "just write every class with
auto."

C++ combined memory allocation and the object destruction in one thing and now
we have too many people that think those should be one primitive. They
shouldn't. Once you recognize that you have to care about both separately, you
start to recognize that even GC is an answer to the wrong question.

For really "cool" programs (using the most of the resources etc) you simply
have to manage memory yourself. Object should not manage their allocation,
they should just "know" who's in charge for their allocation.

Some Bloomberg guys recognized that and made some libraries for C++, ans some
work was done to get something like that in C++ standard, but I don't know if
the result is useful.

I also don't know if such ideas would look nicer in D.

------
ewjordan
My favorite bit:

 _So far, the vast majority of the programs I write have been single-threaded,
refcounted (or manually memory managed), and deterministic. I think I'll
continue to do it that way, thanks._

Well, gee, I'm so glad that's cleared that up for me - here I was, writing
multi-threaded/non-deterministic code all over the place, just because I
_like_ it so much. Tracking down race conditions is a _choice_ , we can choose
not to get involved with any of it, and we'll be just fine! Some guy on the
Internet says so, it must be true!

~~~
jbooth
Well, you can write deterministic code in Java using threads, just use defined
constructs that give you guarantees about how things will be executed and
orders -- ExecutorService and BlockingQueue are your friends.

------
js4all
Real life shows, ref-counting, when done manually, is more error-prone then
GC. Programmers make errors. As single missing release keeps your object
locked in memory. One missing add-ref crashes your program. Microsoft soon
learned this lesson after the introduction of COM and quickly added smart
pointers. Still not a good solution. For their followup technology (.Net) they
used GC. Lession learned.

------
houseabsolute
Meh. Locking is going to be a requirement if you have mutable shared state.
Whether it's threads or events that are driving it, the possible classes of
problems look almost identical.

Merely having a drawback doesn't make something bad. So garbage collection
cannot run destructors deterministically. That is indeed unfortunate but it in
no way outweighs the benefits -- for example, that the vast majority of
classes which don't hold these kinds of resources can just be discarded and
forgotten. It's kind of a stupid thing to say that this alone makes them bad.
It would be like if I said, "Feet suck because they can step on nails." Wait,
that's not quite right.

------
philwise
Please, this is ridiculous. Find me a professional programmer who doesn't
consider garbage collection and threads to be a useful thing.

