From 3b30377e14f60e6381dc1536bd53b5f9c7a3d7c7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 22 Oct 2013 14:59:21 -0700 Subject: [PATCH] Fix a bug with the scheduler and destructor order The PausibleIdleCallback must have some handle into the event loop, and because struct destructors are run in order of top-to-bottom in order of fields, this meant that the event loop was getting destroyed before the idle callback was getting destroyed. I can't confirm that this fixes a problem in how we use libuv, but it does semantically fix a problem for usage with other event loops. --- src/libstd/rt/sched.rs | 12 ++++- src/test/run-pass/field-destruction-order.rs | 52 ++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 src/test/run-pass/field-destruction-order.rs diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 6e661884616..1a6529dab18 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -85,7 +85,17 @@ pub struct Scheduler { priv yield_check_count: uint, /// A flag to tell the scheduler loop it needs to do some stealing /// in order to introduce randomness as part of a yield - priv steal_for_yield: bool + priv steal_for_yield: bool, + + // n.b. currently destructors of an object are run in top-to-bottom in order + // of field declaration. Due to its nature, the pausible idle callback + // must have some sort of handle to the event loop, so it needs to get + // destroyed before the event loop itself. For this reason, we destroy + // the event loop last to ensure that any unsafe references to it are + // destroyed before it's actually destroyed. + + /// The event loop used to drive the scheduler and perform I/O + event_loop: ~EventLoopObject, } /// An indication of how hard to work on a given operation, the difference diff --git a/src/test/run-pass/field-destruction-order.rs b/src/test/run-pass/field-destruction-order.rs new file mode 100644 index 00000000000..1d4c08f0bb5 --- /dev/null +++ b/src/test/run-pass/field-destruction-order.rs @@ -0,0 +1,52 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// In theory, it doesn't matter what order destructors are run in for rust +// because we have explicit ownership of values meaning that there's no need to +// run one before another. With unsafe code, however, there may be a safe +// interface which relies on fields having their destructors run in a particular +// order. At the time of this writing, std::rt::sched::Scheduler is an example +// of a structure which contains unsafe handles to FFI-like types, and the +// destruction order of the fields matters in the sense that some handles need +// to get destroyed before others. +// +// In C++, destruction order happens bottom-to-top in order of field +// declarations, but we currently run them top-to-bottom. I don't think the +// order really matters that much as long as we define what it is. + +struct A; +struct B; +struct C { + a: A, + b: B, +} + +static mut hit: bool = false; + +impl Drop for A { + fn drop(&mut self) { + unsafe { + assert!(!hit); + hit = true; + } + } +} + +impl Drop for B { + fn drop(&mut self) { + unsafe { + assert!(hit); + } + } +} + +pub fn main() { + let _c = C { a: A, b: B }; +}