Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Early stopping doesn't work if a Drop impl tries to acquire locks #22

Open
jamesbornholt opened this issue Apr 14, 2021 · 0 comments
Open

Comments

@jamesbornholt
Copy link
Member

This test panics in the Drop impl of PoolItem:

#[test]
fn max_steps_continue_lock_during_drop() {
    let mut config = Config::new();
    config.max_steps = MaxSteps::ContinueAfter(10);
    let scheduler = DfsScheduler::new(None, false);
    let runner = Runner::new(scheduler, config);
    runner.run(|| {
        #[derive(Clone)]
        struct Pool {
            items: Arc<Mutex<VecDeque<usize>>>,
        }

        struct PoolItem {
            pool: Arc<Mutex<VecDeque<usize>>>,
            item: usize,
        }

        impl Pool {
            fn new(length: usize) -> Self {
                Self {
                    items: Arc::new(Mutex::new((0..length).collect())),
                }
            }

            fn get(&self) -> Option<PoolItem> {
                let mut items = self.items.lock().unwrap();
                let item = items.pop_front()?;
                Some(PoolItem {
                    pool: self.items.clone(),
                    item,
                })
            }
        }

        impl Drop for PoolItem {
            fn drop(&mut self) {
                let mut items = self.pool.lock().unwrap();
                items.push_back(self.item);
            }
        }

        let pool = Pool::new(10);

        let threads: Vec<_> = (0..3).map(|_| {
            let pool = pool.clone();
            thread::spawn(move || {
                let _item = pool.get();
                thread::yield_now();
            })
        }).collect();

        for thd in threads {
            thd.join().unwrap();
        }
    })
}

The problem is that ContinueAfter makes us stop the test early, while some PoolItems are still alive. During cleanup we drop the Generator for each thread, which tries to safely clean up by unwinding their stacks, including dropping the PoolItem. But PoolItem's drop tries to acquire a lock, which is no longer allowed once ContinueAfter has triggered.

I'm not totally sure what to do here:

  • We could just leak the continuation's stack...
  • We could do something smarter during teardown, allowing threads to continue running until termination or deadlock, under the assumption that the only code that can run once we drop the continuations is cleanup code
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant