As we are now voicing personal preferences. I really like these
multiple return values. Much better than doing them inbound as in
C.
grtz Miek
The Go multiple value/result code hack is certainly ugly in a lot of ways - butit forces programmers to actually take some responsibility for their errors.They can't just write code assuming that any errors will turn into exceptionsthat will be caught and handled elsewhere. They need to consider failurewhen and where it can happen, and act appropriately.I can't say that I think that's automatically a bad thing. It's ugly, and itfrequently seems really awkward when I'm writing code. But I can't saythat I'm convinced it's actually worse than typical exception handlers
You're missing one that is very important in Go, but is less common in
other languages.
5. make the exception go up the flow of control where that ISN'T the call stack.
The fact that in Go the call stack is often not the correct flow of
control means you're going to need to add a lot of explicit error
handling passed between goroutines. People who automatically reach
for exceptions tend to think "that's taken care of" and fail to think
those details through.
I think the issue will be resolved almost automatically when we
realize the nature of a problem. Let's compare error propagation up
stack, and error propagation to parent goroutine.
1. Parent function has only 1 child function at a time. Goroutine
might have N children.
2. When child function executes, parent is idle. When child goroutine
executes, parent is busy.
There's no more differences. Both get addressed by simple mechanism:
- each goroutine has one built-in channel to send errors to parent
(like "standard error")
- each goroutine has one built-in channel to receive errors from
children (like "standard input", but for errors only)
Please think of examples where this won't be enough.
3) To be sure we are on the same page: parent never receives EXCEPTIONfrom children; it has to retrieve their errors from "standard input
for errors" channel explicitly. This eliminates some of your issues.
But for all this to work, it really should be a TREE of goroutines.
[...]
Can we agree on that?
Can I reiterate what I said before?
The issue of orphans is the core of the problem. If you don't impose
any restrictions on the graph of connections between goroutines, this
is very hard question.
TerminateThread is a dangerous function that should only be used in the most extreme cases. You should call TerminateThread only if you know exactly what the target thread is doing, and you control all of the code that the target thread could possibly be running at the time of the termination. For example, TerminateThread can result in the following problems:
- If the target thread owns a critical section, the critical section will not be released.
- If the target thread is allocating memory from the heap, the heap lock will not be released.
- If the target thread is executing certain kernel32 calls when it is terminated, the kernel32 state for the thread's process could be inconsistent.
- If the target thread is manipulating the global state of a shared DLL, the state of the DLL could be destroyed, affecting other users of the DLL.
The rest of your proposal is very similar to the one I described in
"unifying exceptions and goroutine termination", except that
everything is explicit -- you must create your own error channel, and
you must pass it to each goroutine you spawn. I think being explicit
is more in the spirit of the language.
> This sort of situation is already very clearly solved by systems like Erlang
I made a *concerted* effort to understand Erlang's concept. I don't
get it. What is signal? Is it an interrupt? How they manage to
terminate a process when it doesn't want to be terminated? Just by
killing it? (Document says so)
> Even if it really is a problem, and even the core one, you'll have to learn to live with it.
The only way known to me to deal with this problem is java way. That
is, provide no mechanism at all, and do it yourself on case-by-case
basis. Then, each time when you say "go func()", you will have to
write a ton of quite complicated code around its errors.
On Fri, Dec 11, 2009 at 11:46 AM, Jonathan Amsterdam
<jbams...@gmail.com> wrote:
>> The main difference between our
>> approaches is that, in my case, all the goroutines started from a checked
>> goroutine also become checked, while in your case, only one goroutine in the
>> middle of the flow gets a custom error channel. It's like if try{} only
>> worked on the outermost function within the try block, and exceptions thrown
>> in functions called from there passed by uncaught.
>
> I like your way better. I think that the Go designers prefer
> explicitness.
>
This is also how Erlang works-- relationships must be explicitly established.
Say process A spawns process B and links to it, then B spawns process
C. If C dies, nobody cares. If B dies, A dies (unless it traps the
signal) and C continues, none the wiser...