@@ -11,26 +11,35 @@ PyTypeObject *StringScalar_Type = NULL;
1111PyObject *
1212new_stringdtype_instance (PyObject * na_object , int coerce )
1313{
14+ npy_string_allocator * allocator = NULL ;
15+ PyThread_type_lock * allocator_lock = NULL ;
16+ npy_packed_static_string packed_na_name = * NPY_EMPTY_STRING ;
17+ npy_packed_static_string packed_default_string = * NPY_EMPTY_STRING ;
18+
1419 PyObject * new =
1520 PyArrayDescr_Type .tp_new ((PyTypeObject * )& StringDType , NULL , NULL );
1621
1722 if (new == NULL ) {
1823 return NULL ;
1924 }
2025
21- npy_string_allocator * allocator = npy_string_new_allocator (
22- PyMem_RawMalloc , PyMem_RawFree , PyMem_RawRealloc );
26+ allocator = npy_string_new_allocator (PyMem_RawMalloc , PyMem_RawFree ,
27+ PyMem_RawRealloc );
2328
2429 if (allocator == NULL ) {
2530 PyErr_SetString (PyExc_MemoryError ,
2631 "Failed to create string allocator" );
27- return NULL ;
32+ goto fail ;
33+ }
34+
35+ allocator_lock = PyThread_allocate_lock ();
36+ if (allocator_lock == NULL ) {
37+ PyErr_SetString (PyExc_MemoryError , "Unable to allocate thread lock" );
38+ goto fail ;
2839 }
2940
3041 Py_XINCREF (na_object );
3142 ((StringDTypeObject * )new )-> na_object = na_object ;
32- npy_packed_static_string packed_na_name = * NPY_EMPTY_STRING ;
33- npy_packed_static_string packed_default_string = * NPY_EMPTY_STRING ;
3443 int hasnull = na_object != NULL ;
3544 int has_nan_na = 0 ;
3645 int has_string_na = 0 ;
@@ -45,8 +54,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
4554 PyErr_SetString (PyExc_MemoryError ,
4655 "Failed to allocate string while creating "
4756 "StringDType instance." );
48- Py_DECREF (new );
49- return NULL ;
57+ goto fail ;
5058 }
5159 }
5260 else {
@@ -55,8 +63,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
5563 // NaN-like object)
5664 PyObject * eq = PyObject_RichCompare (na_object , na_object , Py_NE );
5765 if (eq == NULL ) {
58- Py_DECREF (new );
59- return NULL ;
66+ goto fail ;
6067 }
6168 int is_truthy = PyObject_IsTrue (na_object );
6269 if (is_truthy == -1 ) {
@@ -70,25 +77,22 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
7077 }
7178 PyObject * na_pystr = PyObject_Str (na_object );
7279 if (na_pystr == NULL ) {
73- Py_DECREF (new );
74- return NULL ;
80+ goto fail ;
7581 }
7682
7783 Py_ssize_t size = 0 ;
7884 const char * utf8_ptr = PyUnicode_AsUTF8AndSize (na_pystr , & size );
7985 if (utf8_ptr == NULL ) {
80- Py_DECREF (new );
8186 Py_DECREF (na_pystr );
82- return NULL ;
87+ goto fail ;
8388 }
8489 if (npy_string_newsize (utf8_ptr , (size_t )size , & packed_na_name ,
8590 allocator ) < 0 ) {
8691 PyErr_SetString (PyExc_MemoryError ,
8792 "Failed to allocate string while creating "
8893 "StringDType instance." );
89- Py_DECREF (new );
9094 Py_DECREF (na_pystr );
91- return NULL ;
95+ goto fail ;
9296 }
9397 Py_DECREF (na_pystr );
9498 }
@@ -100,6 +104,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
100104 snew -> packed_default_string = packed_default_string ;
101105 snew -> packed_na_name = packed_na_name ;
102106 snew -> coerce = coerce ;
107+ snew -> allocator_lock = allocator_lock ;
103108 snew -> allocator = allocator ;
104109 snew -> array_owned = 0 ;
105110
@@ -140,6 +145,19 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
140145 }
141146
142147 return new ;
148+
149+ fail :
150+ // this only makes sense if the allocator isn't attached to new yet
151+ Py_DECREF (new );
152+ if (allocator != NULL ) {
153+ npy_string_free (& packed_na_name , allocator );
154+ npy_string_free (& packed_default_string , allocator );
155+ npy_string_free_allocator (allocator );
156+ }
157+ if (allocator_lock != NULL ) {
158+ PyThread_free_lock (allocator_lock );
159+ }
160+ return NULL ;
143161}
144162
145163// sets the logical rules for determining equality between dtype instances
@@ -268,12 +286,14 @@ stringdtype_setitem(StringDTypeObject *descr, PyObject *obj, char **dataptr)
268286{
269287 npy_packed_static_string * sdata = (npy_packed_static_string * )dataptr ;
270288
289+ NPY_STRING_ACQUIRE_ALLOCATOR (descr );
290+
271291 // free if dataptr holds preexisting string data,
272292 // npy_string_free does a NULL check and checks for small strings
273293 if (npy_string_free (sdata , descr -> allocator ) < 0 ) {
274294 PyErr_SetString (PyExc_MemoryError ,
275295 "String deallocation failed in StringDType setitem" );
276- return -1 ;
296+ goto fail ;
277297 }
278298
279299 // borrow reference
@@ -288,26 +308,33 @@ stringdtype_setitem(StringDTypeObject *descr, PyObject *obj, char **dataptr)
288308 PyObject * val_obj = get_value (obj , descr -> coerce );
289309
290310 if (val_obj == NULL ) {
291- return -1 ;
311+ goto fail ;
292312 }
293313
294314 char * val = NULL ;
295315 Py_ssize_t length = 0 ;
296316 if (PyBytes_AsStringAndSize (val_obj , & val , & length ) == -1 ) {
297317 Py_DECREF (val_obj );
298- return -1 ;
318+ goto fail ;
299319 }
300320
301321 if (npy_string_newsize (val , length , sdata , descr -> allocator ) < 0 ) {
302322 PyErr_SetString (PyExc_MemoryError ,
303323 "Failed to allocate string during StringDType "
304324 "setitem" );
305325 Py_DECREF (val_obj );
306- return -1 ;
326+ goto fail ;
307327 }
308328 }
309329
330+ NPY_STRING_RELEASE_ALLOCATOR (descr );
331+
310332 return 0 ;
333+
334+ fail :
335+ NPY_STRING_RELEASE_ALLOCATOR (descr );
336+
337+ return -1 ;
311338}
312339
313340static PyObject *
@@ -317,12 +344,13 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
317344 npy_packed_static_string * psdata = (npy_packed_static_string * )dataptr ;
318345 npy_static_string sdata = {0 , NULL };
319346 int hasnull = descr -> na_object != NULL ;
347+ NPY_STRING_ACQUIRE_ALLOCATOR (descr );
320348 int is_null = npy_string_load (descr -> allocator , psdata , & sdata );
321349
322350 if (is_null < 0 ) {
323351 PyErr_SetString (PyExc_MemoryError ,
324352 "Failed to load string in StringDType getitem" );
325- return NULL ;
353+ goto fail ;
326354 }
327355 else if (is_null == 1 ) {
328356 if (hasnull ) {
@@ -331,16 +359,19 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
331359 val_obj = na_object ;
332360 }
333361 else {
362+ // cannot fail
334363 val_obj = PyUnicode_FromStringAndSize ("" , 0 );
335364 }
336365 }
337366 else {
338367 val_obj = PyUnicode_FromStringAndSize (sdata .buf , sdata .size );
339368 if (val_obj == NULL ) {
340- return NULL ;
369+ goto fail ;
341370 }
342371 }
343372
373+ NPY_STRING_RELEASE_ALLOCATOR (descr );
374+
344375 /*
345376 * In principle we should return a StringScalar instance here, but
346377 * creating a StringScalar via PyObject_CallFunctionObjArgs has
@@ -353,6 +384,12 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
353384 * eventually the scalar type for this dtype will be str.
354385 */
355386 return val_obj ;
387+
388+ fail :
389+
390+ NPY_STRING_RELEASE_ALLOCATOR (descr );
391+
392+ return NULL ;
356393}
357394
358395// PyArray_NonzeroFunc
369406compare (void * a , void * b , void * arr )
370407{
371408 StringDTypeObject * descr = (StringDTypeObject * )PyArray_DESCR (arr );
372- return _compare (a , b , descr , descr );
409+ NPY_STRING_ACQUIRE_ALLOCATOR (descr );
410+ int ret = _compare (a , b , descr , descr );
411+ NPY_STRING_RELEASE_ALLOCATOR (descr );
412+ return ret ;
373413}
374414
375415int
@@ -480,20 +520,25 @@ stringdtype_clear_loop(void *NPY_UNUSED(traverse_context),
480520 npy_intp stride , NpyAuxData * NPY_UNUSED (auxdata ))
481521{
482522 StringDTypeObject * sdescr = (StringDTypeObject * )descr ;
523+ NPY_STRING_ACQUIRE_ALLOCATOR (sdescr );
483524 while (size -- ) {
484525 npy_packed_static_string * sdata = (npy_packed_static_string * )data ;
485526 if (data != NULL ) {
486527 if (npy_string_free (sdata , sdescr -> allocator ) < 0 ) {
487528 gil_error (PyExc_MemoryError ,
488529 "String deallocation failed in clear loop" );
489- return -1 ;
530+ goto fail ;
490531 }
491532 memset (data , 0 , sizeof (npy_packed_static_string ));
492533 }
493534 data += stride ;
494535 }
495-
536+ NPY_STRING_RELEASE_ALLOCATOR ( sdescr );
496537 return 0 ;
538+
539+ fail :
540+ NPY_STRING_RELEASE_ALLOCATOR (sdescr );
541+ return -1 ;
497542}
498543
499544static int
@@ -652,9 +697,14 @@ static void
652697stringdtype_dealloc (StringDTypeObject * self )
653698{
654699 Py_XDECREF (self -> na_object );
655- npy_string_free (& self -> packed_default_string , self -> allocator );
656- npy_string_free (& self -> packed_na_name , self -> allocator );
657- npy_string_free_allocator (self -> allocator );
700+ // this can be null if an error happens while initializing an instance
701+ if (self -> allocator != NULL ) {
702+ // can we assume the destructor for an instance will only get called
703+ // inside of one C thread?
704+ npy_string_free (& self -> packed_default_string , self -> allocator );
705+ npy_string_free (& self -> packed_na_name , self -> allocator );
706+ npy_string_free_allocator (self -> allocator );
707+ }
658708 PyArrayDescr_Type .tp_dealloc ((PyObject * )self );
659709}
660710
0 commit comments