I was thinking, maybe instead we can add a feature to cffi "don't
release the GIL" and use that there? (it would be faster for example)

On Wed, May 6, 2015 at 9:39 PM, arigo <nore...@buildbot.pypy.org> wrote:
> Author: Armin Rigo <ar...@tunes.org>
> Branch:
> Changeset: r77171:df44050e8e33
> Date: 2015-05-06 21:39 +0200
> http://bitbucket.org/pypy/pypy/changeset/df44050e8e33/
>
> Log:    The gdbm library is not thread-safe. Add a global lock.
>
> diff --git a/lib_pypy/gdbm.py b/lib_pypy/gdbm.py
> --- a/lib_pypy/gdbm.py
> +++ b/lib_pypy/gdbm.py
> @@ -1,4 +1,6 @@
>  import cffi, os, sys
> +import thread
> +_lock = thread.allocate_lock()
>
>  ffi = cffi.FFI()
>  ffi.cdef('''
> @@ -40,6 +42,7 @@
>
>  try:
>      verify_code = '''
> +    #include <stdlib.h>
>      #include "gdbm.h"
>
>      static datum pygdbm_fetch(GDBM_FILE gdbm_file, char *dptr, int dsize) {
> @@ -86,102 +89,121 @@
>      return {'dptr': ffi.new("char[]", key), 'dsize': len(key)}
>
>  class gdbm(object):
> -    ll_dbm = None
> +    __ll_dbm = None
> +
> +    # All public methods need to acquire the lock; all private methods
> +    # assume the lock is already held.  Thus public methods cannot call
> +    # other public methods.
>
>      def __init__(self, filename, iflags, mode):
> -        res = lib.gdbm_open(filename, 0, iflags, mode, ffi.NULL)
> -        self.size = -1
> -        if not res:
> -            self._raise_from_errno()
> -        self.ll_dbm = res
> +        with _lock:
> +            res = lib.gdbm_open(filename, 0, iflags, mode, ffi.NULL)
> +            self.__size = -1
> +            if not res:
> +                self.__raise_from_errno()
> +            self.__ll_dbm = res
>
>      def close(self):
> -        if self.ll_dbm:
> -            lib.gdbm_close(self.ll_dbm)
> -            self.ll_dbm = None
> +        with _lock:
> +            if self.__ll_dbm:
> +                lib.gdbm_close(self.__ll_dbm)
> +                self.__ll_dbm = None
>
> -    def _raise_from_errno(self):
> +    def __raise_from_errno(self):
>          if ffi.errno:
>              raise error(ffi.errno, os.strerror(ffi.errno))
>          raise error(lib.gdbm_errno, lib.gdbm_strerror(lib.gdbm_errno))
>
>      def __len__(self):
> -        if self.size < 0:
> -            self.size = len(self.keys())
> -        return self.size
> +        with _lock:
> +            if self.__size < 0:
> +                self.__size = len(self.__keys())
> +            return self.__size
>
>      def __setitem__(self, key, value):
> -        self._check_closed()
> -        self.size = -1
> -        r = lib.gdbm_store(self.ll_dbm, _fromstr(key), _fromstr(value),
> -                           lib.GDBM_REPLACE)
> -        if r < 0:
> -            self._raise_from_errno()
> +        with _lock:
> +            self.__check_closed()
> +            self.__size = -1
> +            r = lib.gdbm_store(self.__ll_dbm, _fromstr(key), _fromstr(value),
> +                               lib.GDBM_REPLACE)
> +            if r < 0:
> +                self.__raise_from_errno()
>
>      def __delitem__(self, key):
> -        self._check_closed()
> -        self.size = -1
> -        res = lib.gdbm_delete(self.ll_dbm, _fromstr(key))
> -        if res < 0:
> -            raise KeyError(key)
> +        with _lock:
> +            self.__check_closed()
> +            self.__size = -1
> +            res = lib.gdbm_delete(self.__ll_dbm, _fromstr(key))
> +            if res < 0:
> +                raise KeyError(key)
>
>      def __contains__(self, key):
> -        self._check_closed()
> -        key = _checkstr(key)
> -        return lib.pygdbm_exists(self.ll_dbm, key, len(key))
> +        with _lock:
> +            self.__check_closed()
> +            key = _checkstr(key)
> +            return lib.pygdbm_exists(self.__ll_dbm, key, len(key))
>      has_key = __contains__
>
>      def __getitem__(self, key):
> -        self._check_closed()
> -        key = _checkstr(key)
> -        drec = lib.pygdbm_fetch(self.ll_dbm, key, len(key))
> -        if not drec.dptr:
> -            raise KeyError(key)
> -        res = str(ffi.buffer(drec.dptr, drec.dsize))
> -        lib.free(drec.dptr)
> -        return res
> +        with _lock:
> +            self.__check_closed()
> +            key = _checkstr(key)
> +            drec = lib.pygdbm_fetch(self.__ll_dbm, key, len(key))
> +            if not drec.dptr:
> +                raise KeyError(key)
> +            res = str(ffi.buffer(drec.dptr, drec.dsize))
> +            lib.free(drec.dptr)
> +            return res
>
> -    def keys(self):
> -        self._check_closed()
> +    def __keys(self):
> +        self.__check_closed()
>          l = []
> -        key = lib.gdbm_firstkey(self.ll_dbm)
> +        key = lib.gdbm_firstkey(self.__ll_dbm)
>          while key.dptr:
>              l.append(str(ffi.buffer(key.dptr, key.dsize)))
> -            nextkey = lib.gdbm_nextkey(self.ll_dbm, key)
> +            nextkey = lib.gdbm_nextkey(self.__ll_dbm, key)
>              lib.free(key.dptr)
>              key = nextkey
>          return l
>
> +    def keys(self):
> +        with _lock:
> +            return self.__keys()
> +
>      def firstkey(self):
> -        self._check_closed()
> -        key = lib.gdbm_firstkey(self.ll_dbm)
> -        if key.dptr:
> -            res = str(ffi.buffer(key.dptr, key.dsize))
> -            lib.free(key.dptr)
> -            return res
> +        with _lock:
> +            self.__check_closed()
> +            key = lib.gdbm_firstkey(self.__ll_dbm)
> +            if key.dptr:
> +                res = str(ffi.buffer(key.dptr, key.dsize))
> +                lib.free(key.dptr)
> +                return res
>
>      def nextkey(self, key):
> -        self._check_closed()
> -        key = lib.gdbm_nextkey(self.ll_dbm, _fromstr(key))
> -        if key.dptr:
> -            res = str(ffi.buffer(key.dptr, key.dsize))
> -            lib.free(key.dptr)
> -            return res
> +        with _lock:
> +            self.__check_closed()
> +            key = lib.gdbm_nextkey(self.__ll_dbm, _fromstr(key))
> +            if key.dptr:
> +                res = str(ffi.buffer(key.dptr, key.dsize))
> +                lib.free(key.dptr)
> +                return res
>
>      def reorganize(self):
> -        self._check_closed()
> -        if lib.gdbm_reorganize(self.ll_dbm) < 0:
> -            self._raise_from_errno()
> +        with _lock:
> +            self.__check_closed()
> +            if lib.gdbm_reorganize(self.__ll_dbm) < 0:
> +                self.__raise_from_errno()
>
> -    def _check_closed(self):
> -        if not self.ll_dbm:
> +    def __check_closed(self):
> +        if not self.__ll_dbm:
>              raise error(0, "GDBM object has already been closed")
>
>      __del__ = close
>
>      def sync(self):
> -        self._check_closed()
> -        lib.gdbm_sync(self.ll_dbm)
> +        with _lock:
> +            self.__check_closed()
> +            lib.gdbm_sync(self.__ll_dbm)
>
>  def open(filename, flags='r', mode=0666):
>      if flags[0] == 'r':
> _______________________________________________
> pypy-commit mailing list
> pypy-com...@python.org
> https://mail.python.org/mailman/listinfo/pypy-commit
_______________________________________________
pypy-dev mailing list
pypy-dev@python.org
https://mail.python.org/mailman/listinfo/pypy-dev

Reply via email to