[pypy-commit] stmgc c7-fork: in-progress

arigo noreply at buildbot.pypy.org
Tue Mar 18 15:43:20 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: c7-fork
Changeset: r1070:bef9be522fb9
Date: 2014-03-18 15:43 +0100
http://bitbucket.org/pypy/stmgc/changeset/bef9be522fb9/

Log:	in-progress

diff --git a/c7/demo/demo_random.c b/c7/demo/demo_random.c
--- a/c7/demo/demo_random.c
+++ b/c7/demo/demo_random.c
@@ -4,20 +4,24 @@
 #include <pthread.h>
 #include <semaphore.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 
 #include "stmgc.h"
 
 #define NUMTHREADS 3
-#define STEPS_PER_THREAD 500
-#define THREAD_STARTS 1000 // how many restarts of threads
+#define STEPS_PER_THREAD 300
+#define THREAD_STARTS 300 // how many restarts of threads
 #define PREBUILT_ROOTS 3
 #define MAXROOTS 1000
+#define FORKS 3
 
 // SUPPORT
 struct node_s;
 typedef TLPREFIX struct node_s node_t;
 typedef node_t* nodeptr_t;
 typedef object_t* objptr_t;
+int num_forked_children = 0;
 
 struct node_s {
     struct object_s hdr;
@@ -337,6 +341,22 @@
             push_roots();
             stm_commit_transaction();
 
+            if (arg) {
+                printf("==========   FORK  =========\n");
+                arg = NULL;
+                pid_t child = fork();
+                printf("=== in process %d thread %lx, fork() returned %d\n",
+                       (int)getpid(), (long)pthread_self(), (int)child);
+                if (child == -1) {
+                    fprintf(stderr, "fork() error: %m\n");
+                    abort();
+                }
+                if (child != 0)
+                    num_forked_children++;
+                else
+                    num_forked_children = 0;
+            }
+
             td.num_roots_at_transaction_start = td.num_roots;
 
             if (get_rand(100) < 98) {
@@ -427,8 +447,24 @@
         assert(status == 0);
         printf("thread finished\n");
         if (thread_starts) {
+            long forkbase = NUMTHREADS * THREAD_STARTS / (FORKS + 1);
+            long _fork = (thread_starts % forkbase) == 0;
             thread_starts--;
-            newthread(demo_random, NULL);
+            newthread(demo_random, (void *)_fork);
+        }
+    }
+
+    for (i = 0; i < num_forked_children; i++) {
+        pid_t child = wait(&status);
+        if (child == -1)
+            perror("wait");
+        printf("From %d: child %d terminated with exit status %d\n",
+               (int)getpid(), (int)child, status);
+        if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
+            ;
+        else {
+            printf("*** error from the child ***\n");
+            return 1;
         }
     }
 
diff --git a/c7/stm/forksupport.c b/c7/stm/forksupport.c
--- a/c7/stm/forksupport.c
+++ b/c7/stm/forksupport.c
@@ -28,10 +28,9 @@
        a __thread variable, so never changes threads.
     */
     s_mutex_lock();
+    mutex_pages_lock();
 
-    synchronize_all_threads();
-
-    mutex_pages_lock();
+    dprintf(("forksupport_prepare: synchronized all threads\n"));
 
     fork_this_tl = NULL;
     stm_thread_local_t *tl = stm_all_thread_locals;
@@ -50,11 +49,29 @@
     if (fork_this_tl == NULL)
         stm_fatalerror("fork(): found no stm_thread_local_t from this thread");
 
+    /* Make a new mmap at some other address, but of the same size as
+       the standard mmap at stm_object_pages
+    */
     char *big_copy = setup_mmap("stmgc's fork support");
 
+    /* Copy each of the segment infos into the new mmap
+     */
+    long i;
+    for (i = 1; i <= NB_SEGMENTS; i++) {
+        struct stm_priv_segment_info_s *src = get_priv_segment(i);
+        char *dst = big_copy + (((char *)src) - stm_object_pages);
+        *(struct stm_priv_segment_info_s *)dst = *src;
+    }
+
+    /* Copy all the data from the two ranges of objects (large, small)
+       into the new mmap --- but only the shared objects
+    */
     uintptr_t pagenum, endpagenum;
     pagenum = END_NURSERY_PAGE;   /* starts after the nursery */
     endpagenum = (uninitialized_page_start - stm_object_pages) / 4096UL;
+    if (endpagenum < NB_PAGES)
+        endpagenum++;   /* the next page too, because it might contain
+                           data from largemalloc */
 
     while (1) {
         if (UNLIKELY(pagenum == endpagenum)) {
@@ -63,9 +80,9 @@
             if (endpagenum == NB_PAGES)
                 break;   /* done */
             pagenum = (uninitialized_page_stop - stm_object_pages) / 4096UL;
+            pagenum--;   /* the prev page too, because it does contain
+                            data from largemalloc */
             endpagenum = NB_PAGES;
-            if (pagenum == endpagenum)
-                break;   /* no pages in the 2nd section, so done too */
         }
 
         pagecopy(big_copy + pagenum * 4096UL,
@@ -82,10 +99,15 @@
     if (stm_object_pages == NULL)
         return;
 
+    /* In the parent, after fork(), we can simply forget about the big copy
+       that we made for the child.
+    */
     assert(fork_big_copy != NULL);
     munmap(fork_big_copy, TOTAL_MEMORY);
     fork_big_copy = NULL;
 
+    dprintf(("forksupport_parent: continuing to run\n"));
+
     mutex_pages_unlock();
     s_mutex_unlock();
 }
@@ -95,32 +117,62 @@
     if (stm_object_pages == NULL)
         return;
 
-    /* xxx the stm_thread_local_t belonging to other threads just leak.
-       Note that stm_all_thread_locals is preserved across a
-       stm_teardown/stm_setup sequence. */
-
+    /* In the child, first unregister all other stm_thread_local_t,
+       mostly as a way to free the memory used by the shadowstacks
+     */
     mutex_pages_unlock();
     s_mutex_unlock();
 
-    stm_thread_local_t *tl = stm_all_thread_locals;
-    do {
-        stm_thread_local_t *nexttl = tl->next;
-        if (tl != fork_this_tl) {
-            stm_unregister_thread_local(tl);
-        }
-        tl = nexttl;
-    } while (tl != stm_all_thread_locals);
+    assert(fork_this_tl != NULL);
+    while (stm_all_thread_locals->next != stm_all_thread_locals) {
+        if (stm_all_thread_locals == fork_this_tl)
+            stm_unregister_thread_local(stm_all_thread_locals->next);
+        else
+            stm_unregister_thread_local(stm_all_thread_locals);
+    }
+    assert(stm_all_thread_locals == fork_this_tl);
 
-    do_or_redo_teardown_after_fork();
+    /* Restore a few things in the child: the new pthread_self(), and
+       the %gs register (although I suppose it should be preserved by
+       fork())
+    */
+    *_get_cpth(fork_this_tl) = pthread_self();
+    set_gs_register(get_segment_base(fork_this_tl->associated_segment_num));
+    fork_this_tl = NULL;
 
+    /* Move the copy of the mmap over the old one, overwriting it
+       and thus freeing the old mapping in this process
+    */
     assert(fork_big_copy != NULL);
     assert(stm_object_pages != NULL);
-    mremap(fork_big_copy, TOTAL_MEMORY, TOTAL_MEMORY,
-           MREMAP_MAYMOVE | MREMAP_FIXED,
-           stm_object_pages);
+    void *res = mremap(fork_big_copy, TOTAL_MEMORY, TOTAL_MEMORY,
+                       MREMAP_MAYMOVE | MREMAP_FIXED,
+                       stm_object_pages);
+    if (res != stm_object_pages)
+        stm_fatalerror("after fork: mremap failed: %m");
     fork_big_copy = NULL;
 
+    /* Call a subset of stm_teardown() / stm_setup() to free and
+       recreate the necessary data in all segments, and to clean up some
+       of the global data like the big arrays that don't make sense any
+       more.  We keep other things like the smallmalloc and largemalloc
+       internal state.
+    */
+    do_or_redo_teardown_after_fork();
     do_or_redo_setup_after_fork();
+
+    /* Make all pages shared again.
+     */
+    mutex_pages_lock();
+    uintptr_t start = END_NURSERY_PAGE;
+    uintptr_t stop  = (uninitialized_page_start - stm_object_pages) / 4096UL;
+    pages_initialize_shared(start, stop - start);
+    start = (uninitialized_page_stop - stm_object_pages) / 4096UL;
+    stop = NB_PAGES;
+    pages_initialize_shared(start, stop - start);
+    mutex_pages_unlock();
+
+    dprintf(("forksupport_child: running one thread now\n"));
 }
 
 
diff --git a/c7/stm/fprintcolor.c b/c7/stm/fprintcolor.c
--- a/c7/stm/fprintcolor.c
+++ b/c7/stm/fprintcolor.c
@@ -8,8 +8,8 @@
     char buffer[2048];
     va_list ap;
     int result;
-    int size = (int)sprintf(buffer, "\033[%dm[%lx] ", dprintfcolor(),
-                            (long)pthread_self());
+    int size = (int)sprintf(buffer, "\033[%dm[%d,%lx] ", dprintfcolor(),
+                            (int)getpid(), (long)pthread_self());
     assert(size >= 0);
 
     va_start(ap, format);
diff --git a/c7/stm/largemalloc.c b/c7/stm/largemalloc.c
--- a/c7/stm/largemalloc.c
+++ b/c7/stm/largemalloc.c
@@ -273,8 +273,10 @@
         /* unlink the following chunk */
         mscan->d.next->prev = mscan->d.prev;
         mscan->d.prev->next = mscan->d.next;
-        assert((mscan->prev_size = (size_t)-258, 1));  /* 0xfffffffffffffefe */
-        assert((mscan->size = (size_t)-515, 1));       /* 0xfffffffffffffdfd */
+#ifndef NDEBUG
+        mscan->prev_size = (size_t)-258;  /* 0xfffffffffffffefe */
+        mscan->size = (size_t)-515;       /* 0xfffffffffffffdfd */
+#endif
 
         /* merge the two chunks */
         assert(fsize == fscan->prev_size);
diff --git a/c7/stm/pages.c b/c7/stm/pages.c
--- a/c7/stm/pages.c
+++ b/c7/stm/pages.c
@@ -107,6 +107,8 @@
        segment 0. */
     uintptr_t i;
     assert(_has_mutex_pages());
+    if (count == 0)
+        return;
     for (i = 1; i <= NB_SEGMENTS; i++) {
         char *segment_base = get_segment_base(i);
         d_remap_file_pages(segment_base + pagenum * 4096UL,
diff --git a/c7/stm/setup.c b/c7/stm/setup.c
--- a/c7/stm/setup.c
+++ b/c7/stm/setup.c
@@ -11,41 +11,36 @@
     if (result == MAP_FAILED)
         stm_fatalerror("%s failed: %m\n", reason);
 
+    return result;
+}
+
+static void do_or_redo_setup_after_fork(void)
+{
     /* The segment 0 is not used to run transactions, but contains the
        shared copy of the pages.  We mprotect all pages before so that
        accesses fail, up to and including the pages corresponding to the
        nurseries of the other segments. */
-    mprotect(result, END_NURSERY_PAGE * 4096UL, PROT_NONE);
+    mprotect(stm_object_pages, END_NURSERY_PAGE * 4096UL, PROT_NONE);
 
     long i;
     for (i = 1; i <= NB_SEGMENTS; i++) {
-        char *segment_base = result + i * (NB_PAGES * 4096UL);
+        char *segment_base = get_segment_base(i);
 
         /* In each segment, the first page is where TLPREFIX'ed
            NULL accesses land.  We mprotect it so that accesses fail. */
         mprotect(segment_base, 4096, PROT_NONE);
 
+        /* Fill the TLS page (page 1) with 0xDC, for debugging */
+        memset(REAL_ADDRESS(segment_base, 4096), 0xDC, 4096);
+        /* Make a "hole" at STM_PSEGMENT (which includes STM_SEGMENT) */
+        memset(REAL_ADDRESS(segment_base, STM_PSEGMENT), 0,
+               sizeof(*STM_PSEGMENT));
+
         /* Pages in range(2, FIRST_READMARKER_PAGE) are never used */
         if (FIRST_READMARKER_PAGE > 2)
             mprotect(segment_base + 8192,
                      (FIRST_READMARKER_PAGE - 2) * 4096UL,
                      PROT_NONE);
-    }
-
-    return result;
-}
-
-static void do_or_redo_setup_after_fork(void)
-{
-    long i;
-    for (i = 1; i <= NB_SEGMENTS; i++) {
-        char *segment_base = get_segment_base(i);
-
-        /* Fill the TLS page (page 1) with 0xDC, for debugging */
-        memset(REAL_ADDRESS(segment_base, 4096), 0xDC, 4096);
-        /* Make a "hole" at STM_PSEGMENT (which includes STM_SEGMENT) */
-        memset(REAL_ADDRESS(segment_base, STM_PSEGMENT), 0,
-               sizeof(*STM_PSEGMENT));
 
         /* Initialize STM_PSEGMENT */
         struct stm_priv_segment_info_s *pr = get_priv_segment(i);
@@ -108,8 +103,8 @@
     long i;
     for (i = 1; i <= NB_SEGMENTS; i++) {
         struct stm_priv_segment_info_s *pr = get_priv_segment(i);
-        assert(pr->objects_pointing_to_nursery == NULL);
-        assert(pr->large_overflow_objects == NULL);
+        LIST_FREE(pr->objects_pointing_to_nursery);
+        LIST_FREE(pr->large_overflow_objects);
         list_free(pr->modified_old_objects);
         list_free(pr->young_weakrefs);
         list_free(pr->old_weakrefs);


More information about the pypy-commit mailing list