Change:
Currently there are no landing pads generated for resource destructors so they are leaky when they fail. We don't have the same tricky situation that C++ has with throwing destructors but I haven't thought through how it should work yet. Edit: This issue has changed into what to do after a dtor fails during unwinding. The previous issue of dtor failure not working at all has been resolved.
There's a failing test in run-fail/unwind-resource-
And another in run-fail/unwind-resource-
The language reference is actually quite explicit about this: "A task may transition to the failing state at any time, due to an un-trapped signal or the evaluation of a fail expression. Once failing, a task unwinds its stack and transitions to the dead state. Unwinding the stack of a task is done by the task itself, on its own control stack. If a value with a destructor is freed during unwinding, the code for the destructor is run, also on the task's control stack. Running the destructor code causes a temporary transition to a running state, and allows the destructor code to cause any subsequent state transitions. The original task of unwinding and failing thereby may suspend temporarily, and may involve (recursive) unwinding of the stack of a failed destructor. Nonetheless, the outermost unwinding activity will continue until the stack is unwound and the task transitions to the dead state. There is no way to “recover” from task failure. Once a task has temporarily suspended its unwinding in the failing state, failure occurring from within this destructor results in hard failure. The unwinding procedure of hard failure frees resources but does not execute destructors. The original (soft) failure is still resumed at the point where it was temporarily suspended. "
Yeah. The hard/soft thing has never been implemented, as far as I know, but I think it's the best we can do: retain correctness of our memory model and try to retain correctness of user code, but not at the cost of infinite-recursion. If their dtor itself fails, just unwind that sub-failure w/o the opportunity for sub-sub-failures.
It would be good to revise the title of this bug to reflect what the task is. (From the comments thus far, it sounds like the task is to implement the hard/sort failure unwindin protocol; but it might also be useful to clean up the docs here.)
Not critical for 0.6; de-milestoning
confirmed again; current code that can crash is as follows: If my idea for an effect system goes through, we might make this sound again by simply requiring no failure in destructors, e.g. .
nominating for well-defined milestone
there is language about this in the manual already in the task section, so I think it's well defined already. just not implemented. feature completeness issue.
accepted for feature-complete milestone
Triage: the updated version of example still crashes:
0 backcompat
This not only applies to destructors failing while failing, but this appears to also apply to linked failure as well. A program may only contain one statement, but it may still leak due to linked failure. An example of this would be: The destructor for never runs in this program because the call to invokes scheduler business which will realize that it needs to fail due to linked failure. It seems odd to me that if a task only contains one source of failure then we can still leak. This specific use case may not entirely fall under this issue, but it seems fairly serious.
The scheduler does have logic for not failing from linked failure when a task is already failing. So if, for example, the sleeping destructor here were to happen during unwinding, I think it should be fine. But if it happens normally you could still get failure.
I've updated the title to reflect what we're currently talking about. My understanding is that it is just impossible to throw from a landing pad because it will potentially corrupt the unwinder and our three options are: abort - this is what C++ does resume unwinding without completing the landing pad - this is what Ada does and we haven't discussed this option abort the task without completing unwinding - this is "hard" failure we've talked about The "hard" failure is made more difficult by tasks that are not simply green threads. For example, we have use cases where we want to run in a 'task context', on the current thread, and then be told whether the task succeeded or failed, all on the same thread. There's no way in that scenario to just terminate the task cleanly. My current opinion is that, for 1.0 we should take the most conservative approach and abort. For future versions we should consider trying to do what Ada does and skip a single landing pad, but still proceed to catch the exception.
I agree with double-abort for 1.0. Thinking about the other two schemes in terms of how RWARCs (or whatever they're called these days) behave, aborting just a single task strikes me as risky in terms of unexpected behaviour befalling the user. If a double-failure occurs inside an access to a RWARC, it won't be automatically unlocked during unwinding, and other contending tasks will block forever. I think that resuming the unwinding and skipping the one landing pad still makes sense, though. If double-failure occurs in a user-provided destructor (or in the destructor of some buggy library), skipping the rest of the destructor can only "surprise" other code directly associated with the faulty destructor.
I agree too.

Discussion:
until the stack is unwound and the task transitions to the *dead* state. There is no way to "recover" from task failure.  Once a task has temporarily suspended its unwinding in the *failing* state, failure <del> occurring from within this destructor results in *hard* failure.  The unwinding procedure of hard failure frees resources but does not execute destructors.  The original (soft) failure is still resumed at the point where it was temporarily suspended. </del> <ins> occurring from within this destructor results in *hard* failure. A hard failure currently results in the process aborting. </ins> A task in the *dead* state cannot transition to other states; it exists only to have its termination status inspected by other tasks, and/or to await

} if (*task).unwinder.unwinding { <del> rtabort!("unwinding again"); </del> <ins> // If a task fails while it's already unwinding then we // have limited options. Currently our preference is to // just abort. In the future we may consider resuming // unwinding or otherwise exiting the task cleanly. rterrln!("task failed during unwinding (double-failure - total drag!)") rterrln!("rust must abort now. so sorry."); intrinsics::abort(); </ins> } }