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

Reply via email to