@@ -11,26 +11,35 @@ PyTypeObject *StringScalar_Type = NULL;
11
11
PyObject *
12
12
new_stringdtype_instance (PyObject * na_object , int coerce )
13
13
{
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
+
14
19
PyObject * new =
15
20
PyArrayDescr_Type .tp_new ((PyTypeObject * )& StringDType , NULL , NULL );
16
21
17
22
if (new == NULL ) {
18
23
return NULL ;
19
24
}
20
25
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 );
23
28
24
29
if (allocator == NULL ) {
25
30
PyErr_SetString (PyExc_MemoryError ,
26
31
"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 ;
28
39
}
29
40
30
41
Py_XINCREF (na_object );
31
42
((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 ;
34
43
int hasnull = na_object != NULL ;
35
44
int has_nan_na = 0 ;
36
45
int has_string_na = 0 ;
@@ -45,8 +54,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
45
54
PyErr_SetString (PyExc_MemoryError ,
46
55
"Failed to allocate string while creating "
47
56
"StringDType instance." );
48
- Py_DECREF (new );
49
- return NULL ;
57
+ goto fail ;
50
58
}
51
59
}
52
60
else {
@@ -55,8 +63,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
55
63
// NaN-like object)
56
64
PyObject * eq = PyObject_RichCompare (na_object , na_object , Py_NE );
57
65
if (eq == NULL ) {
58
- Py_DECREF (new );
59
- return NULL ;
66
+ goto fail ;
60
67
}
61
68
int is_truthy = PyObject_IsTrue (na_object );
62
69
if (is_truthy == -1 ) {
@@ -70,25 +77,22 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
70
77
}
71
78
PyObject * na_pystr = PyObject_Str (na_object );
72
79
if (na_pystr == NULL ) {
73
- Py_DECREF (new );
74
- return NULL ;
80
+ goto fail ;
75
81
}
76
82
77
83
Py_ssize_t size = 0 ;
78
84
const char * utf8_ptr = PyUnicode_AsUTF8AndSize (na_pystr , & size );
79
85
if (utf8_ptr == NULL ) {
80
- Py_DECREF (new );
81
86
Py_DECREF (na_pystr );
82
- return NULL ;
87
+ goto fail ;
83
88
}
84
89
if (npy_string_newsize (utf8_ptr , (size_t )size , & packed_na_name ,
85
90
allocator ) < 0 ) {
86
91
PyErr_SetString (PyExc_MemoryError ,
87
92
"Failed to allocate string while creating "
88
93
"StringDType instance." );
89
- Py_DECREF (new );
90
94
Py_DECREF (na_pystr );
91
- return NULL ;
95
+ goto fail ;
92
96
}
93
97
Py_DECREF (na_pystr );
94
98
}
@@ -100,6 +104,7 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
100
104
snew -> packed_default_string = packed_default_string ;
101
105
snew -> packed_na_name = packed_na_name ;
102
106
snew -> coerce = coerce ;
107
+ snew -> allocator_lock = allocator_lock ;
103
108
snew -> allocator = allocator ;
104
109
snew -> array_owned = 0 ;
105
110
@@ -140,6 +145,19 @@ new_stringdtype_instance(PyObject *na_object, int coerce)
140
145
}
141
146
142
147
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 ;
143
161
}
144
162
145
163
// sets the logical rules for determining equality between dtype instances
@@ -268,12 +286,14 @@ stringdtype_setitem(StringDTypeObject *descr, PyObject *obj, char **dataptr)
268
286
{
269
287
npy_packed_static_string * sdata = (npy_packed_static_string * )dataptr ;
270
288
289
+ NPY_STRING_ACQUIRE_ALLOCATOR (descr );
290
+
271
291
// free if dataptr holds preexisting string data,
272
292
// npy_string_free does a NULL check and checks for small strings
273
293
if (npy_string_free (sdata , descr -> allocator ) < 0 ) {
274
294
PyErr_SetString (PyExc_MemoryError ,
275
295
"String deallocation failed in StringDType setitem" );
276
- return -1 ;
296
+ goto fail ;
277
297
}
278
298
279
299
// borrow reference
@@ -288,26 +308,33 @@ stringdtype_setitem(StringDTypeObject *descr, PyObject *obj, char **dataptr)
288
308
PyObject * val_obj = get_value (obj , descr -> coerce );
289
309
290
310
if (val_obj == NULL ) {
291
- return -1 ;
311
+ goto fail ;
292
312
}
293
313
294
314
char * val = NULL ;
295
315
Py_ssize_t length = 0 ;
296
316
if (PyBytes_AsStringAndSize (val_obj , & val , & length ) == -1 ) {
297
317
Py_DECREF (val_obj );
298
- return -1 ;
318
+ goto fail ;
299
319
}
300
320
301
321
if (npy_string_newsize (val , length , sdata , descr -> allocator ) < 0 ) {
302
322
PyErr_SetString (PyExc_MemoryError ,
303
323
"Failed to allocate string during StringDType "
304
324
"setitem" );
305
325
Py_DECREF (val_obj );
306
- return -1 ;
326
+ goto fail ;
307
327
}
308
328
}
309
329
330
+ NPY_STRING_RELEASE_ALLOCATOR (descr );
331
+
310
332
return 0 ;
333
+
334
+ fail :
335
+ NPY_STRING_RELEASE_ALLOCATOR (descr );
336
+
337
+ return -1 ;
311
338
}
312
339
313
340
static PyObject *
@@ -317,12 +344,13 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
317
344
npy_packed_static_string * psdata = (npy_packed_static_string * )dataptr ;
318
345
npy_static_string sdata = {0 , NULL };
319
346
int hasnull = descr -> na_object != NULL ;
347
+ NPY_STRING_ACQUIRE_ALLOCATOR (descr );
320
348
int is_null = npy_string_load (descr -> allocator , psdata , & sdata );
321
349
322
350
if (is_null < 0 ) {
323
351
PyErr_SetString (PyExc_MemoryError ,
324
352
"Failed to load string in StringDType getitem" );
325
- return NULL ;
353
+ goto fail ;
326
354
}
327
355
else if (is_null == 1 ) {
328
356
if (hasnull ) {
@@ -331,16 +359,19 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
331
359
val_obj = na_object ;
332
360
}
333
361
else {
362
+ // cannot fail
334
363
val_obj = PyUnicode_FromStringAndSize ("" , 0 );
335
364
}
336
365
}
337
366
else {
338
367
val_obj = PyUnicode_FromStringAndSize (sdata .buf , sdata .size );
339
368
if (val_obj == NULL ) {
340
- return NULL ;
369
+ goto fail ;
341
370
}
342
371
}
343
372
373
+ NPY_STRING_RELEASE_ALLOCATOR (descr );
374
+
344
375
/*
345
376
* In principle we should return a StringScalar instance here, but
346
377
* creating a StringScalar via PyObject_CallFunctionObjArgs has
@@ -353,6 +384,12 @@ stringdtype_getitem(StringDTypeObject *descr, char **dataptr)
353
384
* eventually the scalar type for this dtype will be str.
354
385
*/
355
386
return val_obj ;
387
+
388
+ fail :
389
+
390
+ NPY_STRING_RELEASE_ALLOCATOR (descr );
391
+
392
+ return NULL ;
356
393
}
357
394
358
395
// PyArray_NonzeroFunc
369
406
compare (void * a , void * b , void * arr )
370
407
{
371
408
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 ;
373
413
}
374
414
375
415
int
@@ -480,20 +520,25 @@ stringdtype_clear_loop(void *NPY_UNUSED(traverse_context),
480
520
npy_intp stride , NpyAuxData * NPY_UNUSED (auxdata ))
481
521
{
482
522
StringDTypeObject * sdescr = (StringDTypeObject * )descr ;
523
+ NPY_STRING_ACQUIRE_ALLOCATOR (sdescr );
483
524
while (size -- ) {
484
525
npy_packed_static_string * sdata = (npy_packed_static_string * )data ;
485
526
if (data != NULL ) {
486
527
if (npy_string_free (sdata , sdescr -> allocator ) < 0 ) {
487
528
gil_error (PyExc_MemoryError ,
488
529
"String deallocation failed in clear loop" );
489
- return -1 ;
530
+ goto fail ;
490
531
}
491
532
memset (data , 0 , sizeof (npy_packed_static_string ));
492
533
}
493
534
data += stride ;
494
535
}
495
-
536
+ NPY_STRING_RELEASE_ALLOCATOR ( sdescr );
496
537
return 0 ;
538
+
539
+ fail :
540
+ NPY_STRING_RELEASE_ALLOCATOR (sdescr );
541
+ return -1 ;
497
542
}
498
543
499
544
static int
@@ -652,9 +697,14 @@ static void
652
697
stringdtype_dealloc (StringDTypeObject * self )
653
698
{
654
699
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
+ }
658
708
PyArrayDescr_Type .tp_dealloc ((PyObject * )self );
659
709
}
660
710
0 commit comments