Author: Armin Rigo <[email protected]>
Branch: stmgc-c4
Changeset: r66843:ad626a2c303c
Date: 2013-09-07 19:26 +0200
http://bitbucket.org/pypy/pypy/changeset/ad626a2c303c/

Log:    Fix raw mallocs to be freed in case of an abort occurring at the
        wrong time.

diff --git a/rpython/translator/c/src/mem.c b/rpython/translator/c/src/mem.c
--- a/rpython/translator/c/src/mem.c
+++ b/rpython/translator/c/src/mem.c
@@ -3,6 +3,35 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+
+#ifdef RPY_STM
+# include "src/mem.h"
+# include "src/allocator.h"
+# ifdef RPY_ASSERT
+int try_pypy_debug_alloc_stop(void *);
+# else
+#  define try_pypy_debug_alloc_stop(p)  /* nothing */
+# endif
+void _pypy_stm_free(void *ptr)
+{
+    /* This is called by src_stm/*.c when the transaction is aborted
+       and the 'ptr' was malloced but not freed.  We have first to
+       unregister the object with a tentative pypy_debug_alloc_stop(),
+       which ignores it if it was not actually registered.  Then we
+       free the object in the normal way.  Finally we increment the
+       free counter to keep it in sync. */
+    try_pypy_debug_alloc_stop(ptr);
+    PyObject_Free(ptr);
+    COUNT_FREE;
+}
+#endif
+
+
+#ifdef COUNT_OP_MALLOCS
+int count_mallocs=0, count_frees=0;
+#endif
+
+
 /***  tracking raw mallocs and frees for debugging ***/
 
 #ifdef RPY_ASSERT
@@ -35,7 +64,7 @@
   spinlock_release(pypy_debug_alloc_lock);
 }
 
-void pypy_debug_alloc_stop(void *addr)
+int try_pypy_debug_alloc_stop(void *addr)
 {
   struct pypy_debug_alloc_s **p;
   spinlock_acquire(pypy_debug_alloc_lock, '-');
@@ -47,9 +76,15 @@
         *p = dying->next;
         spinlock_release(pypy_debug_alloc_lock);
         free(dying);
-        return;
+        return 1;
       }
-  RPyAssert(0, "free() of a never-malloc()ed object");
+  return 0;
+}
+
+void pypy_debug_alloc_stop(void *addr)
+{
+  if (!try_pypy_debug_alloc_stop(addr))
+    RPyAssert(0, "free() of a never-malloc()ed object");
 }
 
 void pypy_debug_alloc_results(void)
diff --git a/rpython/translator/c/src/mem.h b/rpython/translator/c/src/mem.h
--- a/rpython/translator/c/src/mem.h
+++ b/rpython/translator/c/src/mem.h
@@ -11,30 +11,26 @@
 #define OP_STACK_CURRENT(r)  r = (Signed)&r
 
 
-#define RAW_MALLOC_ZERO_FILLED 0
+#ifdef RPY_STM
+void _pypy_stm_free(void *);
+#define _OP_RAW_MALLOCED(r)         stm_call_on_abort(r, _pypy_stm_free)
+#define _OP_RAW_STM_UNREGISTER(r)   stm_call_on_abort(r, NULL)
+#else
+#define _OP_RAW_MALLOCED(r)         /* nothing */
+#define _OP_RAW_STM_UNREGISTER(r)   /* nothing */
+#endif
 
-#if RAW_MALLOC_ZERO_FILLED
-
-#define OP_RAW_MALLOC(size, r, restype)  {                             \
-       r = (restype) PyObject_Malloc(size);                            \
-       if (r != NULL) {                                                \
-           memset((void*)r, 0, size);                                  \
-           COUNT_MALLOC;                                               \
-       }                                                               \
-    }
-
-#else
 
 #define OP_RAW_MALLOC(size, r, restype)  {                             \
        r = (restype) PyObject_Malloc(size);                            \
        if (r != NULL) {                                                \
            COUNT_MALLOC;                                               \
+            _OP_RAW_MALLOCED(r);                                        \
        }                                                               \
     }
 
-#endif
-
-#define OP_RAW_FREE(p, r) PyObject_Free(p); COUNT_FREE;
+#define OP_RAW_FREE(p, r) PyObject_Free(p); COUNT_FREE; \
+                          _OP_RAW_STM_UNREGISTER(p);
 
 #define OP_RAW_MEMCLEAR(p, size, r) memset((void*)p, 0, size)
 
@@ -63,7 +59,7 @@
 
 #else /* COUNT_OP_MALLOCS */
 
