Repository: actix/actix (language: rust)

Issue:
`index out of bounds` during Context::cancel_future(...)
HI.

I encountered a `actix` internal exception which shouldn't happen (I think). Basically I store `SpawnHandles` that represent (kind of) timeouts in a map and try to cancel them upon certain events. The `actix` version used is `0.5.5` along with `rustc 1.25.0 (84203cac6 2018-03-25)` on HighSierra. The crash happens on every run.
I stripped down my case to:

```rust
extern crate actix;

use actix::prelude::*;
use std::time::Duration;

#[derive(Default)]
pub struct A;

struct M;

impl Message for M {
    type Result = ();
}

impl Actor for A {
    type Context = Context<Self>;

    fn started(&mut self, ctx: &mut Self::Context) {
        let t1 = ctx.run_later(Duration::new(2, 0), |_, _| { println!("t1") });
        ctx.run_later(Duration::new(1, 0), move |_, ctx| {
            println!("cancelling t1: {}", ctx.cancel_future(t1));
        });
        ctx.notify_later(M {}, Duration::new(5, 0));
    }
}

impl Handler<M> for A {
    type Result = ();

    fn handle(&mut self, _: M, ctx: &mut Self::Context) -> Self::Result {
        let t2 = ctx.run_later(Duration::new(2, 0), |_, _| { println!("t2") });
        ctx.run_later(Duration::new(1, 0), move |_, ctx| {
            println!("cancelling t2 in handler: {}", ctx.cancel_future(t2));
        });
    }
}

fn main() {
    let sys = actix::System::new("");
    let _ : Addr<Syn, _> = A::default().start();
    let _ = sys.run();
}
```

This code results in 

```shell
# RUST_BACKTRACE=1 cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/actix-cancel`
canceling t1: true
thread 'main' panicked at 'index out of bounds: the len is 1 but the index is 1', /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/smallvec-0.6.0/lib.rs:881:18
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
             at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1: std::sys_common::backtrace::print
             at libstd/sys_common/backtrace.rs:71
             at libstd/sys_common/backtrace.rs:59
   2: std::panicking::default_hook::{{closure}}
             at libstd/panicking.rs:207
   3: std::panicking::default_hook
             at libstd/panicking.rs:223
   4: std::panicking::begin_panic
             at libstd/panicking.rs:402
   5: std::panicking::try::do_call
             at libstd/panicking.rs:349
   6: std::panicking::try::do_call
             at libstd/panicking.rs:325
   7: core::ptr::drop_in_place
             at libcore/panicking.rs:72
   8: core::ptr::drop_in_place
             at libcore/panicking.rs:58
   9: <smallvec::SmallVec<A> as core::ops::index::Index<usize>>::index
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/smallvec-0.6.0/lib.rs:881
  10: <actix::contextimpl::ContextImpl<A>>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-0.5.5/src/contextimpl.rs:284
  11: <actix::context::Context<A> as futures::future::Future>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-0.5.5/src/context.rs:125
  12: <futures::future::map::Map<A, F> as futures::future::Future>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/future/map.rs:30
  13: <futures::future::map_err::MapErr<A, F> as futures::future::Future>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/future/map_err.rs:30
  14: <alloc::boxed::Box<F> as futures::future::Future>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/future/mod.rs:113
  15: <futures::task_impl::Spawn<T>>::poll_future_notify::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/task_impl/mod.rs:289
  16: <futures::task_impl::Spawn<T>>::enter::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/task_impl/mod.rs:363
  17: futures::task_impl::std::set
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/task_impl/std/mod.rs:78
  18: <futures::task_impl::Spawn<T>>::enter
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/task_impl/mod.rs:363
  19: <futures::task_impl::Spawn<T>>::poll_future_notify
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-0.1.21/src/task_impl/mod.rs:289
  20: <tokio::executor::current_thread::scheduler::Scheduled<'a, U>>::tick
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/scheduler.rs:343
  21: <tokio::executor::current_thread::scheduler::Scheduler<U>>::tick::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/scheduler.rs:323
  22: <tokio::executor::current_thread::Borrow<'a, U>>::enter::{{closure}}::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:657
  23: tokio::executor::current_thread::CurrentRunner::set_spawn
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:689
  24: <tokio::executor::current_thread::Borrow<'a, U>>::enter::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:656
  25: <std::thread::local::LocalKey<T>>::try_with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:290
  26: <std::thread::local::LocalKey<T>>::with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:244
  27: <tokio::executor::current_thread::Borrow<'a, U>>::enter
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:655
  28: <tokio::executor::current_thread::scheduler::Scheduler<U>>::tick
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/scheduler.rs:323
  29: <tokio::executor::current_thread::Entered<'a, P>>::tick
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:541
  30: <tokio::executor::current_thread::Entered<'a, P>>::turn
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.5/src/executor/current_thread/mod.rs:483
  31: tokio_core::reactor::Core::poll::{{closure}}::{{closure}}::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.16/src/reactor/mod.rs:326
  32: <scoped_tls::ScopedKey<T>>::set
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/scoped-tls-0.1.1/src/lib.rs:155
  33: tokio_core::reactor::Core::poll::{{closure}}::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.16/src/reactor/mod.rs:325
  34: tokio_executor::global::with_default::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-executor-0.1.2/src/global.rs:176
  35: <std::thread::local::LocalKey<T>>::try_with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:290
  36: <std::thread::local::LocalKey<T>>::with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:244
  37: tokio_executor::global::with_default
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-executor-0.1.2/src/global.rs:150
  38: tokio_core::reactor::Core::poll::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.16/src/reactor/mod.rs:289
  39: tokio_reactor::with_default::{{closure}}
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-reactor-0.1.1/src/lib.rs:207
  40: <std::thread::local::LocalKey<T>>::try_with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:290
  41: <std::thread::local::LocalKey<T>>::with
             at /Users/travis/build/rust-lang/rust/src/libstd/thread/local.rs:244
  42: tokio_reactor::with_default
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-reactor-0.1.1/src/lib.rs:199
  43: futures::task_impl::Notify::clone_id
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.16/src/reactor/mod.rs:288
  44: tokio_core::reactor::Core::run
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-core-0.1.16/src/reactor/mod.rs:264
  45: <actix::context::Context<A> as futures::future::Future>::poll
             at /Users/felix/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-0.5.5/src/system.rs:100
  46: actix_cancel::main
             at src/main.rs:41
  47: std::rt::lang_start::{{closure}}
             at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
  48: std::panicking::try::do_call
             at libstd/rt.rs:59
             at libstd/panicking.rs:306
  49: panic_unwind::dwarf::eh::read_encoded_pointer
             at libpanic_unwind/lib.rs:102
  50: <std::io::Write::write_fmt::Adaptor<'a, T> as core::fmt::Write>::write_str
             at libstd/panicking.rs:285
             at libstd/panic.rs:361
             at libstd/rt.rs:58
  51: std::rt::lang_start
             at /Users/travis/build/rust-lang/rust/src/libstd/rt.rs:74
  52: <actix_cancel::A as core::default::Default>::default
