- 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, fork
s 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
self
will be consumed by scope-based cleanup at the end of theexc_drop
routine), which seems like an unnecessary constraint on the design. -
Parallelism with the
Drop::drop
interface struck me as highly beneficial in the API. For example, if we ever solve the issue with allowing moves out ofself
duringdrop
, 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?