Author: Philip Jenvey <pjen...@underboss.org> Branch: Changeset: r69137:2d71669b2c3c Date: 2014-02-13 11:59 -0800 http://bitbucket.org/pypy/pypy/changeset/2d71669b2c3c/
Log: merge upstream diff --git a/include/PyPy.h b/include/PyPy.h new file mode 100644 --- /dev/null +++ b/include/PyPy.h @@ -0,0 +1,54 @@ +#ifndef _PYPY_H_ +#define _PYPY_H_ + +/* This header is meant to be included in programs that use PyPy as an + embedded library. */ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* You should call this first once. */ +void rpython_startup_code(void); + + +/* Initialize the home directory of PyPy. It is necessary to call this. + + Call it with "home" being the file name of the libpypy.so, for + example; it will be used as a starting point when searching for the + lib-python and lib_pypy directories. They are searched from + "home/..", "home/../..", etc. Returns 0 if everything was fine. If + an error occurs, returns 1 and (if verbose != 0) prints some + information to stderr. + */ +int pypy_setup_home(char *home, int verbose); + + +/* If your program has multiple threads, then you need to call + pypy_init_threads() once at init time, and then pypy_thread_attach() + once in each other thread that just started and in which you want to + run Python code (including via callbacks, see below). + */ +void pypy_init_threads(void); +void pypy_thread_attach(void); + + +/* The main entry point: executes "source" as plain Python code. + Returns 0 if everything was fine. If a Python exception is + uncaught, it is printed to stderr and 1 is returned. + + Usually, the Python code from "source" should use cffi to fill in + global variables of "function pointer" type in your program. Use + cffi callbacks to do so. Once it is done, there is no need to call + pypy_execute_source() any more: from C, you call directly the + functions (which are "callbacks" from the point of view of Python). + */ +int pypy_execute_source(char *source); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pypy/doc/project-ideas.rst b/pypy/doc/project-ideas.rst --- a/pypy/doc/project-ideas.rst +++ b/pypy/doc/project-ideas.rst @@ -142,32 +142,17 @@ * `hg` -Experiment (again) with LLVM backend for RPython compilation ------------------------------------------------------------- - -We already tried working with LLVM and at the time, LLVM was not mature enough -for our needs. It's possible that this has changed, reviving the LLVM backend -(or writing new from scratch) for static compilation would be a good project. - -(On the other hand, just generating C code and using clang might be enough. -The issue with that is the so-called "asmgcc GC root finder", which has tons -of issues of this own. In my opinion (arigo), it would be definitely a -better project to try to optimize the alternative, the "shadowstack" GC root -finder, which is nicely portable. So far it gives a pypy that is around -7% slower.) - Embedding PyPy ---------------------------------------- Note: there is a basic proof-of-concept for that as a `uwsgi pypy plugin`_ Being able to embed PyPy, say with its own limited C API, would be -useful. But here is the most interesting variant, straight from -EuroPython live discussion :-) We can have a generic "libpypy.so" that -can be used as a placeholder dynamic library, and when it gets loaded, -it runs a .py module that installs (via ctypes) the interface it wants -exported. This would give us a one-size-fits-all generic .so file to be -imported by any application that wants to load .so files :-) +useful. But there is a possibly better variant: use CFFI. With some +minimal tools atop CFFI, it would be possible to write a pure Python +library, and then compile automatically from it an .so/.dll file that is +a dynamic-link library with whatever C API we want. This gives us a +one-size-fits-all generic way to make .so/.dll files from Python. .. _`uwsgi pypy plugin`: http://uwsgi-docs.readthedocs.org/en/latest/PyPy.html diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -90,9 +90,10 @@ return f """) - @entrypoint('main', [rffi.CCHARP, lltype.Signed], c_name='pypy_setup_home') + @entrypoint('main', [rffi.CCHARP, rffi.INT], c_name='pypy_setup_home') def pypy_setup_home(ll_home, verbose): from pypy.module.sys.initpath import pypy_find_stdlib + verbose = rffi.cast(lltype.Signed, verbose) if ll_home: home = rffi.charp2str(ll_home) else: @@ -120,7 +121,8 @@ @entrypoint('main', [rffi.CCHARP], c_name='pypy_execute_source') def pypy_execute_source(ll_source): source = rffi.charp2str(ll_source) - return _pypy_execute_source(source) + res = _pypy_execute_source(source) + return rffi.cast(rffi.INT, res) @entrypoint('main', [], c_name='pypy_init_threads') def pypy_init_threads(): diff --git a/pypy/interpreter/test/test_targetpypy.py b/pypy/interpreter/test/test_targetpypy.py --- a/pypy/interpreter/test/test_targetpypy.py +++ b/pypy/interpreter/test/test_targetpypy.py @@ -12,8 +12,10 @@ _, d = create_entry_point(space, None) execute_source = d['pypy_execute_source'] lls = rffi.str2charp("import sys; sys.modules['xyz'] = 3") - execute_source(lls) + res = execute_source(lls) lltype.free(lls, flavor='raw') + assert lltype.typeOf(res) == rffi.INT + assert rffi.cast(lltype.Signed, res) == 0 x = space.int_w(space.getitem(space.getattr(space.builtin_modules['sys'], space.wrap('modules')), space.wrap('xyz'))) @@ -24,5 +26,5 @@ # did not crash - the same globals pypy_setup_home = d['pypy_setup_home'] lls = rffi.str2charp(__file__) - pypy_setup_home(lls, 1) + pypy_setup_home(lls, rffi.cast(rffi.INT, 1)) lltype.free(lls, flavor='raw') diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py --- a/pypy/module/bz2/interp_bz2.py +++ b/pypy/module/bz2/interp_bz2.py @@ -31,7 +31,7 @@ _compilation_info_ = eci calling_conv = 'c' - CHECK_LIBRARY = platform.Has('dump("x", (int)&BZ2_bzCompress)') + CHECK_LIBRARY = platform.Has('dump("x", (long)&BZ2_bzCompress)') off_t = platform.SimpleType("off_t", rffi.LONGLONG) size_t = platform.SimpleType("size_t", rffi.ULONG) diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py --- a/rpython/memory/gc/incminimark.py +++ b/rpython/memory/gc/incminimark.py @@ -1837,6 +1837,11 @@ # if self.objects_with_finalizers.non_empty(): self.deal_with_objects_with_finalizers() + elif self.old_objects_with_weakrefs.non_empty(): + # Weakref support: clear the weak pointers to dying objects + # (if we call deal_with_objects_with_finalizers(), it will + # invoke invalidate_old_weakrefs() itself directly) + self.invalidate_old_weakrefs() ll_assert(not self.objects_to_trace.non_empty(), "objects_to_trace should be empty") @@ -1846,9 +1851,7 @@ self.more_objects_to_trace.delete() # - # Weakref support: clear the weak pointers to dying objects - if self.old_objects_with_weakrefs.non_empty(): - self.invalidate_old_weakrefs() + # Light finalizers if self.old_objects_with_light_finalizers.non_empty(): self.deal_with_old_objects_with_finalizers() #objects_to_trace processed fully, can move on to sweeping @@ -2206,6 +2209,12 @@ self._recursively_bump_finalization_state_from_2_to_3(y) self._recursively_bump_finalization_state_from_1_to_2(x) + # Clear the weak pointers to dying objects. Also clears them if + # they point to objects which have the GCFLAG_FINALIZATION_ORDERING + # bit set here. These are objects which will be added to + # run_finalizers(). + self.invalidate_old_weakrefs() + while marked.non_empty(): x = marked.popleft() state = self._finalization_state(x) @@ -2333,7 +2342,9 @@ ll_assert((self.header(pointing_to).tid & GCFLAG_NO_HEAP_PTRS) == 0, "registered old weakref should not " "point to a NO_HEAP_PTRS obj") - if self.header(pointing_to).tid & GCFLAG_VISITED: + tid = self.header(pointing_to).tid + if ((tid & (GCFLAG_VISITED | GCFLAG_FINALIZATION_ORDERING)) == + GCFLAG_VISITED): new_with_weakref.append(obj) else: (obj + offset).address[0] = llmemory.NULL diff --git a/rpython/memory/test/gc_test_base.py b/rpython/memory/test/gc_test_base.py --- a/rpython/memory/test/gc_test_base.py +++ b/rpython/memory/test/gc_test_base.py @@ -29,6 +29,7 @@ GC_CAN_SHRINK_ARRAY = False GC_CAN_SHRINK_BIG_ARRAY = False BUT_HOW_BIG_IS_A_BIG_STRING = 3*WORD + WREF_IS_INVALID_BEFORE_DEL_IS_CALLED = False def setup_class(cls): cls._saved_logstate = py.log._getstate() @@ -370,15 +371,23 @@ class A(object): count = 0 a = A() + expected_invalid = self.WREF_IS_INVALID_BEFORE_DEL_IS_CALLED class B(object): def __del__(self): # when __del__ is called, the weakref to myself is still valid - # in RPython (at least with most GCs; this test might be - # skipped for specific GCs) - if self.ref() is self: - a.count += 10 # ok + # in RPython with most GCs. However, this can lead to strange + # bugs with incminimark. https://bugs.pypy.org/issue1687 + # So with incminimark, we expect the opposite. + if expected_invalid: + if self.ref() is None: + a.count += 10 # ok + else: + a.count = 666 # not ok else: - a.count = 666 # not ok + if self.ref() is self: + a.count += 10 # ok + else: + a.count = 666 # not ok def g(): b = B() ref = weakref.ref(b) diff --git a/rpython/memory/test/test_incminimark_gc.py b/rpython/memory/test/test_incminimark_gc.py --- a/rpython/memory/test/test_incminimark_gc.py +++ b/rpython/memory/test/test_incminimark_gc.py @@ -1,6 +1,38 @@ -from rpython.rlib.rarithmetic import LONG_BIT +from rpython.rtyper.lltypesystem import lltype +from rpython.rtyper.lltypesystem.lloperation import llop from rpython.memory.test import test_minimark_gc class TestIncrementalMiniMarkGC(test_minimark_gc.TestMiniMarkGC): from rpython.memory.gc.incminimark import IncrementalMiniMarkGC as GCClass + WREF_IS_INVALID_BEFORE_DEL_IS_CALLED = True + + def test_weakref_not_in_stack(self): + import weakref + class A(object): + pass + class B(object): + def __init__(self, next): + self.next = next + def g(): + a = A() + a.x = 5 + wr = weakref.ref(a) + llop.gc__collect(lltype.Void) # make everything old + assert wr() is not None + assert a.x == 5 + return wr + def f(): + ref = g() + llop.gc__collect(lltype.Void, 1) # start a major cycle + # at this point the stack is scanned, and the weakref points + # to an object not found, but still reachable: + b = ref() + llop.debug_print(lltype.Void, b) + assert b is not None + llop.gc__collect(lltype.Void) # finish the major cycle + # assert does not crash, because 'b' is still kept alive + b.x = 42 + return ref() is b + res = self.interpret(f, []) + assert res == True diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py --- a/rpython/rlib/objectmodel.py +++ b/rpython/rlib/objectmodel.py @@ -366,6 +366,9 @@ def compute_result_annotation(self, s_l, s_sizehint): from rpython.annotator import model as annmodel + if annmodel.s_None.contains(s_l): + return # first argument is only None so far, but we + # expect a generalization later if not isinstance(s_l, annmodel.SomeList): raise annmodel.AnnotatorError("First argument must be a list") if not isinstance(s_sizehint, annmodel.SomeInteger): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit