Skip to content

Commit 2f71c50

Browse files
address review
1 parent de9bbf6 commit 2f71c50

3 files changed

Lines changed: 32 additions & 54 deletions

File tree

Lib/test/test_xml_etree_c.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# xml.etree test for cElementTree
22
import io
33
import struct
4+
import time
45
from test import support
56
from test.support.import_helper import import_fresh_module
67
from test.support import threading_helper
78
import types
89
import unittest
9-
import threading
1010

1111
cET = import_fresh_module('xml.etree.ElementTree',
1212
fresh=['_elementtree'])
@@ -258,18 +258,19 @@ def test_element_with_children(self):
258258
self.check_sizeof(e, self.elementsize + self.extra +
259259
struct.calcsize('8P'))
260260

261+
261262
@unittest.skipUnless(cET, 'requires _elementtree')
262263
@threading_helper.requires_working_threading()
263264
class TestElementTreeFreeThreading(unittest.TestCase):
264265
def test_element_concurrent_clear_and_access(self):
265-
#Race len(), .attrib, and .clear() to verify fix for gh-149861.
266+
# Race len(), .attrib, and .clear() to verify fix for gh-149861.
266267
root = cET.Element('root')
267268
children = [cET.Element(f'child-{i}') for i in range(5)]
268269

269-
stop_event = threading.Event()
270+
end_time = time.monotonic() + 1.0
270271

271272
def reader_task():
272-
while not stop_event.is_set():
273+
while time.monotonic() < end_time:
273274
len(root)
274275
try:
275276
_ = root.attrib
@@ -279,23 +280,19 @@ def reader_task():
279280
pass
280281

281282
def writer_task():
282-
while not stop_event.is_set():
283+
while time.monotonic() < end_time:
283284
# Test element_add_subelement / extend
284285
root.extend(children)
285286
# Test clear_extra
286287
root.clear()
287288

288289
threads = []
289290
for _ in range(4):
290-
threads.append(threading.Thread(target=reader_task))
291+
threads.append(reader_task)
291292
for _ in range(2):
292-
threads.append(threading.Thread(target=writer_task))
293-
294-
with threading_helper.start_threads(threads):
295-
import time
296-
time.sleep(1.0)
297-
stop_event.set()
293+
threads.append(writer_task)
298294

295+
threading_helper.run_concurrently(worker_func=threads, nthreads=6)
299296

300297

301298
def install_tests():
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
Fixes race conditions in :mod:`xml.etree.ElementTree` where the ``extra``
2-
pointer dereference and ``attrib`` access was unsynchronized . This prevents
2+
pointer dereference and ``attrib`` access was unsynchronized. This prevents
33
potential crashes (core dumps) in the free-threading build caused by
44
concurrent modification of an element's internal structure.

Modules/_elementtree.c

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -271,31 +271,20 @@ typedef struct {
271271
LOCAL(int)
272272
create_extra(ElementObject* self, PyObject* attrib)
273273
{
274-
int res;
275-
Py_BEGIN_CRITICAL_SECTION(self);
276-
277-
if (self->extra != NULL) {
278-
res = 0;
279-
goto end;
280-
}
281-
ElementObjectExtra* extra = PyMem_Malloc(sizeof(ElementObjectExtra));
282-
if (!extra) {
274+
self->extra = PyMem_Malloc(sizeof(ElementObjectExtra));
275+
if (!self->extra) {
283276
PyErr_NoMemory();
284-
res = -1;
285-
goto end;
277+
return -1;
286278
}
279+
Py_BEGIN_CRITICAL_SECTION(self);
280+
self->extra->attrib = Py_XNewRef(attrib);
287281

288-
extra->attrib = Py_XNewRef(attrib);
289-
290-
extra->length = 0;
291-
extra->allocated = STATIC_CHILDREN;
292-
extra->children = extra->_children;
293-
self->extra = extra;
294-
res = 0;
282+
self->extra->length = 0;
283+
self->extra->allocated = STATIC_CHILDREN;
284+
self->extra->children = self->extra->_children;
295285

296-
end: ;
297286
Py_END_CRITICAL_SECTION();
298-
return res;
287+
return 0;
299288
}
300289

301290
LOCAL(void)
@@ -575,23 +564,15 @@ element_get_attrib(ElementObject* self)
575564
/* return new reference to attrib dictionary */
576565
/* note: this function assumes that the extra section exists */
577566

578-
PyObject *res = NULL;
567+
PyObject *res;
579568
Py_BEGIN_CRITICAL_SECTION(self);
580-
if (self->extra == NULL) {
581-
PyErr_SetString(PyExc_AttributeError, "extra section does not exist");
582-
goto end;
583-
}
584569
res = self->extra->attrib;
585570

586571
if (!res) {
587572
/* create missing dictionary */
588-
res = PyDict_New();
589-
if (res) {
590-
self->extra->attrib = res;
591-
}
573+
res = self->extra->attrib = PyDict_New();
592574
}
593575
Py_XINCREF(res);
594-
end: ;
595576
Py_END_CRITICAL_SECTION();
596577
return res;
597578
}
@@ -709,7 +690,6 @@ static int
709690
element_gc_clear(PyObject *op)
710691
{
711692
ElementObject *self = _Element_CAST(op);
712-
Py_BEGIN_CRITICAL_SECTION(self);
713693
Py_CLEAR(self->tag);
714694
_clear_joined_ptr(&self->text);
715695
_clear_joined_ptr(&self->tail);
@@ -718,7 +698,6 @@ element_gc_clear(PyObject *op)
718698
* so fully deallocate it.
719699
*/
720700
clear_extra(self);
721-
Py_END_CRITICAL_SECTION();
722701
return 0;
723702
}
724703

@@ -1655,9 +1634,12 @@ static Py_ssize_t
16551634
element_length(PyObject *op)
16561635
{
16571636
ElementObject *self = _Element_CAST(op);
1658-
Py_ssize_t res = 0;
1637+
Py_ssize_t res;
16591638
Py_BEGIN_CRITICAL_SECTION(self);
1660-
if (self->extra) {
1639+
if (!self->extra) {
1640+
res = 0;
1641+
}
1642+
else {
16611643
res = self->extra->length;
16621644
}
16631645
Py_END_CRITICAL_SECTION();
@@ -2114,16 +2096,15 @@ element_tail_getter(PyObject *op, void *closure)
21142096
static PyObject*
21152097
element_attrib_getter(PyObject *op, void *closure)
21162098
{
2117-
PyObject *res = NULL;
2099+
PyObject *res;
21182100
ElementObject *self = _Element_CAST(op);
21192101
Py_BEGIN_CRITICAL_SECTION(self);
2120-
if (!self->extra) {
2121-
if (create_extra(self, NULL) < 0)
2122-
goto end;
2102+
if (!self->extra && create_extra(self, NULL) < 0){
2103+
res = NULL;
2104+
}
2105+
else {
2106+
res = element_get_attrib(self);
21232107
}
2124-
res = element_get_attrib(self);
2125-
2126-
end: ;
21272108
Py_END_CRITICAL_SECTION();
21282109
return res;
21292110
}

0 commit comments

Comments
 (0)