-static int count_mallocs=0, count_frees=0;
+extern int count_mallocs, count_frees;
 
 #define COUNT_MALLOC   count_mallocs++
 #define COUNT_FREE     count_frees++
diff --git a/rpython/translator/stm/test/test_inevitable.py 
b/rpython/translator/stm/test/test_inevitable.py
--- a/rpython/translator/stm/test/test_inevitable.py
+++ b/rpython/translator/stm/test/test_inevitable.py
@@ -114,8 +114,6 @@
 
         res = self.interpret_inevitable(f1, [])
         assert res is None
-        assert 0, """we do not turn inevitable before
-        raw-mallocs which causes leaks on aborts"""
 
     def test_raw_malloc_2(self):
         X = lltype.Struct('X', ('foo', lltype.Signed))
@@ -126,8 +124,6 @@
 
         res = self.interpret_inevitable(f1, [])
         assert res is None
-        assert 0, """we do not turn inevitable before
-        raw-mallocs which causes leaks on aborts"""
 
     def test_unknown_raw_free(self):
         X = lltype.Struct('X', ('foo', lltype.Signed))
diff --git a/rpython/translator/stm/test/test_ztranslated.py 
b/rpython/translator/stm/test/test_ztranslated.py
--- a/rpython/translator/stm/test/test_ztranslated.py
+++ b/rpython/translator/stm/test/test_ztranslated.py
@@ -1,5 +1,7 @@
 from rpython.rlib import rstm, rgc, objectmodel
+from rpython.rlib.debug import debug_print
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rtyper.lltypesystem.rclass import OBJECTPTR
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.translator.stm.test.support import CompiledSTMTests
 from rpython.translator.stm.test import targetdemo2
@@ -131,7 +133,6 @@
             rgc.collect(0)
             return 0
         #
-        from rpython.rtyper.lltypesystem.rclass import OBJECTPTR
         S = lltype.GcStruct('S', ('got_exception', OBJECTPTR))
         PS = lltype.Ptr(S)
         perform_transaction = rstm.make_perform_transaction(check, PS)
@@ -162,7 +163,6 @@
             pass
         prebuilt2 = [X2(), X2()]
         #
-        from rpython.rtyper.lltypesystem.rclass import OBJECTPTR
         S = lltype.GcStruct('S', ('got_exception', OBJECTPTR))
         PS = lltype.Ptr(S)
         perform_transaction = rstm.make_perform_transaction(check, PS)
@@ -190,7 +190,6 @@
     def test_prebuilt_nongc(self):
         def check(foobar, retry_counter):
             return 0    # do nothing
-        from rpython.rtyper.lltypesystem.rclass import OBJECTPTR
         from rpython.rtyper.lltypesystem import lltype
         S = lltype.GcStruct('S', ('got_exception', OBJECTPTR))
         PS = lltype.Ptr(S)
@@ -237,8 +236,6 @@
         assert 'ok\n' in data
 
     def test_abort_info(self):
-        from rpython.rtyper.lltypesystem.rclass import OBJECTPTR
-
         class Parent(object):
             pass
         class Foobar(Parent):
@@ -329,3 +326,35 @@
         t, cbuilder = self.compile(main)
         data = cbuilder.cmdexec('')
         assert 'test ok\n' in data
+
+    def test_raw_malloc_no_leak(self):
+        FOOARRAY = lltype.Array(lltype.Signed)
+
+        def check(_, retry_counter):
+            x = lltype.malloc(FOOARRAY, 100000, flavor='raw')
+            if retry_counter < 1000:
+                if (retry_counter & 3) == 0:
+                    lltype.free(x, flavor='raw')
+                debug_print(rffi.cast(lltype.Signed, x))
+                rstm.abort_and_retry()
+            lltype.free(x, flavor='raw')
+            return 0
+
+        PS = lltype.Ptr(lltype.GcStruct('S', ('got_exception', OBJECTPTR)))
+        perform_transaction = rstm.make_perform_transaction(check, PS)
+
+        def main(argv):
+            perform_transaction(lltype.nullptr(PS.TO))
+            return 0
+
+        t, cbuilder = self.compile(main)
+        data, dataerr = cbuilder.cmdexec('', err=True)
+        lines = dataerr.split('\n')
+        assert len(lines) > 1000
+        addresses = map(int, lines[:1000])
+        assert len(addresses) == 1000
+        assert len(set(addresses)) < 500    # should ideally just be a few
+        import re
+        match = re.search(r"(\d+) mallocs left", dataerr)
+        assert match
+        assert int(match.group(1)) < 20
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to