diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2014-07-23 22:49:19 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2014-07-30 07:06:44 -0700 |
| commit | 355c798ac3eba15bb2d53a6c553c6149391f9615 (patch) | |
| tree | 7426185c4107170da393d78489638b505b7994cb | |
| parent | e156d001c6577593295f6eee417ea8758fbc4a84 (diff) | |
| download | rust-355c798ac3eba15bb2d53a6c553c6149391f9615.tar.gz rust-355c798ac3eba15bb2d53a6c553c6149391f9615.zip | |
native: Don't deadlock the runtime on spawn failure
Previously, the call to bookkeeping::increment() was never paired with a decrement when the spawn failed (due to unwinding). This fixes the problem by returning a "bomb" from increment() which will decrement on drop, and then moving the bomb into the child task's procedure which will be dropped naturally.
| -rw-r--r-- | src/libnative/task.rs | 4 | ||||
| -rw-r--r-- | src/librustrt/bookkeeping.rs | 12 |
2 files changed, 13 insertions, 3 deletions
diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 35367ff2efa..c72d6c24a7c 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -71,7 +71,7 @@ pub fn spawn_opts(opts: TaskOpts, f: proc():Send) { // Note that this increment must happen *before* the spawn in order to // guarantee that if this task exits it will always end up waiting for the // spawned task to exit. - bookkeeping::increment(); + let token = bookkeeping::increment(); // Spawning a new OS thread guarantees that __morestack will never get // triggered, but we must manually set up the actual stack bounds once this @@ -93,7 +93,7 @@ pub fn spawn_opts(opts: TaskOpts, f: proc():Send) { let mut task = task; task.put_runtime(ops); drop(task.run(|| { f.take_unwrap()() }).destroy()); - bookkeeping::decrement(); + drop(token); }) } diff --git a/src/librustrt/bookkeeping.rs b/src/librustrt/bookkeeping.rs index fd290491eaf..ba9995e34ca 100644 --- a/src/librustrt/bookkeeping.rs +++ b/src/librustrt/bookkeeping.rs @@ -19,14 +19,24 @@ //! decrement() manually. use core::atomics; +use core::ops::Drop; use mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; static mut TASK_COUNT: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT; static mut TASK_LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; -pub fn increment() { +pub struct Token(()); + +impl Drop for Token { + fn drop(&mut self) { decrement() } +} + +/// Increment the number of live tasks, returning a token which will decrement +/// the count when dropped. +pub fn increment() -> Token { let _ = unsafe { TASK_COUNT.fetch_add(1, atomics::SeqCst) }; + Token(()) } pub fn decrement() { |
