From b8eb6545b5db4048799e337766246026ac695dcb Mon Sep 17 00:00:00 2001
From: Stefan Behnel <stefan_ml@behnel.de>
Date: Mon, 26 Oct 2015 20:30:56 +0100
Subject: [PATCH] reuse pre-allocated thread locks in critical memoryview
 creation path

---
 Cython/Utility/MemoryView.pyx | 41 +++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/Cython/Utility/MemoryView.pyx b/Cython/Utility/MemoryView.pyx
index 27bc11f67..f6e7ee811 100644
--- a/Cython/Utility/MemoryView.pyx
+++ b/Cython/Utility/MemoryView.pyx
@@ -300,6 +300,24 @@ cdef void *align_pointer(void *memory, size_t alignment) nogil:
 
     return <void *> aligned_p
 
+
+# pre-allocate thread locks for reuse
+## note that this could be implemented in a more beautiful way in "normal" Cython,
+## but this code gets merged into the user module and not everything works there.
+DEF THREAD_LOCKS_PREALLOCATED = 8
+cdef int _thread_locks_used = 0
+cdef PyThread_type_lock[THREAD_LOCKS_PREALLOCATED] _thread_locks = [
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+    PyThread_allocate_lock(),
+]
+
+
 @cname('__pyx_memoryview')
 cdef class memoryview(object):
 
@@ -325,9 +343,14 @@ cdef class memoryview(object):
                 (<__pyx_buffer *> &self.view).obj = Py_None
                 Py_INCREF(Py_None)
 
-        self.lock = PyThread_allocate_lock()
-        if self.lock == NULL:
-            raise MemoryError
+        global _thread_locks_used
+        if _thread_locks_used < THREAD_LOCKS_PREALLOCATED:
+            self.lock = _thread_locks[_thread_locks_used]
+            _thread_locks_used += 1
+        if self.lock is NULL:
+            self.lock = PyThread_allocate_lock()
+            if self.lock is NULL:
+                raise MemoryError
 
         if flags & PyBUF_FORMAT:
             self.dtype_is_object = (self.view.format[0] == b'O' and self.view.format[1] == b'\0')
@@ -342,8 +365,18 @@ cdef class memoryview(object):
         if self.obj is not None:
             __Pyx_ReleaseBuffer(&self.view)
 
+        cdef int i
+        global _thread_locks_used
         if self.lock != NULL:
-            PyThread_free_lock(self.lock)
+            for i in range(_thread_locks_used):
+                if _thread_locks[i] is self.lock:
+                    _thread_locks_used -= 1
+                    if i != _thread_locks_used:
+                        _thread_locks[i], _thread_locks[_thread_locks_used] = (
+                            _thread_locks[_thread_locks_used], _thread_locks[i])
+                    break
+            else:
+                PyThread_free_lock(self.lock)
 
     cdef char *get_item_pointer(memoryview self, object index) except NULL:
         cdef Py_ssize_t dim
-- 
2.30.9