Permalink
Browse files

badly cythonified the context stack manager

  • Loading branch information...
mitsuhiko committed Sep 9, 2010
1 parent 58a05c3 commit 15dade7fe59e8a481fb5463d80cb6666301912a6
Showing with 94 additions and 1 deletion.
  1. +3 −1 Makefile
  2. +90 −0 logbook/_speedups.pyx
  3. +1 −0 logbook/base.py
View
@@ -12,9 +12,11 @@ upload-docs:
make -C docs html SPHINXOPTS=-Aonline=1
python setup.py upload_docs
-cybuild:
+logbook/_speedups.so: logbook/_speedups.pyx
cython logbook/_speedups.pyx
python setup.py build
cp build/*/logbook/_speedups.so logbook
+cybuild: logbook/_speedups.so
+
.PHONY: test upload-docs clean-pyc cybuild all
View
@@ -9,8 +9,21 @@
:license: BSD, see LICENSE for more details.
"""
+import thread
+import threading
+
+from cpython.dict cimport PyDict_Clear, PyDict_SetItem
+from cpython.list cimport PyList_Append, PyList_Sort, PyList_GET_SIZE
+from cpython.pythread cimport PyThread_type_lock, PyThread_allocate_lock, \
+ PyThread_release_lock, PyThread_acquire_lock, WAIT_LOCK
+
cdef object _missing = object()
+cdef enum:
+ _MAX_CONTEXT_OBJECT_CACHE = 256
+
+cdef current_thread = thread.get_ident
+
cdef class group_reflected_property:
cdef char* name
@@ -40,3 +53,80 @@ cdef class group_reflected_property:
def __del__(self, obj):
delattr(obj, self._name)
+
+
+cdef class _StackItem:
+ cdef int id
+ cdef readonly object val
+
+ def __init__(self, int id, object val):
+ self.id = id
+ self.val = val
+
+ def __cmp__(self, _StackItem other):
+ return cmp(other.id, self.id)
+
+
+cdef class ContextStackManager:
+ cdef list _global
+ cdef PyThread_type_lock _context_lock
+ cdef object _context
+ cdef dict _cache
+ cdef int _stackcnt
+
+ def __init__(self):
+ self._global = []
+ self._context_lock = PyThread_allocate_lock()
+ self._context = threading.local()
+ self._cache = {}
+ self._stackcnt = 0
+
+ cdef _stackop(self):
+ self._stackcnt += 1
+ return self._stackcnt
+
+ cpdef iter_context_objects(self):
+ tid = current_thread()
+ objects = self._cache.get(tid)
+ if objects is None:
+ if PyList_GET_SIZE(self._cache) > _MAX_CONTEXT_OBJECT_CACHE:
+ PyDict_Clear(self._cache)
+ objects = self._global[:]
+ objects.extend(getattr3(self._context, 'stack', ()))
+ PyList_Sort(objects)
+ objects = [(<_StackItem>x).val for x in objects]
+ PyDict_SetItem(self._cache, tid, objects)
+ return iter(objects)
+
+ cpdef push_thread(self, obj):
+ PyThread_acquire_lock(self._context_lock, WAIT_LOCK)
+ try:
+ self._cache.pop(current_thread(), None)
+ item = _StackItem(self._stackop(), obj)
+ stack = getattr3(self._context, 'stack', None)
+ if stack is None:
+ self._context.stack = [item]
+ else:
+ PyList_Append(stack, item)
+ finally:
+ PyThread_release_lock(self._context_lock)
+
+ cpdef pop_thread(self):
+ PyThread_acquire_lock(self._context_lock, WAIT_LOCK)
+ try:
+ self._cache.pop(current_thread(), None)
+ stack = getattr3(self._context, 'stack', None)
+ assert stack, 'no objects on stack'
+ return (<_StackItem>stack.pop()).val
+ finally:
+ PyThread_release_lock(self._context_lock)
+
+ cpdef push_application(self, obj):
+ self._global.append(_StackItem(self._stackop(), obj))
+ PyDict_Clear(self._cache)
+
+ cpdef pop_application(self):
+ assert self._global, 'no objects on application stack'
+ popped = (<_StackItem>self._global.pop()).val
+ PyDict_Clear(self._cache)
+ return popped
View
@@ -90,6 +90,7 @@ def lookup_level(level):
from logbook._speedups import group_reflected_property, \
ContextStackManager
except ImportError:
+ raise
from logbook._fallback import group_reflected_property, \
ContextStackManager

0 comments on commit 15dade7

Please sign in to comment.