From 675bad714584939e688145ea5e0a66548695595b Mon Sep 17 00:00:00 2001 From: Julian Taylor Date: Mon, 17 Sep 2018 19:09:27 +0200 Subject: [PATCH] BUG: fix cached allocations without the GIL There currently only is a global cache for small allocations so the functions must be called while the GIL is held. Ensure this by checking the GIL state in debug mode (which is tested via a ci configuration). Closes gh-11942 --- numpy/core/src/multiarray/alloc.c | 9 +++++++++ numpy/core/src/multiarray/item_selection.c | 22 +++++++++++----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/numpy/core/src/multiarray/alloc.c b/numpy/core/src/multiarray/alloc.c index f8305d1150a4..97935f171e24 100644 --- a/numpy/core/src/multiarray/alloc.c +++ b/numpy/core/src/multiarray/alloc.c @@ -35,6 +35,13 @@ typedef struct { static cache_bucket datacache[NBUCKETS]; static cache_bucket dimcache[NBUCKETS_DIM]; +/* as the cache is managed in global variables verify the GIL is held */ +#if defined(NPY_PY3K) +#define NPY_CHECK_GIL_HELD() PyGILState_Check() +#else +#define NPY_CHECK_GIL_HELD() 1 +#endif + /* * very simplistic small memory block cache to avoid more expensive libc * allocations @@ -47,6 +54,7 @@ _npy_alloc_cache(npy_uintp nelem, npy_uintp esz, npy_uint msz, { assert((esz == 1 && cache == datacache) || (esz == sizeof(npy_intp) && cache == dimcache)); + assert(NPY_CHECK_GIL_HELD()); if (nelem < msz) { if (cache[nelem].available > 0) { return cache[nelem].ptrs[--(cache[nelem].available)]; @@ -75,6 +83,7 @@ static NPY_INLINE void _npy_free_cache(void * p, npy_uintp nelem, npy_uint msz, cache_bucket * cache, void (*dealloc)(void *)) { + assert(NPY_CHECK_GIL_HELD()); if (p != NULL && nelem < msz) { if (cache[nelem].available < NCACHE) { cache[nelem].ptrs[cache[nelem].available++] = p; diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index 486eb43ce4e5..c6dfcfdf5127 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -832,8 +832,6 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, } size = it->size; - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); - if (needcopy) { buffer = npy_alloc_cache(N * elsize); if (buffer == NULL) { @@ -842,6 +840,8 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, } } + NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); + while (size--) { char *bufptr = it->dataptr; @@ -916,8 +916,8 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort, } fail: - npy_free_cache(buffer, N * elsize); NPY_END_THREADS_DESCR(PyArray_DESCR(op)); + npy_free_cache(buffer, N * elsize); if (ret < 0 && !PyErr_Occurred()) { /* Out of memory during sorting or buffer creation */ PyErr_NoMemory(); @@ -977,8 +977,6 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } size = it->size; - NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); - if (needcopy) { valbuffer = npy_alloc_cache(N * elsize); if (valbuffer == NULL) { @@ -995,6 +993,8 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } } + NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op)); + while (size--) { char *valptr = it->dataptr; npy_intp *idxptr = (npy_intp *)rit->dataptr; @@ -1078,9 +1078,9 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort, } fail: + NPY_END_THREADS_DESCR(PyArray_DESCR(op)); npy_free_cache(valbuffer, N * elsize); npy_free_cache(idxbuffer, N * sizeof(npy_intp)); - NPY_END_THREADS_DESCR(PyArray_DESCR(op)); if (ret < 0) { if (!PyErr_Occurred()) { /* Out of memory during sorting or buffer creation */ @@ -1495,13 +1495,13 @@ PyArray_LexSort(PyObject *sort_keys, int axis) char *valbuffer, *indbuffer; int *swaps; - valbuffer = npy_alloc_cache(N * maxelsize); + valbuffer = PyDataMem_NEW(N * maxelsize); if (valbuffer == NULL) { goto fail; } - indbuffer = npy_alloc_cache(N * sizeof(npy_intp)); + indbuffer = PyDataMem_NEW(N * sizeof(npy_intp)); if (indbuffer == NULL) { - npy_free_cache(indbuffer, N * sizeof(npy_intp)); + PyDataMem_FREE(indbuffer); goto fail; } swaps = malloc(n*sizeof(int)); @@ -1544,8 +1544,8 @@ PyArray_LexSort(PyObject *sort_keys, int axis) sizeof(npy_intp), N, sizeof(npy_intp)); PyArray_ITER_NEXT(rit); } - npy_free_cache(valbuffer, N * maxelsize); - npy_free_cache(indbuffer, N * sizeof(npy_intp)); + PyDataMem_FREE(valbuffer); + PyDataMem_FREE(indbuffer); free(swaps); } else {