| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 9f76666 commit fe3b51e
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3309,7 +3309,6 @@ fn is_scope_exit_block(block: &Block) -> bool { | |||
| 3309 | 3309 | .is_some_and(|instr| instr.instr.is_scope_exit()) | |
| 3310 | 3310 | } | |
| 3311 | 3311 | ||
| 3312 | - #[allow(dead_code)] | ||
| 3313 | 3312 | fn is_exception_cleanup_block(block: &Block) -> bool { | |
| 3314 | 3313 | block | |
| 3315 | 3314 | .instructions | |
@@ -3322,7 +3321,7 @@ fn is_exception_cleanup_block(block: &Block) -> bool { | |||
| 3322 | 3321 | } | |
| 3323 | 3322 | ||
| 3324 | 3323 | fn block_is_exceptional(block: &Block) -> bool { | |
| 3325 | - block.except_handler || block.preserve_lasti | ||
| 3324 | + block.except_handler || block.preserve_lasti || is_exception_cleanup_block(block) | ||
| 3326 | 3325 | } | |
| 3327 | 3326 | ||
| 3328 | 3327 | fn trailing_conditional_jump_index(block: &Block) -> Option<usize> { | |
@@ -3773,9 +3772,6 @@ fn duplicate_jump_targets_without_lineno(blocks: &mut Vec<Block>, predecessors: | |||
| 3773 | 3772 | last_mut.target = new_idx; | |
| 3774 | 3773 | predecessors[target.idx()] -= 1; | |
| 3775 | 3774 | predecessors.push(1); | |
| 3776 | - if old_next != BlockIdx::NULL { | ||
| 3777 | - predecessors[old_next.idx()] += 1; | ||
| 3778 | - } | ||
| 3779 | 3775 | ||
| 3780 | 3776 | current = old_next; | |
| 3781 | 3777 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,8 +55,6 @@ pub(crate) mod _thread { | |||
| 55 | 55 | // this is a value in seconds | |
| 56 | 56 | #[pyattr] | |
| 57 | 57 | const TIMEOUT_MAX: f64 = (TIMEOUT_MAX_IN_MICROSECONDS / 1_000_000) as f64; | |
| 58 | - const DEFAULT_THREAD_STACK_SIZE: usize = 8 * 1024 * 1024; | ||
| 59 | - | ||
| 60 | 58 | #[pyattr] | |
| 61 | 59 | fn error(vm: &VirtualMachine) -> PyTypeRef { | |
| 62 | 60 | vm.ctx.exceptions.runtime_error.to_owned() | |
@@ -595,12 +593,11 @@ pub(crate) mod _thread { | |||
| 595 | 593 | vm: &VirtualMachine, | |
| 596 | 594 | ) -> thread::Builder { | |
| 597 | 595 | let configured = vm.state.stacksize.load(); | |
| 598 | - let stack_size = if configured != 0 { | ||
| 599 | - configured | ||
| 596 | + if configured != 0 { | ||
| 597 | + thread_builder.stack_size(configured) | ||
| 600 | 598 | } else { | |
| 601 | - VirtualMachine::current_c_stack_size().max(DEFAULT_THREAD_STACK_SIZE) | ||
| 602 | - }; | ||
| 603 | - thread_builder.stack_size(stack_size) | ||
| 599 | + thread_builder | ||
| 600 | + } | ||
| 604 | 601 | } | |
| 605 | 602 | ||
| 606 | 603 | /// Clean up thread-local data for the current thread. | |
@@ -903,7 +900,7 @@ pub(crate) mod _thread { | |||
| 903 | 900 | // Guard that removes thread-local data when dropped | |
| 904 | 901 | struct LocalGuard { | |
| 905 | 902 | local: Weak<LocalData>, | |
| 906 | - thread_id: std::thread::ThreadId, | ||
| 903 | + thread_id: u64, | ||
| 907 | 904 | } | |
| 908 | 905 | ||
| 909 | 906 | impl Drop for LocalGuard { | |
@@ -919,7 +916,7 @@ pub(crate) mod _thread { | |||
| 919 | 916 | ||
| 920 | 917 | // Shared data structure for Local | |
| 921 | 918 | struct LocalData { | |
| 922 | - data: parking_lot::Mutex<std::collections::HashMap<std::thread::ThreadId, PyDictRef>>, | ||
| 919 | + data: parking_lot::Mutex<std::collections::HashMap<u64, PyDictRef>>, | ||
| 923 | 920 | } | |
| 924 | 921 | ||
| 925 | 922 | impl fmt::Debug for LocalData { | |
@@ -938,7 +935,7 @@ pub(crate) mod _thread { | |||
| 938 | 935 | #[pyclass(with(GetAttr, SetAttr), flags(BASETYPE))] | |
| 939 | 936 | impl Local { | |
| 940 | 937 | fn l_dict(&self, vm: &VirtualMachine) -> PyDictRef { | |
| 941 | - let thread_id = std::thread::current().id(); | ||
| 938 | + let thread_id = current_thread_id(); | ||
| 942 | 939 | ||
| 943 | 940 | // Fast path: check if dict exists under lock | |
| 944 | 941 | if let Some(dict) = self.inner.data.lock().get(&thread_id).cloned() { | |
@@ -974,6 +971,11 @@ pub(crate) mod _thread { | |||
| 974 | 971 | dict | |
| 975 | 972 | } | |
| 976 | 973 | ||
| 974 | + #[pygetset(name = "__dict__")] | ||
| 975 | + fn dict(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyDictRef { | ||
| 976 | + zelf.l_dict(vm) | ||
| 977 | + } | ||
| 978 | + | ||
| 977 | 979 | #[pyslot] | |
| 978 | 980 | fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { | |
| 979 | 981 | Self { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1523,17 +1523,6 @@ impl VirtualMachine { | |||
| 1523 | 1523 | 0 | |
| 1524 | 1524 | } | |
| 1525 | 1525 | ||
| 1526 | - #[cfg(all(feature = "threading", not(miri), not(target_env = "musl")))] | ||
| 1527 | - pub(crate) fn current_c_stack_size() -> usize { | ||
| 1528 | - let (base, top) = Self::get_stack_bounds(); | ||
| 1529 | - top.saturating_sub(base) | ||
| 1530 | - } | ||
| 1531 | - | ||
| 1532 | - #[cfg(all(feature = "threading", any(miri, target_env = "musl")))] | ||
| 1533 | - pub(crate) fn current_c_stack_size() -> usize { | ||
| 1534 | - 8 * 1024 * 1024 | ||
| 1535 | - } | ||
| 1536 | - | ||
| 1537 | 1526 | /// Check if we're near the C stack limit (like _Py_MakeRecCheck). | |
| 1538 | 1527 | /// Returns true only when stack pointer is in the "danger zone" between | |
| 1539 | 1528 | /// soft_limit and hard_limit (soft_limit - 2*margin). | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -557,6 +557,10 @@ impl ThreadedVirtualMachine { | |||
| 557 | 557 | F: FnOnce(&VirtualMachine) -> R, | |
| 558 | 558 | { | |
| 559 | 559 | let vm = &self.vm; | |
| 560 | + // Each spawned thread has its own native stack bounds. Recompute the | ||
| 561 | + // soft limit here instead of inheriting the parent thread's value. | ||
| 562 | + vm.c_stack_soft_limit | ||
| 563 | + .set(VirtualMachine::calculate_c_stack_soft_limit()); | ||
| 560 | 564 | enter_vm(vm, || f(vm)) | |
| 561 | 565 | } | |
| 562 | 566 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,37 +1,71 @@ | |||
| 1 | + import multiprocessing | ||
| 2 | + import os | ||
| 1 | 3 | import threading | |
| 2 | 4 | ||
| 3 | 5 | ||
| 4 | 6 | def import_in_thread(module_name): | |
| 5 | 7 | outcome = {} | |
| 8 | + error = {} | ||
| 6 | 9 | ||
| 7 | 10 | def worker(): | |
| 8 | - module = __import__(module_name, fromlist=["*"]) | ||
| 9 | - outcome["name"] = module.__name__ | ||
| 11 | + try: | ||
| 12 | + module = __import__(module_name, fromlist=["*"]) | ||
| 13 | + outcome["name"] = module.__name__ | ||
| 14 | + except Exception as exc: | ||
| 15 | + error["exc"] = exc | ||
| 10 | 16 | ||
| 11 | 17 | thread = threading.Thread(target=worker) | |
| 12 | 18 | thread.start() | |
| 13 | - thread.join() | ||
| 19 | + thread.join(timeout=5) | ||
| 20 | + assert not thread.is_alive(), "thread did not finish in time" | ||
| 21 | + if "exc" in error: | ||
| 22 | + raise error["exc"] | ||
| 14 | 23 | ||
| 15 | 24 | assert outcome["name"] == module_name | |
| 16 | 25 | ||
| 17 | 26 | ||
| 18 | 27 | def run_exec(code): | |
| 19 | 28 | result = {} | |
| 29 | + error = {} | ||
| 20 | 30 | ||
| 21 | 31 | def worker(): | |
| 22 | - scope = {"__builtins__": __builtins__} | ||
| 23 | - exec(code, scope, scope) | ||
| 24 | - result["scope"] = scope | ||
| 32 | + try: | ||
| 33 | + scope = {"__builtins__": __builtins__} | ||
| 34 | + exec(code, scope, scope) # noqa: S102 - intentional threaded exec regression test | ||
| 35 | + result["scope"] = scope | ||
| 36 | + except Exception as exc: | ||
| 37 | + error["exc"] = exc | ||
| 25 | 38 | ||
| 26 | 39 | thread = threading.Thread(target=worker) | |
| 27 | 40 | thread.start() | |
| 28 | - thread.join() | ||
| 41 | + thread.join(timeout=5) | ||
| 42 | + assert not thread.is_alive(), "thread did not finish in time" | ||
| 43 | + if "exc" in error: | ||
| 44 | + raise error["exc"] | ||
| 29 | 45 | return result["scope"] | |
| 30 | 46 | ||
| 31 | 47 | ||
| 48 | + def child_process(): | ||
| 49 | + return None | ||
| 50 | + | ||
| 51 | + | ||
| 52 | + def start_fork_process_after_thread(): | ||
| 53 | + if not hasattr(os, "fork"): | ||
| 54 | + return | ||
| 55 | + | ||
| 56 | + import_in_thread("multiprocessing.connection") | ||
| 57 | + | ||
| 58 | + ctx = multiprocessing.get_context("fork") | ||
| 59 | + process = ctx.Process(target=child_process) | ||
| 60 | + process.start() | ||
| 61 | + process.join(timeout=10) | ||
| 62 | + assert process.exitcode == 0, process.exitcode | ||
| 63 | + | ||
| 64 | + | ||
| 32 | 65 | import_in_thread("functools") | |
| 33 | 66 | import_in_thread("tempfile") | |
| 34 | 67 | import_in_thread("multiprocessing.connection") | |
| 68 | + start_fork_process_after_thread() | ||
| 35 | 69 | ||
| 36 | 70 | scope = run_exec("import functools") | |
| 37 | 71 | assert scope["functools"].__name__ == "functools" | |
| Back | FazBrowse Home | New Git URL |
0 commit comments