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