```

Am I missing something?


Discussion / hints:

Fix patch:
diff --git a/src/contextimpl.rs b/src/contextimpl.rs
index 2ea73df19..63b486446 100644
--- a/src/contextimpl.rs
+++ b/src/contextimpl.rs
@@ -236,9 +236,12 @@ impl<A> ContextImpl<A> where A: Actor, A::Context: AsyncContext<A>
             // check cancelled handles, just in case
             while self.handles.len() > 2 {
                 let handle = self.handles.pop().unwrap();
-                for idx in 0..self.items.len() {
+                let mut idx = 0;
+                while idx < self.items.len() {
                     if self.items[idx].0 == handle {
                         self.items.swap_remove(idx);
+                    } else {
+                        idx += 1;
                     }
                 }
             }
@@ -280,9 +283,12 @@ impl<A> ContextImpl<A> where A: Actor, A::Context: AsyncContext<A>
                             // in actor context should be small
                             while self.handles.len() > 2 {
                                 let handle = self.handles.pop().unwrap();
-                                for idx in 0..self.items.len() {
+                                let mut idx = 0;
+                                while idx < self.items.len() {
                                     if self.items[idx].0 == handle {
                                         self.items.swap_remove(idx);
+                                    } else {
+                                        idx += 1;
                                     }
                                 }
                             }


Test patch:
diff --git a/tests/test_context.rs b/tests/test_context.rs
index 0c8e72354..b792aaa0c 100644
--- a/tests/test_context.rs
+++ b/tests/test_context.rs
@@ -30,8 +30,10 @@ impl Actor for MyActor {
     fn started(&mut self, ctx: &mut Context<MyActor>) {
         match self.op {
             Op::Cancel => {
-                let handle = ctx.notify_later(TimeoutMessage, Duration::new(0, 100));
-                ctx.cancel_future(handle);
+                let handle0 = ctx.notify_later(TimeoutMessage, Duration::new(0, 100));
+                let handle1 = ctx.notify_later(TimeoutMessage, Duration::new(0, 100));
+                assert!(ctx.cancel_future(handle1));
+                assert!(ctx.cancel_future(handle0));
             },
             Op::Timeout => {
                 ctx.notify_later(TimeoutMessage, Duration::new(0, 1000));