Author: Brian Kearns <bdkea...@gmail.com> Branch: Changeset: r80917:37057ce12bc9 Date: 2015-11-24 17:30 -0500 http://bitbucket.org/pypy/pypy/changeset/37057ce12bc9/
Log: merge heads diff --git a/lib_pypy/cffi/api.py b/lib_pypy/cffi/api.py --- a/lib_pypy/cffi/api.py +++ b/lib_pypy/cffi/api.py @@ -72,6 +72,7 @@ self._cdefsources = [] self._included_ffis = [] self._windows_unicode = None + self._init_once_cache = {} if hasattr(backend, 'set_ffi'): backend.set_ffi(self) for name in backend.__dict__: @@ -598,6 +599,30 @@ return recompile(self, module_name, source, tmpdir=tmpdir, source_extension=source_extension, **kwds) + def init_once(self, func, tag): + # Read _init_once_cache[tag], which is either (False, lock) if + # we're calling the function now in some thread, or (True, result). + # Don't call setdefault() in most cases, to avoid allocating and + # immediately freeing a lock; but still use setdefaut() to avoid + # races. + try: + x = self._init_once_cache[tag] + except KeyError: + x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) + # Common case: we got (True, result), so we return the result. + if x[0]: + return x[1] + # Else, it's a lock. Acquire it to serialize the following tests. + with x[1]: + # Read again from _init_once_cache the current status. + x = self._init_once_cache[tag] + if x[0]: + return x[1] + # Call the function and store the result back. + result = func() + self._init_once_cache[tag] = (True, result) + return result + def _load_backend_lib(backend, name, flags): if name is None: diff --git a/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h --- a/lib_pypy/cffi/parse_c_type.h +++ b/lib_pypy/cffi/parse_c_type.h @@ -1,5 +1,6 @@ -/* See doc/misc/parse_c_type.rst in the source of CFFI for more information */ +/* This part is from file 'cffi/parse_c_type.h'. It is copied at the + beginning of C sources generated by CFFI's ffi.set_source(). */ typedef void *_cffi_opcode_t; diff --git a/pypy/module/_cffi_backend/ffi_obj.py b/pypy/module/_cffi_backend/ffi_obj.py --- a/pypy/module/_cffi_backend/ffi_obj.py +++ b/pypy/module/_cffi_backend/ffi_obj.py @@ -49,6 +49,8 @@ ACCEPT_CDATA = ACCEPT_CDATA w_gc_wref_remove = None + w_init_once_cache = None + jit_init_once_cache = None @jit.dont_look_inside def __init__(self, space, src_ctx): @@ -585,6 +587,59 @@ return w_result + def descr_init_once(self, w_func, w_tag): + """XXX document me""" + # + # first, a fast-path for the JIT which only works if the very + # same w_tag object is passed; then it turns into no code at all + try: + return self._init_once_elidable(w_tag) + except KeyError: + return self._init_once_slowpath(w_func, w_tag) + + @jit.elidable + def _init_once_elidable(self, w_tag): + jit_cache = self.jit_init_once_cache + if jit_cache is not None: + return jit_cache[w_tag] + else: + raise KeyError + + @jit.dont_look_inside + def _init_once_slowpath(self, w_func, w_tag): + space = self.space + w_cache = self.w_init_once_cache + if w_cache is None: + w_cache = self.space.newdict() + jit_cache = {} + self.w_init_once_cache = w_cache + self.jit_init_once_cache = jit_cache + # + # get the lock or result from cache[tag] + w_res = space.finditem(w_cache, w_tag) + if w_res is None: + w_res = W_InitOnceLock(space) + w_res = space.call_method(w_cache, 'setdefault', w_tag, w_res) + if not isinstance(w_res, W_InitOnceLock): + return w_res + with w_res.lock: + w_res = space.finditem(w_cache, w_tag) + if w_res is None or isinstance(w_res, W_InitOnceLock): + w_res = space.call_function(w_func) + self.jit_init_once_cache[w_tag] = w_res + space.setitem(w_cache, w_tag, w_res) + else: + # the real result was put in the dict while we were + # waiting for lock.__enter__() above + pass + return w_res + + +class W_InitOnceLock(W_Root): + def __init__(self, space): + self.lock = space.allocate_lock() + + @jit.dont_look_inside def make_plain_ffi_object(space, w_ffitype=None): if w_ffitype is None: @@ -641,6 +696,7 @@ from_handle = interp2app(W_FFIObject.descr_from_handle), gc = interp2app(W_FFIObject.descr_gc), getctype = interp2app(W_FFIObject.descr_getctype), + init_once = interp2app(W_FFIObject.descr_init_once), integer_const = interp2app(W_FFIObject.descr_integer_const), memmove = interp2app(W_FFIObject.descr_memmove), new = interp2app(W_FFIObject.descr_new), diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py --- a/pypy/module/_cffi_backend/test/test_ffi_obj.py +++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py @@ -447,3 +447,19 @@ assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 + + def test_init_once(self): + import _cffi_backend as _cffi1_backend + def do_init(): + seen.append(1) + return 42 + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(3): + res = ffi.init_once(do_init, "tag1") + assert res == 42 + assert seen == [1] + for i in range(3): + res = ffi.init_once(do_init, "tag2") + assert res == 42 + assert seen == [1, 1] diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/backend_tests.py @@ -1810,3 +1810,35 @@ assert lib.EE1 == 0 assert lib.EE2 == 0 assert lib.EE3 == 1 + + def test_init_once(self): + def do_init(): + seen.append(1) + return 42 + ffi = FFI() + seen = [] + for i in range(3): + res = ffi.init_once(do_init, "tag1") + assert res == 42 + assert seen == [1] + for i in range(3): + res = ffi.init_once(do_init, "tag2") + assert res == 42 + assert seen == [1, 1] + + def test_init_once_multithread(self): + import thread, time + def do_init(): + seen.append('init!') + time.sleep(1) + seen.append('init done') + return 7 + ffi = FFI() + seen = [] + for i in range(6): + def f(): + res = ffi.init_once(do_init, "tag") + seen.append(res) + thread.start_new_thread(f, ()) + time.sleep(1.5) + assert seen == ['init!', 'init done'] + 6 * [7] diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py @@ -487,7 +487,7 @@ ffi = FFI(backend=self.Backend()) ffi.cdef("double __stdcall sin(double x);") # stdcall ignored m = ffi.dlopen(lib_m) - if (sys.platform == 'win32' and sys.maxint < 2**32 and + if (sys.platform == 'win32' and sys.maxsize < 2**32 and self.Backend is not CTypesBackend): assert "double(__stdcall *)(double)" in str(ffi.typeof(m.sin)) else: diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_ffi_obj.py @@ -194,6 +194,11 @@ yp = ffi.new_handle([6, 4, 2]) assert ffi.from_handle(yp) == [6, 4, 2] +def test_handle_unique(): + ffi = _cffi1_backend.FFI() + assert ffi.new_handle(None) is not ffi.new_handle(None) + assert ffi.new_handle(None) != ffi.new_handle(None) + def test_ffi_cast(): ffi = _cffi1_backend.FFI() assert ffi.cast("int(*)(int)", 0) == ffi.NULL @@ -416,3 +421,37 @@ assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 + +def test_init_once(): + def do_init(): + seen.append(1) + return 42 + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(3): + res = ffi.init_once(do_init, "tag1") + assert res == 42 + assert seen == [1] + for i in range(3): + res = ffi.init_once(do_init, "tag2") + assert res == 42 + assert seen == [1, 1] + +def test_init_once_multithread(): + import thread, time + def do_init(): + print 'init!' + seen.append('init!') + time.sleep(1) + seen.append('init done') + print 'init done' + return 7 + ffi = _cffi1_backend.FFI() + seen = [] + for i in range(6): + def f(): + res = ffi.init_once(do_init, "tag") + seen.append(res) + thread.start_new_thread(f, ()) + time.sleep(1.5) + assert seen == ['init!', 'init done'] + 6 * [7] _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit