Skip to content

Commit f581f8d

Browse files
authored
refactor: keep interpreter exit signaling native (#1277)
Replace the private process-wide threading.Event with an atomic flag while preserving existing shutdown hooks, polling, waits and public interfaces. Retire its four Python-crossing allowances and document the existing monotonic process-wide signal. Amp-Thread-ID: https://ampcode.com/threads/T-01a18715-4e00-708e-a87d-6bd109748d66
1 parent 8dcdc08 commit f581f8d

5 files changed

Lines changed: 17 additions & 27 deletions

File tree

‎crates/sqlbuild-python/src/bindings/_helpers/event_exporting/dispatcher.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ fn limits(options: &KeywordArguments<'_>) -> PyResult<Limits> {
333333
/// Stop every live dispatcher's threads and join them before finalization ends them inside native code.
334334
#[pyfunction]
335335
pub(crate) fn stop_event_exporter_threads(py: Python<'_>) -> PyResult<()> {
336-
signal_interpreter_exit(py)?;
336+
signal_interpreter_exit();
337337
let mut threads: Vec<Bound<'_, PyAny>> = Vec::new();
338338
for thread in imported(py, "threading", "enumerate")?
339339
.call0()?
@@ -483,7 +483,7 @@ impl Shared {
483483
let threads: Vec<Py<PyAny>> = {
484484
let _held = self.hold(py)?;
485485
loop {
486-
if interpreter_exiting(py)? {
486+
if interpreter_exiting() {
487487
return Ok(());
488488
}
489489
let state = self.locked()?;
@@ -597,7 +597,7 @@ impl Shared {
597597
Err(error) if error.is_instance(py, &empty) => {}
598598
Err(error) => return Err(error),
599599
}
600-
if interpreter_exiting(py)? {
600+
if interpreter_exiting() {
601601
return Ok(Delivery::Abandoned);
602602
}
603603
}
Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,15 @@
11
//! The interpreter-exit signal native waits poll so Python daemon threads leave native frames before finalization.
22
3-
use pyo3::prelude::{Bound, Py, PyAny, PyAnyMethods, PyResult, Python};
4-
use pyo3::sync::PyOnceLock;
3+
use std::sync::atomic::{AtomicBool, Ordering};
54

6-
use crate::bindings::_helpers::boundary::python_calls::imported;
7-
8-
static EXIT_SIGNAL: PyOnceLock<Py<PyAny>> = PyOnceLock::new();
9-
10-
/// The process-wide `threading.Event` set once interpreter exit begins.
11-
fn exit_signal(py: Python<'_>) -> PyResult<Bound<'_, PyAny>> {
12-
EXIT_SIGNAL
13-
.get_or_try_init(py, || {
14-
imported(py, "threading", "Event")?
15-
.call0()
16-
.map(Bound::unbind)
17-
})
18-
.map(|signal| signal.bind(py).clone())
19-
}
5+
static EXIT_SIGNAL: AtomicBool = AtomicBool::new(false);
206

217
/// Whether an exit hook has started stopping native daemon threads.
22-
pub(crate) fn interpreter_exiting(py: Python<'_>) -> PyResult<bool> {
23-
exit_signal(py)?.call_method0("is_set")?.is_truthy()
8+
pub(crate) fn interpreter_exiting() -> bool {
9+
EXIT_SIGNAL.load(Ordering::SeqCst)
2410
}
2511

2612
/// Tell every native wait to stop waiting on Python work that may outlive the interpreter.
27-
pub(crate) fn signal_interpreter_exit(py: Python<'_>) -> PyResult<()> {
28-
exit_signal(py)?.call_method0("set")?;
29-
Ok(())
13+
pub(crate) fn signal_interpreter_exit() {
14+
EXIT_SIGNAL.store(true, Ordering::SeqCst);
3015
}

‎crates/sqlbuild-python/src/bindings/_helpers/output_capture/dispatcher.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ fn delivering_thread(py: Python<'_>) -> PyResult<bool> {
122122
/// Stop every capture worker and join it before finalization ends it inside native code.
123123
#[pyfunction]
124124
fn stop_output_capture_threads(py: Python<'_>) -> PyResult<()> {
125-
signal_interpreter_exit(py)?;
125+
signal_interpreter_exit();
126126
let mut threads: Vec<Bound<'_, PyAny>> = Vec::new();
127127
for thread in imported(py, "threading", "enumerate")?
128128
.call0()?

‎crates/sqlbuild-python/src/bindings/_helpers/planner_entries/test_planning.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ pub(crate) fn start_planning(
215215
/// Join SQL test planning threads at exit: finalization ending one inside native planning aborts.
216216
#[pyfunction]
217217
fn join_sql_test_planning_threads(py: Python<'_>) -> PyResult<()> {
218-
signal_interpreter_exit(py)?;
218+
signal_interpreter_exit();
219219
let threads: &PlanningThreads = planning_threads(py)?.get();
220220
py.detach(|| threads.wait(PLANNING_EXIT_JOIN));
221221
Ok(())

‎fensu.toml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,12 @@ rule = "FPRSL105"
15741574
path = "crates/sqlbuild-analysis/src/sql_tokens/main/builtin_function_names.rs"
15751575
reason = "This test-support entry exists only for the SQL lint tests in sqlbuild-rules, which the analyzer does not count as importers."
15761576

1577+
[[targets.rust.rule_exceptions]]
1578+
rule = "FPRSS130"
1579+
path = "crates/sqlbuild-python/src/bindings/_helpers/native_threads/exit_signal.rs"
1580+
symbols = ["signal_interpreter_exit"]
1581+
reason = "The existing interpreter-exit signal is process-wide and monotonic: every native daemon wait must observe any exit hook before Python finalization. The atomic replaces the same global threading.Event, not per-command lifecycle state."
1582+
15771583
[targets.rust.rule_options.FPRSR311]
15781584
allowed_entries = [
15791585
"Cargo.toml",
@@ -1990,7 +1996,6 @@ pending_budgets = [
19901996
"crates/sqlbuild-python/src/bindings/_helpers/model_config/template_data.rs=4",
19911997
"crates/sqlbuild-python/src/bindings/_helpers/model_config/typed_config.rs=2",
19921998
"crates/sqlbuild-python/src/bindings/_helpers/model_config/unicode_environment.rs=4",
1993-
"crates/sqlbuild-python/src/bindings/_helpers/native_threads/exit_signal.rs=4",
19941999
"crates/sqlbuild-python/src/bindings/_helpers/output_capture/context.rs=6",
19952000
"crates/sqlbuild-python/src/bindings/_helpers/output_capture/dispatcher.rs=33",
19962001
"crates/sqlbuild-python/src/bindings/_helpers/output_capture/records.rs=16",

0 commit comments

Comments
 (0)