Skip to content

Commit 4ae6d7e

Browse files
authored
Merge branch 'main' into vfazio-thread_get_ident
2 parents 63812b5 + ad053d8 commit 4ae6d7e

21 files changed

+895
-989
lines changed

Doc/c-api/type.rst

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ Type Objects
8282
error (e.g. no more watcher IDs available), return ``-1`` and set an
8383
exception.
8484
85+
In free-threaded builds, :c:func:`PyType_AddWatcher` is not thread-safe,
86+
so it must be called at start up (before spawning the first thread).
87+
8588
.. versionadded:: 3.12
8689
8790

Lib/test/test_generated_cases.py

-9
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,6 @@ def test_error_if_plain_with_comment(self):
457457
if (cond) {
458458
JUMP_TO_LABEL(label);
459459
}
460-
// Comment is ok
461460
DISPATCH();
462461
}
463462
"""
@@ -586,7 +585,6 @@ def test_suppress_dispatch(self):
586585
587586
LABEL(somewhere)
588587
{
589-
590588
}
591589
"""
592590
self.run_cases_test(input, output)
@@ -1351,7 +1349,6 @@ def test_pop_on_error_peeks(self):
13511349
}
13521350
// THIRD
13531351
{
1354-
// Mark j and k as used
13551352
if (cond) {
13561353
JUMP_TO_LABEL(pop_2_error);
13571354
}
@@ -1757,17 +1754,14 @@ def test_complex_label(self):
17571754
output = """
17581755
LABEL(other_label)
17591756
{
1760-
17611757
}
17621758
17631759
LABEL(other_label2)
17641760
{
1765-
17661761
}
17671762
17681763
LABEL(my_label)
17691764
{
1770-
// Comment
17711765
_PyFrame_SetStackPointer(frame, stack_pointer);
17721766
do_thing();
17731767
stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -1795,7 +1789,6 @@ def test_spilled_label(self):
17951789
output = """
17961790
LABEL(one)
17971791
{
1798-
/* STACK SPILLED */
17991792
stack_pointer = _PyFrame_GetStackPointer(frame);
18001793
JUMP_TO_LABEL(two);
18011794
}
@@ -1851,7 +1844,6 @@ def test_multiple_labels(self):
18511844
output = """
18521845
LABEL(my_label_1)
18531846
{
1854-
// Comment
18551847
_PyFrame_SetStackPointer(frame, stack_pointer);
18561848
do_thing1();
18571849
stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -1860,7 +1852,6 @@ def test_multiple_labels(self):
18601852
18611853
LABEL(my_label_2)
18621854
{
1863-
// Comment
18641855
_PyFrame_SetStackPointer(frame, stack_pointer);
18651856
do_thing2();
18661857
stack_pointer = _PyFrame_GetStackPointer(frame);

Lib/test/test_threading.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2145,7 +2145,7 @@ def test_set_name(self):
21452145
if os_helper.TESTFN_UNENCODABLE:
21462146
tests.append(os_helper.TESTFN_UNENCODABLE)
21472147

2148-
if sys.platform.startswith("solaris"):
2148+
if sys.platform.startswith("sunos"):
21492149
encoding = "utf-8"
21502150
else:
21512151
encoding = sys.getfilesystemencoding()
@@ -2161,7 +2161,7 @@ def work():
21612161
encoded = encoded.split(b'\0', 1)[0]
21622162
if truncate is not None:
21632163
encoded = encoded[:truncate]
2164-
if sys.platform.startswith("solaris"):
2164+
if sys.platform.startswith("sunos"):
21652165
expected = encoded.decode("utf-8", "surrogateescape")
21662166
else:
21672167
expected = os.fsdecode(encoded)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix several thread-safety issues in :mod:`ctypes` on the :term:`free
2+
threaded <free threading>` build.

Modules/_ctypes/_ctypes.c

+81-16
Original file line numberDiff line numberDiff line change
@@ -5326,13 +5326,13 @@ static PyType_Spec pycsimple_spec = {
53265326
PyCPointer_Type
53275327
*/
53285328
static PyObject *
5329-
Pointer_item(PyObject *myself, Py_ssize_t index)
5329+
Pointer_item_lock_held(PyObject *myself, Py_ssize_t index)
53305330
{
53315331
CDataObject *self = _CDataObject_CAST(myself);
53325332
Py_ssize_t size;
53335333
Py_ssize_t offset;
53345334
PyObject *proto;
5335-
void *deref = locked_deref(self);
5335+
void *deref = *(void **)self->b_ptr;
53365336

53375337
if (deref == NULL) {
53385338
PyErr_SetString(PyExc_ValueError,
@@ -5364,8 +5364,23 @@ Pointer_item(PyObject *myself, Py_ssize_t index)
53645364
index, size, (char *)((char *)deref + offset));
53655365
}
53665366

5367+
static PyObject *
5368+
Pointer_item(PyObject *myself, Py_ssize_t index)
5369+
{
5370+
CDataObject *self = _CDataObject_CAST(myself);
5371+
PyObject *res;
5372+
// TODO: The plan is to make LOCK_PTR() a mutex instead of a critical
5373+
// section someday, so when that happens, this needs to get refactored
5374+
// to be re-entrant safe.
5375+
// This goes for all the locks here.
5376+
LOCK_PTR(self);
5377+
res = Pointer_item_lock_held(myself, index);
5378+
UNLOCK_PTR(self);
5379+
return res;
5380+
}
5381+
53675382
static int
5368-
Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
5383+
Pointer_ass_item_lock_held(PyObject *myself, Py_ssize_t index, PyObject *value)
53695384
{
53705385
CDataObject *self = _CDataObject_CAST(myself);
53715386
Py_ssize_t size;
@@ -5378,7 +5393,7 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
53785393
return -1;
53795394
}
53805395

5381-
void *deref = locked_deref(self);
5396+
void *deref = *(void **)self->b_ptr;
53825397
if (deref == NULL) {
53835398
PyErr_SetString(PyExc_ValueError,
53845399
"NULL pointer access");
@@ -5409,10 +5424,21 @@ Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
54095424
index, size, ((char *)deref + offset));
54105425
}
54115426

5427+
static int
5428+
Pointer_ass_item(PyObject *myself, Py_ssize_t index, PyObject *value)
5429+
{
5430+
CDataObject *self = _CDataObject_CAST(myself);
5431+
int res;
5432+
LOCK_PTR(self);
5433+
res = Pointer_ass_item_lock_held(myself, index, value);
5434+
UNLOCK_PTR(self);
5435+
return res;
5436+
}
5437+
54125438
static PyObject *
5413-
Pointer_get_contents(PyObject *self, void *closure)
5439+
Pointer_get_contents_lock_held(PyObject *self, void *closure)
54145440
{
5415-
void *deref = locked_deref(_CDataObject_CAST(self));
5441+
void *deref = *(void **)_CDataObject_CAST(self)->b_ptr;
54165442
if (deref == NULL) {
54175443
PyErr_SetString(PyExc_ValueError,
54185444
"NULL pointer access");
@@ -5429,6 +5455,17 @@ Pointer_get_contents(PyObject *self, void *closure)
54295455
return PyCData_FromBaseObj(st, stginfo->proto, self, 0, deref);
54305456
}
54315457

5458+
static PyObject *
5459+
Pointer_get_contents(PyObject *myself, void *closure)
5460+
{
5461+
CDataObject *self = _CDataObject_CAST(myself);
5462+
PyObject *res;
5463+
LOCK_PTR(self);
5464+
res = Pointer_get_contents_lock_held(myself, closure);
5465+
UNLOCK_PTR(self);
5466+
return res;
5467+
}
5468+
54325469
static int
54335470
Pointer_set_contents(PyObject *op, PyObject *value, void *closure)
54345471
{
@@ -5462,7 +5499,15 @@ Pointer_set_contents(PyObject *op, PyObject *value, void *closure)
54625499
}
54635500

54645501
dst = (CDataObject *)value;
5465-
locked_deref_assign(self, dst->b_ptr);
5502+
if (dst != self) {
5503+
LOCK_PTR(dst);
5504+
locked_deref_assign(self, dst->b_ptr);
5505+
UNLOCK_PTR(dst);
5506+
} else {
5507+
LOCK_PTR(self);
5508+
*((void **)self->b_ptr) = dst->b_ptr;
5509+
UNLOCK_PTR(self);
5510+
}
54665511

54675512
/*
54685513
A Pointer instance must keep the value it points to alive. So, a
@@ -5514,6 +5559,23 @@ Pointer_new(PyTypeObject *type, PyObject *args, PyObject *kw)
55145559
return generic_pycdata_new(st, type, args, kw);
55155560
}
55165561

5562+
static int
5563+
copy_pointer_to_list_lock_held(PyObject *myself, PyObject *np, Py_ssize_t len,
5564+
Py_ssize_t start, Py_ssize_t step)
5565+
{
5566+
Py_ssize_t i;
5567+
size_t cur;
5568+
for (cur = start, i = 0; i < len; cur += step, i++) {
5569+
PyObject *v = Pointer_item_lock_held(myself, cur);
5570+
if (!v) {
5571+
return -1;
5572+
}
5573+
PyList_SET_ITEM(np, i, v);
5574+
}
5575+
5576+
return 0;
5577+
}
5578+
55175579
static PyObject *
55185580
Pointer_subscript(PyObject *myself, PyObject *item)
55195581
{
@@ -5595,14 +5657,14 @@ Pointer_subscript(PyObject *myself, PyObject *item)
55955657
}
55965658
assert(iteminfo);
55975659
if (iteminfo->getfunc == _ctypes_get_fielddesc("c")->getfunc) {
5598-
char *ptr = locked_deref(self);
55995660
char *dest;
56005661

56015662
if (len <= 0)
56025663
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
56035664
if (step == 1) {
56045665
PyObject *res;
56055666
LOCK_PTR(self);
5667+
char *ptr = *(void **)self->b_ptr;
56065668
res = PyBytes_FromStringAndSize(ptr + start,
56075669
len);
56085670
UNLOCK_PTR(self);
@@ -5612,6 +5674,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
56125674
if (dest == NULL)
56135675
return PyErr_NoMemory();
56145676
LOCK_PTR(self);
5677+
char *ptr = *(void **)self->b_ptr;
56155678
for (cur = start, i = 0; i < len; cur += step, i++) {
56165679
dest[i] = ptr[cur];
56175680
}
@@ -5621,14 +5684,14 @@ Pointer_subscript(PyObject *myself, PyObject *item)
56215684
return np;
56225685
}
56235686
if (iteminfo->getfunc == _ctypes_get_fielddesc("u")->getfunc) {
5624-
wchar_t *ptr = locked_deref(self);
56255687
wchar_t *dest;
56265688

56275689
if (len <= 0)
56285690
return Py_GetConstant(Py_CONSTANT_EMPTY_STR);
56295691
if (step == 1) {
56305692
PyObject *res;
56315693
LOCK_PTR(self);
5694+
wchar_t *ptr = *(wchar_t **)self->b_ptr;
56325695
res = PyUnicode_FromWideChar(ptr + start,
56335696
len);
56345697
UNLOCK_PTR(self);
@@ -5638,6 +5701,7 @@ Pointer_subscript(PyObject *myself, PyObject *item)
56385701
if (dest == NULL)
56395702
return PyErr_NoMemory();
56405703
LOCK_PTR(self);
5704+
wchar_t *ptr = *(wchar_t **)self->b_ptr;
56415705
for (cur = start, i = 0; i < len; cur += step, i++) {
56425706
dest[i] = ptr[cur];
56435707
}
@@ -5651,14 +5715,15 @@ Pointer_subscript(PyObject *myself, PyObject *item)
56515715
if (np == NULL)
56525716
return NULL;
56535717

5654-
for (cur = start, i = 0; i < len; cur += step, i++) {
5655-
PyObject *v = Pointer_item(myself, cur);
5656-
if (!v) {
5657-
Py_DECREF(np);
5658-
return NULL;
5659-
}
5660-
PyList_SET_ITEM(np, i, v);
5718+
int res;
5719+
LOCK_PTR(self);
5720+
res = copy_pointer_to_list_lock_held(myself, np, len, start, step);
5721+
UNLOCK_PTR(self);
5722+
if (res < 0) {
5723+
Py_DECREF(np);
5724+
return NULL;
56615725
}
5726+
56625727
return np;
56635728
}
56645729
else {

Objects/setobject.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -793,12 +793,12 @@ frozenset_hash(PyObject *self)
793793
PySetObject *so = _PySet_CAST(self);
794794
Py_uhash_t hash;
795795

796-
if (so->hash != -1) {
797-
return so->hash;
796+
if (FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash) != -1) {
797+
return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash);
798798
}
799799

800800
hash = frozenset_hash_impl(self);
801-
so->hash = hash;
801+
FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, hash);
802802
return hash;
803803
}
804804

Python/bytecodes.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ dummy_func(
163163
op(_CHECK_PERIODIC_IF_NOT_YIELD_FROM, (--)) {
164164
if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
165165
_Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
166-
QSBR_QUIESCENT_STATE(tstate); \
166+
QSBR_QUIESCENT_STATE(tstate);
167167
if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
168168
int err = _Py_HandlePending(tstate);
169169
ERROR_IF(err != 0, error);
@@ -2245,7 +2245,8 @@ dummy_func(
22452245
PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr);
22462246
DEOPT_IF(attr_o == NULL);
22472247
#ifdef Py_GIL_DISABLED
2248-
if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
2248+
int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
2249+
if (!increfed) {
22492250
DEOPT_IF(true);
22502251
}
22512252
#else
@@ -2322,7 +2323,8 @@ dummy_func(
23222323
}
23232324
STAT_INC(LOAD_ATTR, hit);
23242325
#ifdef Py_GIL_DISABLED
2325-
if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
2326+
int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
2327+
if (!increfed) {
23262328
DEOPT_IF(true);
23272329
}
23282330
#else

0 commit comments

Comments
 (0)