Skip to content

Commit 504e741

Browse files
make instance creation thread safe
1 parent 6fb5f7f commit 504e741

4 files changed

Lines changed: 70 additions & 20 deletions

File tree

Include/internal/pycore_pyatomic_ft_wrappers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ extern "C" {
2020
#endif
2121

2222
#ifdef Py_GIL_DISABLED
23+
#define FT_ATOMIC_LOAD_INT(value) _Py_atomic_load_int(&value)
24+
#define FT_ATOMIC_STORE_INT(value, new_value) _Py_atomic_store_int(&value, new_value)
2325
#define FT_ATOMIC_LOAD_PTR(value) _Py_atomic_load_ptr(&value)
2426
#define FT_ATOMIC_STORE_PTR(value, new_value) _Py_atomic_store_ptr(&value, new_value)
2527
#define FT_ATOMIC_LOAD_SSIZE(value) _Py_atomic_load_ssize(&value)
@@ -111,6 +113,8 @@ extern "C" {
111113
_Py_atomic_load_ullong_relaxed(&value)
112114

113115
#else
116+
#define FT_ATOMIC_LOAD_INT(value) value
117+
#define FT_ATOMIC_STORE_INT(value, new_value) value = new_value
114118
#define FT_ATOMIC_LOAD_PTR(value) value
115119
#define FT_ATOMIC_STORE_PTR(value, new_value) value = new_value
116120
#define FT_ATOMIC_LOAD_SSIZE(value) value

Modules/_ctypes/_ctypes.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ bytes(cdata)
109109
#include "pycore_call.h" // _PyObject_CallNoArgs()
110110
#include "pycore_ceval.h" // _Py_EnterRecursiveCall()
111111
#include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString()
112+
#include "pycore_pyatomic_ft_wrappers.h"
112113
#ifdef MS_WIN32
113114
# include "pycore_modsupport.h" // _PyArg_NoKeywords()
114115
#endif
@@ -710,13 +711,16 @@ StructUnionType_init(PyObject *self, PyObject *args, PyObject *kwds, int isStruc
710711
if (baseinfo == NULL) {
711712
return 0;
712713
}
713-
714+
int ret = 0;
715+
STGINFO_LOCK2(info, baseinfo);
714716
/* copy base info */
715-
if (PyCStgInfo_clone(info, baseinfo) < 0) {
716-
return -1;
717+
ret = PyCStgInfo_clone(info, baseinfo);
718+
if (ret >= 0) {
719+
stginfo_set_dict_final(info);
720+
stginfo_set_dict_final(baseinfo);
717721
}
718-
info->flags &= ~DICTFLAG_FINAL; /* clear the 'final' flag in the subclass info */
719-
baseinfo->flags |= DICTFLAG_FINAL; /* set the 'final' flag in the baseclass info */
722+
STGINFO_UNLOCK2();
723+
return ret;
720724
}
721725
return 0;
722726
}
@@ -3178,11 +3182,11 @@ PyCData_FromBaseObj(ctypes_state *st,
31783182
return NULL;
31793183
}
31803184

3181-
info->flags |= DICTFLAG_FINAL;
31823185
cmem = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
31833186
if (cmem == NULL) {
31843187
return NULL;
31853188
}
3189+
STGINFO_LOCK(info);
31863190
assert(CDataObject_Check(st, cmem));
31873191
cmem->b_length = info->length;
31883192
cmem->b_size = info->size;
@@ -3194,12 +3198,15 @@ PyCData_FromBaseObj(ctypes_state *st,
31943198
cmem->b_index = index;
31953199
} else { /* copy contents of adr */
31963200
if (-1 == PyCData_MallocBuffer(cmem, info)) {
3197-
Py_DECREF(cmem);
3198-
return NULL;
3201+
Py_CLEAR(cmem);
3202+
goto exit;
31993203
}
32003204
memcpy(cmem->b_ptr, adr, info->size);
32013205
cmem->b_index = index;
32023206
}
3207+
exit:;
3208+
stginfo_set_dict_final(info);
3209+
STGINFO_UNLOCK();
32033210
return (PyObject *)cmem;
32043211
}
32053212

@@ -3226,8 +3233,11 @@ PyCData_AtAddress(ctypes_state *st, PyObject *type, void *buf)
32263233
"abstract class");
32273234
return NULL;
32283235
}
3229-
3230-
info->flags |= DICTFLAG_FINAL;
3236+
if (stginfo_get_dict_final(info) != 1) {
3237+
STGINFO_LOCK(info);
3238+
stginfo_set_dict_final(info);
3239+
STGINFO_UNLOCK();
3240+
}
32313241

32323242
pd = (CDataObject *)((PyTypeObject *)type)->tp_alloc((PyTypeObject *)type, 0);
32333243
if (!pd) {
@@ -3461,8 +3471,11 @@ generic_pycdata_new(ctypes_state *st,
34613471
"abstract class");
34623472
return NULL;
34633473
}
3464-
3465-
info->flags |= DICTFLAG_FINAL;
3474+
if (stginfo_get_dict_final(info) != 1) {
3475+
STGINFO_LOCK(info);
3476+
stginfo_set_dict_final(info);
3477+
STGINFO_UNLOCK();
3478+
}
34663479

34673480
obj = (CDataObject *)type->tp_alloc(type, 0);
34683481
if (!obj)

