Skip to content

Commit 5da9eb7

Browse files
Use PyWeakref_GetRef and critical section in BlockValuesRefs (#60540)
* Use PyWeakref_GetRef and critical section in BlockValuesRefs * Add conditional compilation for critical sections * run pre-commit * Address feedback; move cdata in meson.build * Early return in case of error --------- Co-authored-by: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com>
1 parent f13cd4c commit 5da9eb7

File tree

3 files changed

+72
-14
lines changed

3 files changed

+72
-14
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Autogenerated file containing Cython compile-time defines
2+
3+
DEF CYTHON_COMPATIBLE_WITH_FREE_THREADING = @freethreading_compatible@

pandas/_libs/internals.pyx

+56-14
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@ cimport cython
44
from cpython.object cimport PyObject
55
from cpython.pyport cimport PY_SSIZE_T_MAX
66
from cpython.slice cimport PySlice_GetIndicesEx
7-
from cpython.weakref cimport (
8-
PyWeakref_GetObject,
9-
PyWeakref_NewRef,
10-
)
7+
from cpython.weakref cimport PyWeakref_NewRef
118
from cython cimport Py_ssize_t
129

1310
import numpy as np
@@ -29,6 +26,14 @@ from pandas._libs.util cimport (
2926
is_integer_object,
3027
)
3128

29+
include "free_threading_config.pxi"
30+
31+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
32+
from cpython.ref cimport Py_DECREF
33+
from cpython.weakref cimport PyWeakref_GetRef
34+
ELSE:
35+
from cpython.weakref cimport PyWeakref_GetObject
36+
3237

3338
cdef extern from "Python.h":
3439
PyObject* Py_None
@@ -908,17 +913,37 @@ cdef class BlockValuesRefs:
908913
# if force=False. Clearing for every insertion causes slowdowns if
909914
# all these objects stay alive, e.g. df.items() for wide DataFrames
910915
# see GH#55245 and GH#55008
916+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
917+
cdef PyObject* pobj
918+
cdef bint status
919+
911920
if force or len(self.referenced_blocks) > self.clear_counter:
912-
self.referenced_blocks = [
913-
ref for ref in self.referenced_blocks
914-
if PyWeakref_GetObject(ref) != Py_None
915-
]
921+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
922+
new_referenced_blocks = []
923+
for ref in self.referenced_blocks:
924+
status = PyWeakref_GetRef(ref, &pobj)
925+
if status == -1:
926+
return
927+
elif status == 1:
928+
new_referenced_blocks.append(ref)
929+
Py_DECREF(<object>pobj)
930+
self.referenced_blocks = new_referenced_blocks
931+
ELSE:
932+
self.referenced_blocks = [
933+
ref for ref in self.referenced_blocks
934+
if PyWeakref_GetObject(ref) != Py_None
935+
]
936+
916937
nr_of_refs = len(self.referenced_blocks)
917938
if nr_of_refs < self.clear_counter // 2:
918939
self.clear_counter = max(self.clear_counter // 2, 500)
919940
elif nr_of_refs > self.clear_counter:
920941
self.clear_counter = max(self.clear_counter * 2, nr_of_refs)
921942

943+
cpdef _add_reference_maybe_locked(self, Block blk):
944+
self._clear_dead_references()
945+
self.referenced_blocks.append(PyWeakref_NewRef(blk, None))
946+
922947
cpdef add_reference(self, Block blk):
923948
"""Adds a new reference to our reference collection.
924949
@@ -927,8 +952,15 @@ cdef class BlockValuesRefs:
927952
blk : Block
928953
The block that the new references should point to.
929954
"""
955+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
956+
with cython.critical_section(self):
957+
self._add_reference_maybe_locked(blk)
958+
ELSE:
959+
self._add_reference_maybe_locked(blk)
960+
961+
def _add_index_reference_maybe_locked(self, index: object) -> None:
930962
self._clear_dead_references()
931-
self.referenced_blocks.append(PyWeakref_NewRef(blk, None))
963+
self.referenced_blocks.append(PyWeakref_NewRef(index, None))
932964

933965
def add_index_reference(self, index: object) -> None:
934966
"""Adds a new reference to our reference collection when creating an index.
@@ -938,8 +970,16 @@ cdef class BlockValuesRefs:
938970
index : Index
939971
The index that the new reference should point to.
940972
"""
941-
self._clear_dead_references()
942-
self.referenced_blocks.append(PyWeakref_NewRef(index, None))
973+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
974+
with cython.critical_section(self):
975+
self._add_index_reference_maybe_locked(index)
976+
ELSE:
977+
self._add_index_reference_maybe_locked(index)
978+
979+
def _has_reference_maybe_locked(self) -> bool:
980+
self._clear_dead_references(force=True)
981+
# Checking for more references than block pointing to itself
982+
return len(self.referenced_blocks) > 1
943983

944984
def has_reference(self) -> bool:
945985
"""Checks if block has foreign references.
@@ -951,6 +991,8 @@ cdef class BlockValuesRefs:
951991
-------
952992
bool
953993
"""
954-
self._clear_dead_references(force=True)
955-
# Checking for more references than block pointing to itself
956-
return len(self.referenced_blocks) > 1
994+
IF CYTHON_COMPATIBLE_WITH_FREE_THREADING:
995+
with cython.critical_section(self):
996+
return self._has_reference_maybe_locked()
997+
ELSE:
998+
return self._has_reference_maybe_locked()

pandas/_libs/meson.build

+13
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,19 @@ _khash_primitive_helper_dep = declare_dependency(
5050
sources: _khash_primitive_helper,
5151
)
5252

53+
cdata = configuration_data()
54+
if cy.version().version_compare('>=3.1.0')
55+
cdata.set('freethreading_compatible', '1')
56+
else
57+
cdata.set('freethreading_compatible', '0')
58+
endif
59+
_free_threading_config = configure_file(
60+
input: 'free_threading_config.pxi.in',
61+
output: 'free_threading_config.pxi',
62+
configuration: cdata,
63+
install: false,
64+
)
65+
5366
subdir('tslibs')
5467

5568
libs_sources = {

0 commit comments

Comments
 (0)