Skip to content

Commit 51e89a4

Browse files
author
Andrew J Westlake
committed
Added test to reproduce race condition
1 parent 375c59b commit 51e89a4

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ path = "pytests/test_tokio_multi_thread_uvloop.rs"
102102
harness = false
103103
required-features = ["tokio-runtime", "testing"]
104104

105+
106+
[[test]]
107+
name = "test_race_condition_regression"
108+
path = "pytests/test_race_condition_regression.rs"
109+
harness = false
110+
required-features = ["async-std-runtime", "testing"]
111+
105112
[dependencies]
106113
async-channel = { version = "1.6", optional = true }
107114
clap = { version = "3.1.5", optional = true }
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
use pyo3::{prelude::*, wrap_pyfunction};
2+
3+
#[pyfunction]
4+
fn sleep<'p>(py: Python<'p>, secs: &'p PyAny) -> PyResult<&'p PyAny> {
5+
let secs = secs.extract()?;
6+
7+
pyo3_asyncio::async_std::future_into_py(py, async move {
8+
async_std::task::sleep(std::time::Duration::from_secs_f64(secs)).await;
9+
Ok(())
10+
})
11+
}
12+
13+
const RACE_CONDITION_REGRESSION_TEST: &str = r#"
14+
import asyncio
15+
import random
16+
17+
async def trigger_race_condition(rust_sleeper, delay):
18+
coro = asyncio.wrap_future(rust_sleeper(0.1))
19+
await asyncio.sleep(delay)
20+
coro.cancel()
21+
22+
def main(rust_sleeper):
23+
race_condition_triggered = False
24+
25+
for i in range(1000):
26+
delay = random.uniform(0.099, 0.101)
27+
loop = asyncio.new_event_loop()
28+
loop.set_debug(True)
29+
30+
def custom_exception_handler(loop, context):
31+
nonlocal race_condition_triggered
32+
race_condition_triggered = True
33+
34+
try:
35+
loop.set_exception_handler(custom_exception_handler)
36+
loop.run_until_complete(trigger_race_condition(rust_sleeper, delay))
37+
38+
if race_condition_triggered:
39+
raise Exception("Race condition triggered")
40+
41+
finally:
42+
loop.run_until_complete(loop.shutdown_asyncgens())
43+
if hasattr(loop, 'shutdown_default_executor'):
44+
loop.run_until_complete(loop.shutdown_default_executor())
45+
loop.close()
46+
"#;
47+
48+
fn main() -> pyo3::PyResult<()> {
49+
pyo3::prepare_freethreaded_python();
50+
51+
Python::with_gil(|py| -> PyResult<()> {
52+
let sleeper_mod = PyModule::new(py, "rust_sleeper")?;
53+
54+
sleeper_mod.add_wrapped(wrap_pyfunction!(sleep))?;
55+
56+
let test_mod = PyModule::from_code(
57+
py,
58+
RACE_CONDITION_REGRESSION_TEST,
59+
"race_condition_regression_test.py",
60+
"race_condition_regression_test",
61+
)?;
62+
63+
test_mod.call_method1("main", (sleeper_mod.getattr("sleep")?,))?;
64+
Ok(())
65+
})
66+
}

0 commit comments

Comments
 (0)