Modules/_ctypes/ctypes.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "pycore_moduleobject.h" // _PyModule_GetState()
88
#include "pycore_typeobject.h" // _PyType_GetModuleState()
9+
#include "pycore_critical_section.h"
10+
#include "pycore_pyatomic_ft_wrappers.h"
911

1012
// Do we support C99 complex types in ffi?
1113
// For Apple's libffi, this must be determined at runtime (see gh-128156).
@@ -373,6 +375,9 @@ typedef struct CFieldObject {
373375
*****************************************************************/
374376

375377
typedef struct {
378+
#ifdef Py_GIL_DISABLED
379+
PyMutex mutex;
380+
#endif
376381
int initialized;
377382
Py_ssize_t size; /* number of bytes */
378383
Py_ssize_t align; /* alignment requirements */
@@ -390,6 +395,7 @@ typedef struct {
390395
PyObject *checker;
391396
PyObject *module;
392397
int flags; /* calling convention and such */
398+
int dict_final;
393399

394400
/* pep3118 fields, pointers need PyMem_Free */
395401
char *format;
@@ -399,6 +405,26 @@ typedef struct {
399405
/* Py_ssize_t *suboffsets; */ /* unused in ctypes */
400406
} StgInfo;
401407

408+
409+
#define STGINFO_LOCK(stginfo) Py_BEGIN_CRITICAL_SECTION_MUT(&(stginfo)->mutex)
410+
#define STGINFO_LOCK2(stginfo1, stginfo2) Py_BEGIN_CRITICAL_SECTION2_MUT(&(stginfo1)->mutex, &(stginfo2)->mutex)
411+
#define STGINFO_UNLOCK() Py_END_CRITICAL_SECTION()
412+
#define STGINFO_UNLOCK2() Py_END_CRITICAL_SECTION2()
413+
414+
static inline void
415+
stginfo_set_dict_final(StgInfo *info)
416+
{
417+
_Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(&info->mutex);
418+
FT_ATOMIC_STORE_INT(info->dict_final, 1);
419+
}
420+
421+
static inline int
422+
stginfo_get_dict_final(StgInfo *info)
423+
{
424+
return FT_ATOMIC_LOAD_INT(info->dict_final);
425+
}
426+
427+
402428
extern int PyCStgInfo_clone(StgInfo *dst_info, StgInfo *src_info);
403429
extern void ctype_clear_stginfo(StgInfo *info);
404430

@@ -427,8 +453,6 @@ PyObject *_ctypes_callproc(ctypes_state *st,
427453
#define TYPEFLAG_ISPOINTER 0x100
428454
#define TYPEFLAG_HASPOINTER 0x200
429455

430-
#define DICTFLAG_FINAL 0x1000
431-
432456
struct tagPyCArgObject {
433457
PyObject_HEAD
434458
ffi_type *pffi_type;

Modules/_ctypes/stgdict.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,14 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
264264
/* If this structure/union is already marked final we cannot assign
265265
_fields_ anymore. */
266266

267-
if (stginfo->flags & DICTFLAG_FINAL) {/* is final ? */
267+
if (stginfo_get_dict_final(stginfo) == 1) {/* is final ? */
268+
PyErr_SetString(PyExc_AttributeError,
269+
"_fields_ is final");
270+
goto error;
271+
}
272+
STGINFO_LOCK(stginfo);
273+
// check again after locking
274+
if (stginfo_get_dict_final(stginfo) == 1) {/* is final ? */
268275
PyErr_SetString(PyExc_AttributeError,
269276
"_fields_ is final");
270277
goto error;
@@ -422,12 +429,13 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
422429
goto error;
423430
}
424431
assert(info);
425-
432+
STGINFO_LOCK(info);
426433
stginfo->ffi_type_pointer.elements[ffi_ofs + i] = &info->ffi_type_pointer;
427434
if (info->flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER))
428435
stginfo->flags |= TYPEFLAG_HASPOINTER;
429-
info->flags |= DICTFLAG_FINAL; /* mark field type final */
430436

437+
stginfo_set_dict_final(info); /* mark field type final */
438+
STGINFO_UNLOCK();
431439
if (-1 == PyObject_SetAttr(type, prop->name, prop_obj)) {
432440
goto error;
433441
}
@@ -461,15 +469,15 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
461469

462470
/* We did check that this flag was NOT set above, it must not
463471
have been set until now. */
464-
if (stginfo->flags & DICTFLAG_FINAL) {
472+
if (stginfo_get_dict_final(stginfo) == 1) {
465473
PyErr_SetString(PyExc_AttributeError,
466474
"Structure or union cannot contain itself");
467475
goto error;
468476
}
469-
stginfo->flags |= DICTFLAG_FINAL;
477+
stginfo_set_dict_final(stginfo);
470478

471479
retval = MakeAnonFields(type);
472-
error:
480+
error:;
473481
Py_XDECREF(layout_func);
474482
Py_XDECREF(kwnames);
475483
Py_XDECREF(align_obj);
@@ -478,6 +486,7 @@ PyCStructUnionType_update_stginfo(PyObject *type, PyObject *fields, int isStruct
478486
Py_XDECREF(layout_fields);
479487
Py_XDECREF(layout);
480488
Py_XDECREF(format_spec_obj);
489+
STGINFO_UNLOCK();
481490
return retval;
482491
}
483492

0 commit comments

Comments
 (0)