summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/src/multiarray/buffer.c30
-rw-r--r--numpy/core/tests/test_multiarray.py15
2 files changed, 36 insertions, 9 deletions
diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c
index 770813e70..6415dc276 100644
--- a/numpy/core/src/multiarray/buffer.c
+++ b/numpy/core/src/multiarray/buffer.c
@@ -625,22 +625,34 @@ _buffer_get_info(PyObject *obj, npy_bool f_contiguous)
item_list = PyDict_GetItem(_buffer_info_cache, key);
if (item_list != NULL) {
+ Py_ssize_t item_list_length = PyList_GET_SIZE(item_list);
Py_INCREF(item_list);
- if (PyList_GET_SIZE(item_list) > 0) {
- item = PyList_GetItem(item_list, PyList_GET_SIZE(item_list) - 1);
+ if (item_list_length > 0) {
+ item = PyList_GetItem(item_list, item_list_length - 1);
old_info = (_buffer_info_t*)PyLong_AsVoidPtr(item);
- /*
- * TODO: In rare cases when an array is both C- and
- * F-contiguous, and buffers are exported alternating
- * we may always have a cache miss even though the correct
- * info had already been exported before (causing a memory
- * leak).
- */
if (_buffer_info_cmp(info, old_info) == 0) {
_buffer_info_free(info);
info = old_info;
}
+ else if (item_list_length > 1 && info->ndim > 1) {
+ /*
+ * Some arrays are C- and F-contiguous and if they have more
+ * than one dimension, the buffer-info may differ between the
+ * two due to RELAXED_STRIDES_CHECKING.
+ * If we export both buffers, the first stored one may be the
+ * one for the other contiguity, so check both in this case.
+ * This is generally very unlikely in all other cases, since
+ * in all other cases the first one will match unless array
+ * metadata was modified in-place (which is discouraged).
+ */
+ item = PyList_GetItem(item_list, item_list_length - 2);
+ old_info = (_buffer_info_t*)PyLong_AsVoidPtr(item);
+ if (_buffer_info_cmp(info, old_info) == 0) {
+ _buffer_info_free(info);
+ info = old_info;
+ }
+ }
}
}
else {
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index d376fe6b7..c80d0c073 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -7206,6 +7206,21 @@ class TestNewBufferProtocol:
arr, ['C_CONTIGUOUS'])
assert_(strides[-1] == 8)
+ @pytest.mark.valgrind_error(reason="leaks buffer info cache temporarily.")
+ @pytest.mark.skipif(not np.ones((10, 1), order="C").flags.f_contiguous,
+ reason="Test is unnecessary (but fails) without relaxed strides.")
+ def test_relaxed_strides_buffer_info_leak(self, arr=np.ones((1, 10))):
+ """Test that alternating export of C- and F-order buffers from
+ an array which is both C- and F-order when relaxed strides is
+ active works.
+ This test defines array in the signature to ensure leaking more
+ references every time the test is run (catching the leak with
+ pytest-leaks).
+ """
+ for i in range(10):
+ _multiarray_tests.get_buffer_info(arr, ['F_CONTIGUOUS'])
+ _multiarray_tests.get_buffer_info(arr, ['C_CONTIGUOUS'])
+
def test_out_of_order_fields(self):
dt = np.dtype(dict(
formats=['<i4', '<i4'],