Author: Armin Rigo <[email protected]>
Branch:
Changeset: r293:9bb735eaca1a
Date: 2013-06-26 22:17 +0200
http://bitbucket.org/pypy/stmgc/changeset/9bb735eaca1a/
Log: Divide the nursery into sections that are cleared incrementally,
like in minimark.py. This gives a code reorganization that makes it
easy to implement the real goal: stm_allocate() of objects that are
very large (here defined as "more than one section large").
diff --git a/c4/et.c b/c4/et.c
--- a/c4/et.c
+++ b/c4/et.c
@@ -586,10 +586,7 @@
assert(is_private(W));
if (W->h_tid & GCFLAG_OLD)
- {
- W->h_tid |= GCFLAG_WRITE_BARRIER;
- record_write_barrier(W);
- }
+ gcptrlist_insert(&d->old_objects_to_trace, W);
else
gcptrlist_insert(&d->public_with_young_copy, R);
}
diff --git a/c4/nursery.c b/c4/nursery.c
--- a/c4/nursery.c
+++ b/c4/nursery.c
@@ -24,11 +24,13 @@
{
struct tx_descriptor *d = thread_descriptor;
+ assert(GC_NURSERY % GC_NURSERY_SECTION == 0);
+
assert(d->nursery_base == NULL);
- d->nursery_base = stm_malloc(GC_NURSERY);
- memset(d->nursery_base, 0, GC_NURSERY);
- d->nursery_end = d->nursery_base + GC_NURSERY;
- d->nursery_current = d->nursery_base;
+ d->nursery_base = stm_malloc(GC_NURSERY); /* start of nursery */
+ d->nursery_end = d->nursery_base + GC_NURSERY; /* end of nursery */
+ d->nursery_current = d->nursery_base; /* current position */
+ d->nursery_nextlimit = d->nursery_base; /* next section limit */
dprintf(("minor: nursery is at [%p to %p]\n", d->nursery_base,
d->nursery_end));
@@ -50,32 +52,31 @@
d->nursery_current = d->nursery_end;
}
-static char *collect_and_allocate_size(size_t size); /* forward */
+static gcptr allocate_next_section(size_t size, revision_t tid); /* forward */
-inline static char *allocate_nursery(size_t size, int can_collect)
+inline static gcptr allocate_nursery(size_t size, revision_t tid)
{
+ /* if 'tid == -1', we must not collect */
struct tx_descriptor *d = thread_descriptor;
+ gcptr P;
char *cur = d->nursery_current;
char *end = cur + size;
d->nursery_current = end;
- if (end > d->nursery_end) {
- if (can_collect) {
- cur = collect_and_allocate_size(size);
- }
- else {
- d->nursery_current = cur;
- cur = NULL;
- }
+ if (end > d->nursery_nextlimit) {
+ P = allocate_next_section(size, tid);
}
- return cur;
+ else {
+ P = (gcptr)cur;
+ P->h_tid = tid;
+ }
+ return P;
}
gcptr stm_allocate(size_t size, unsigned long tid)
{
/* XXX inline the fast path */
- gcptr P = (gcptr)allocate_nursery(size, 1);
assert(tid == (tid & STM_USER_TID_MASK));
- P->h_tid = tid;
+ gcptr P = allocate_nursery(size, tid);
P->h_revision = stm_private_rev_num;
P->h_original = 0;
return P;
@@ -84,7 +85,7 @@
gcptr stmgc_duplicate(gcptr P)
{
size_t size = stmgc_size(P);
- gcptr L = (gcptr)allocate_nursery(size, 0);
+ gcptr L = allocate_nursery(size, -1);
if (L == NULL)
return stmgc_duplicate_old(P);
@@ -531,19 +532,19 @@
*/
teardown_minor_collect(d);
- /* clear the nursery */
+ /* if in debugging mode, we allocate a different nursery and make
+ the old one inaccessible
+ */
#if defined(_GC_DEBUG) && _GC_DEBUG >= 2
stm_free(d->nursery_base, GC_NURSERY);
d->nursery_base = stm_malloc(GC_NURSERY);
- memset(d->nursery_base, 0, GC_NURSERY);
d->nursery_end = d->nursery_base + GC_NURSERY;
dprintf(("minor: nursery moved to [%p to %p]\n", d->nursery_base,
d->nursery_end));
-#else
- memset(d->nursery_base, 0,
- d->nursery_current - d->nursery_base);
#endif
+
d->nursery_current = d->nursery_base;
+ d->nursery_nextlimit = d->nursery_base;
assert(!stmgc_minor_collect_anything_to_do(d));
}
@@ -584,17 +585,66 @@
}
}
-static char *collect_and_allocate_size(size_t allocate_size)
+static gcptr allocate_next_section(size_t allocate_size, revision_t tid)
{
- stmgc_minor_collect();
- //stmgcpage_possibly_major_collect(0);
+ /* This is called when the next allocation request hits
+ 'nursery_nextlimit', which points to the next multiple of
+ GC_NURSERY_SECTION bytes in the nursery.
+ 'tid' is the value to store in the h_tid of the result,
+ or if it's equal to -1, it means we must not collect.
+
+ First fix 'nursery_current', left to a bogus value by the caller.
+ */
struct tx_descriptor *d = thread_descriptor;
- assert(d->nursery_current == d->nursery_base);
+ d->nursery_current -= allocate_size;
- //_debug_roots(d->shadowstack, *d->shadowstack_end_ref);
+ /* Are we asking for a "reasonable" number of bytes, i.e. a value
+ at most equal to one section?
+ */
+ if (allocate_size > GC_NURSERY_SECTION) {
+ /* No */
+ if (tid == -1)
+ return NULL; /* cannot collect */
- d->nursery_current = d->nursery_base + allocate_size;
- assert(d->nursery_current <= d->nursery_end); /* XXX object too big */
- return d->nursery_base;
+ /* Allocate it externally, and make it old */
+ gcptr P = stmgcpage_malloc(allocate_size);
+ P->h_tid = tid | GCFLAG_OLD;
+ gcptrlist_insert(&d->old_objects_to_trace, P);
+ return P;
+ }
+
+ revision_t clear_section_count = GC_NURSERY_SECTION;
+
+ /* Are we at the end of the nursery? */
+ if (d->nursery_nextlimit == d->nursery_end) {
+ /* Yes */
+ if (tid == -1)
+ return NULL; /* cannot collect */
+
+ /* Start a minor collection
+ */
+ if (d->nursery_current - d->nursery_base < clear_section_count) {
+ /* help cases where we do a large amount of minor collections */
+ clear_section_count = d->nursery_current - d->nursery_base;
+ }
+
+ stmgc_minor_collect();
+ stmgcpage_possibly_major_collect(0);
+
+ assert(d->nursery_current == d->nursery_base);
+ assert(d->nursery_nextlimit == d->nursery_base);
+ }
+
+ /* Clear the next section */
+ memset(d->nursery_nextlimit, 0, clear_section_count);
+ d->nursery_nextlimit += GC_NURSERY_SECTION;
+
+ /* Return the object from there */
+ gcptr P = (gcptr)d->nursery_current;
+ d->nursery_current += allocate_size;
+ assert(d->nursery_current <= d->nursery_nextlimit);
+
+ P->h_tid = tid;
+ return P;
}
diff --git a/c4/nursery.h b/c4/nursery.h
--- a/c4/nursery.h
+++ b/c4/nursery.h
@@ -6,10 +6,19 @@
//#define GC_NURSERY (1<<20) /* 1 MB */
#endif
+#ifndef GC_NURSERY_SECTION
+# if GC_NURSERY >= 2 * 135168
+# define GC_NURSERY_SECTION 135168
+# else
+# define GC_NURSERY_SECTION (GC_NURSERY / 2)
+# endif
+#endif
+
#define NURSERY_FIELDS_DECL \
/* the nursery */ \
char *nursery_current; \
+ char *nursery_nextlimit; \
char *nursery_end; \
char *nursery_base; \
\
diff --git a/c4/test/support.py b/c4/test/support.py
--- a/c4/test/support.py
+++ b/c4/test/support.py
@@ -1,5 +1,10 @@
import os, cffi, thread, sys
+if sys.maxint > 2**32:
+ WORD = 8
+else:
+ WORD = 4
+
# ----------
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -103,6 +108,7 @@
/* some constants normally private that are useful in the tests */
#define WORD ...
#define GC_PAGE_SIZE ...
+ #define GC_NURSERY_SECTION ...
#define GCFLAG_OLD ...
#define GCFLAG_VISITED ...
#define GCFLAG_PUBLIC ...
@@ -276,7 +282,7 @@
}
'''.lstrip(), include_dirs=[parent_dir],
undef_macros=['NDEBUG'],
- define_macros=[('GC_NURSERY', '(16 * sizeof(void *))'),
+ define_macros=[('GC_NURSERY', str(16 * WORD)),
('_GC_DEBUG', '2'),
('GC_PAGE_SIZE', '1000'),
('GC_MIN', '200000'),
@@ -288,7 +294,7 @@
extra_compile_args=['-g', '-O0'])
HDR = ffi.sizeof("struct stm_object_s")
-WORD = lib.WORD
+assert WORD == lib.WORD
PAGE_ROOM = lib.GC_PAGE_SIZE - ffi.sizeof("page_header_t")
for name in lib.__dict__:
if name.startswith('GCFLAG_') or name.startswith('PREBUILT_'):
@@ -470,7 +476,10 @@
def nalloc(size):
"Allocate a fresh object from the nursery"
p = lib.stm_allocate(size, 42 + size)
- assert p.h_tid == 42 + size # no GC flags
+ if size <= lib.GC_NURSERY_SECTION:
+ assert p.h_tid == 42 + size # no GC flags
+ else:
+ assert p.h_tid == 42 + size + GCFLAG_OLD
assert p.h_revision == lib.get_private_rev_num()
return p
diff --git a/c4/test/test_nursery.py b/c4/test/test_nursery.py
--- a/c4/test/test_nursery.py
+++ b/c4/test/test_nursery.py
@@ -296,3 +296,8 @@
assert not lib.in_nursery(p1)
p2 = lib.getptr(p1, 0)
assert lib.getlong(p2, 0) == 389719
+
+def test_nalloc_large_object():
+ for words in range(80):
+ p1 = nalloc(HDR + words * WORD)
+ # assert did not crash
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit