- Start date: 2015-06-??
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Summary
Add a exc_drop language item that will be identified with a
ExcDrop trait. When ExcDrop is implemented against a type, the
ExcDrop::exc_drop trait method will be invoked when a value of
that type goes out of scope due to exception-path unwinding.
Motivation
Drop is useful for resource clean-up, but some forms of resource
clean-up may wish to behave differently depending on whether the
resource is being released in the “normal” path execution, or in an
"exceptional" path execution. For example, consider a hypothetical
Fork::scoped routine for IPC in Unix, where, instead of creating a
thread and blocking waiting it to exit, forks off a new process, and
uses waitpid to wait for a process to exit. Fork::scoped might
return a WaitPidGuard object, which will block until the sub-process
exits when normally dropped. When dropped due to exception-handling,
though, it may be preferable for WaitPidGuard to first issue a
signal to the child process (such that the child process would
terminate), to avoid blocking during exceptional-path unwinding.
The idea for ExcDrop was originally developed as a Finalize trait
in this postponed linear-types RFC (in which
implementing Drop around a type with a linear element would allow
values of that type to cease being treated as linear, but in which
exceptional-path clean-up might still be necessary for values of
linear type).
Detailed design
We define an ExcDrop trait, as follows:
#[lang="exc_drop"]
trait ExcDrop {
fn exc_drop(&mut self);
}
The drop-glue inserted for types that implement ExcDrop will be the
normal drop-glue (invoking the Drop::drop function, if defined) for
normal, scope-oriented clean-up. On the other hand, exception-oriented
cleanup, called from an exception-path landing pad, will instead
invoke the exc_drop routine. Considering the parallelism with
Drop, only types for which Drop can be implemented will be able to
implement ExcDrop. (In particular, ExcDrop cannot be implemented
for Copy types.) There are a few corner cases to consider, but I
think the basic design covers all the bases:
Raw structure that contains an ExcDrop field.
No complications.
Drop structure that contains an ExcDrop field.
The exceptional-case drop glue for the Drop type will not change. If
an exception is thrown during the drop routine, then this landing
pad can invoke the exceptional-case code for the ExcDrop field,
before terminating the process. (TBD: perhaps there’s a way that
ExcDrop could be used to clean up our “panic during panic” story?)
ExcDrop structure that contains an ExcDrop field.
Same logic as Drop structure containing an ExcDrop field.
Alternatives
I considered making exc_drop take self by move, instead of as a
mutable borrow. This would allow easy re-use of the scope-based drop
glue from within the exceptional-case drop glue, because it
immediately moves self into the scope-based path. There are a few
drawbacks to that approach:
-
It would imply that the normal-case drop glue would always be invoked at the end of the exceptional-case drop glue (since
selfwill be consumed by scope-based cleanup at the end of theexc_droproutine), which seems like an unnecessary constraint on the design. -
Parallelism with the
Drop::dropinterface struck me as highly beneficial in the API. For example, if we ever solve the issue with allowing moves out ofselfduringdrop, it seems that the same solution should also apply toExcDrop. This is facilitated by making an explicit parallel toDrop's API. -
(Importantly to me, though perhaps not to others): This would complicate the linear-types forward-compatibility story.
Unresolved questions
It feels like ExcDrop may offer a chance for better behavior if we
enter a panic-during-unwind scenario. Does it?