mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Align del behavior (#6772)
* slot_del * refcount inc_by for atomicity * temp patch multithreading * apply review
This commit is contained in:
1 parent
cdd47b6a59
commit
ba8749b792
8 files changed
+30
-78
No files matched your search
@@ -108,12 +108,7 @@ env:
|
||||
test_yield_from
|
||||
ENV_POLLUTING_TESTS_COMMON: >-
|
||||
ENV_POLLUTING_TESTS_LINUX: >-
|
||||
test.test_multiprocessing_fork.test_processes
|
||||
test.test_multiprocessing_forkserver.test_processes
|
||||
test.test_multiprocessing_spawn.test_processes
|
||||
ENV_POLLUTING_TESTS_MACOS: >-
|
||||
test.test_multiprocessing_forkserver.test_processes
|
||||
test.test_multiprocessing_spawn.test_processes
|
||||
ENV_POLLUTING_TESTS_WINDOWS: >-
|
||||
# Python version targeted by the CI.
|
||||
PYTHON_VERSION: "3.14.2"
|
||||
|
||||
Vendored
+4
-2
@@ -6592,7 +6592,8 @@ class BaseMixin(object):
|
||||
# cycles. Trigger a garbage collection to break these cycles.
|
||||
test.support.gc_collect()
|
||||
|
||||
processes = set(multiprocessing.process._dangling) - set(cls.dangling[0])
|
||||
# TODO: RUSTPYTHON: Filter out stopped processes since gc.collect() is a no-op
|
||||
processes = {p for p in multiprocessing.process._dangling if p.is_alive()} - {p for p in cls.dangling[0] if p.is_alive()}
|
||||
if processes:
|
||||
test.support.environment_altered = True
|
||||
support.print_warning(f'Dangling processes: {processes}')
|
||||
@@ -6789,7 +6790,8 @@ def install_tests_in_module_dict(remote_globs, start_method,
|
||||
|
||||
multiprocessing.set_start_method(old_start_method[0], force=True)
|
||||
# pause a bit so we don't get warning about dangling threads/processes
|
||||
processes = set(multiprocessing.process._dangling) - set(dangling[0])
|
||||
# TODO: RUSTPYTHON: Filter out stopped processes since gc.collect() is a no-op
|
||||
processes = {p for p in multiprocessing.process._dangling if p.is_alive()} - {p for p in dangling[0] if p.is_alive()}
|
||||
if processes:
|
||||
need_sleep = True
|
||||
test.support.environment_altered = True
|
||||
|
||||
@@ -22,23 +22,6 @@ class WithProcessesTestProcess(WithProcessesTestProcess): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == 'linux', 'TODO: RUSTPYTHON flaky timeout')
|
||||
def test_process(self): super().test_process() # TODO: RUSTPYTHON
|
||||
|
||||
class WithProcessesTestPool(WithProcessesTestPool): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform == 'linux' and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_async_timeout(self): super().test_async_timeout() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform == 'linux' and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_terminate(self): super().test_terminate() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform == 'linux' and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_traceback(self): super().test_traceback() # TODO: RUSTPYTHON
|
||||
|
||||
class WithProcessesTestPoolWorkerLifetime(WithProcessesTestPoolWorkerLifetime): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(sys.platform == 'linux', 'TODO: RUSTPYTHON flaky timeout')
|
||||
def test_pool_worker_lifetime(self): super().test_pool_worker_lifetime() # TODO: RUSTPYTHON
|
||||
|
||||
@@ -11,33 +11,11 @@ class WithProcessesTestCondition(WithProcessesTestCondition): # TODO: RUSTPYTHO
|
||||
def test_notify_n(self): super().test_notify_n()
|
||||
|
||||
class WithProcessesTestLock(WithProcessesTestLock): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_repr_lock(self): super().test_repr_lock() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform == 'linux', # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON flaky BrokenPipeError, flaky ConnectionRefusedError, flaky ConnectionResetError, flaky EOFError'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_repr_rlock(self): super().test_repr_rlock() # TODO: RUSTPYTHON
|
||||
|
||||
class WithProcessesTestPool(WithProcessesTestPool): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_async_timeout(self): super().test_async_timeout() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_terminate(self): super().test_terminate() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_traceback(self): super().test_traceback() # TODO: RUSTPYTHON
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -3,13 +3,5 @@ from test._test_multiprocessing import install_tests_in_module_dict
|
||||
|
||||
install_tests_in_module_dict(globals(), 'forkserver', only_type="threads")
|
||||
|
||||
import os # TODO: RUSTPYTHON
|
||||
class WithThreadsTestPool(WithThreadsTestPool): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_terminate(self): super().test_terminate() # TODO: RUSTPYTHON
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -9,33 +9,11 @@ class WithProcessesTestCondition(WithProcessesTestCondition): # TODO: RUSTPYTHO
|
||||
def test_notify(self): super().test_notify()
|
||||
|
||||
class WithProcessesTestLock(WithProcessesTestLock): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform in ('darwin', 'linux') and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_repr_lock(self): super().test_repr_lock() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform == 'linux', # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON flaky BrokenPipeError, flaky ConnectionRefusedError, flaky ConnectionResetError, flaky EOFError'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_repr_rlock(self): super().test_repr_rlock() # TODO: RUSTPYTHON
|
||||
|
||||
class WithProcessesTestPool(WithProcessesTestPool): # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform in ('darwin', 'linux') and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_async_timeout(self): super().test_async_timeout() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform in ('darwin', 'linux') and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_terminate(self): super().test_terminate() # TODO: RUSTPYTHON
|
||||
@unittest.skipIf( # TODO: RUSTPYTHON
|
||||
sys.platform in ('darwin', 'linux') and 'RUSTPYTHON_SKIP_ENV_POLLUTERS' in os.environ, # TODO: RUSTPYTHON
|
||||
'TODO: RUSTPYTHON environment pollution when running rustpython -m test --fail-env-changed due to unknown reason'
|
||||
) # TODO: RUSTPYTHON
|
||||
def test_traceback(self): super().test_traceback() # TODO: RUSTPYTHON
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -40,6 +40,16 @@ impl RefCount {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inc_by(&self, n: usize) {
|
||||
debug_assert!(n <= Self::MASK);
|
||||
let old_size = self.strong.fetch_add(n, Relaxed);
|
||||
|
||||
if old_size & Self::MASK > Self::MASK - n {
|
||||
std::process::abort();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if successful
|
||||
#[inline]
|
||||
pub fn safe_inc(&self) -> bool {
|
||||
|
||||
@@ -220,6 +220,12 @@ impl WeakRefList {
|
||||
}))
|
||||
});
|
||||
let mut inner = unsafe { inner_ptr.as_ref().lock() };
|
||||
// If obj was cleared but object is still alive (e.g., new weakref
|
||||
// created during __del__), restore the obj pointer
|
||||
if inner.obj.is_none() {
|
||||
inner.obj = Some(NonNull::from(obj));
|
||||
inner.ref_count += 1;
|
||||
}
|
||||
if is_generic && let Some(generic_weakref) = inner.generic_weakref {
|
||||
let generic_weakref = unsafe { generic_weakref.as_ref() };
|
||||
if generic_weakref.0.ref_count.get() != 0 {
|
||||
@@ -835,15 +841,23 @@ impl PyObject {
|
||||
slot_del: fn(&PyObject, &VirtualMachine) -> PyResult<()>,
|
||||
) -> Result<(), ()> {
|
||||
let ret = crate::vm::thread::with_vm(zelf, |vm| {
|
||||
zelf.0.ref_count.inc();
|
||||
// Temporarily resurrect (0→2) so ref_count stays positive
|
||||
// during __del__, preventing safe_inc from seeing 0.
|
||||
zelf.0.ref_count.inc_by(2);
|
||||
|
||||
if let Err(e) = slot_del(zelf, vm) {
|
||||
let del_method = zelf.get_class_attr(identifier!(vm, __del__)).unwrap();
|
||||
vm.run_unraisable(e, None, del_method);
|
||||
}
|
||||
|
||||
// Undo the temporary resurrection. Always remove both
|
||||
// temporary refs; the second dec returns true only when
|
||||
// ref_count drops to 0 (no resurrection).
|
||||
zelf.0.ref_count.dec();
|
||||
zelf.0.ref_count.dec()
|
||||
});
|
||||
match ret {
|
||||
// the decref right above set ref_count back to 0
|
||||
// the decref set ref_count back to 0
|
||||
Some(true) => Ok(()),
|
||||
// we've been resurrected by __del__
|
||||
Some(false) => Err(()),
|
||||
|
||||
Reference in new issue
Block a user