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

Race condition in task killing #1923

Closed
brson opened this issue Mar 4, 2012 · 3 comments
Closed

Race condition in task killing #1923

brson opened this issue Mar 4, 2012 · 3 comments
Labels
A-runtime Area: std's runtime and "pre-main" init for handling backtraces, unwinds, stack overflows
Milestone

Comments

@brson
Copy link
Contributor

brson commented Mar 4, 2012

If you run this enough times it will eventually fail: (EDIT by bblum: update to new closure syntax & new failure propagation semantics)

fn main() {
    for iter::repeat(100u) {
        let builder = task::builder();
        task::unsupervise(builder);
        do task::run(builder) {
            let po = comm::port();
            let ch = comm::chan(po);
            let builder = task::builder();
            task::unsupervise(builder);
            do task::run(builder) {
                comm::send(ch, ());
            }   
            do task::spawn {
                fail;
            }   
            comm::recv(po);
        }   
    }   
}
rust: fatal, 'task->get_state() == src' failed, ../src/rt/rust_task_thread.cpp:338

The race is between the two tasks, one that wants to unblock the third by killing it, the other by sending a message.

@brson
Copy link
Contributor Author

brson commented Mar 5, 2012

Here's a similar one that will fail for different reasons: (EDIT by bblum: same as edit above)

// Trying to generate races between killing tasks, sending, and deleting ports
fn main() {
    for iter::repeat(100u) {
        let builder = task::builder();
        task::unsupervise(builder);
        do task::run(builder) {
            let po = comm::port();
            let ch = comm::chan(po);
            let builder = task::builder();
            task::unsupervise(builder);
            do task::run(builder) {
                comm::send(ch, ());
            }
            do task::spawn {
                fail;
            }
        }
    }
}

@ghost ghost assigned brson Mar 8, 2012
@ghost ghost assigned bblum Jun 27, 2012
@bblum
Copy link
Contributor

bblum commented Jul 11, 2012

Running the first one I got a hopefully more useful assertion message (after many tries):

rust_task.cpp:432: void rust_task::wakeup_locked(rust_cond*):
Assertion `cond != __null && "Cannot wake up unblocked task."' failed.

A killer is trying to punt awake a blocked task, and the task is unblocking at the same time. I believe this is the same race.

I've got a scheme for solving this race with my new rust_cond_lock design, but if we're keeping the old ports' one, I should track this down too.

@bblum
Copy link
Contributor

bblum commented Jul 11, 2012

I could not reproduce the second race. I believe 74f4b8d solved it. (It would have been kill() getting interrupted between checking if blocked (kill_lock held) and calling wakeup (which uses state_lock). Unifying the locks made that atomic.)

The first one still races around detach_cond in rust_port.cpp, specifically when a kill happens between these two lines:

 29         if (task->blocked_on(&detach_cond)) {
 30             // XXX race here
 31             task->wakeup(&detach_cond);
 32         }

This is the sort of thing #2787 is warning about.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-runtime Area: std's runtime and "pre-main" init for handling backtraces, unwinds, stack overflows
Projects
None yet
Development

No branches or pull requests

2 participants