Author: Armin Rigo <[email protected]>
Branch: stmgc-c7
Changeset: r70088:b6ee514a4d42
Date: 2014-03-19 11:05 +0100
http://bitbucket.org/pypy/pypy/changeset/b6ee514a4d42/
Log: Oups, forgot to add this.
diff --git a/rpython/translator/stm/src_stm/stm/forksupport.c
b/rpython/translator/stm/src_stm/stm/forksupport.c
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/src_stm/stm/forksupport.c
@@ -0,0 +1,296 @@
+/* Imported by rpython/translator/stm/import_stmgc.py */
+#ifndef _STM_CORE_H_
+# error "must be compiled via stmgc.c"
+#endif
+
+
+/* XXX this is currently not doing copy-on-write, but simply forces a
+ copy of all pages as soon as fork() is called. */
+
+
+static char *fork_big_copy = NULL;
+static stm_thread_local_t *fork_this_tl;
+static bool fork_was_in_transaction;
+
+static char *setup_mmap(char *reason); /* forward, in setup.c */
+static pthread_t *_get_cpth(stm_thread_local_t *);/* forward, in setup.c */
+
+
+static bool page_is_null(char *p)
+{
+ long *q = (long *)p;
+ long i;
+ for (i = 0; i < 4096 / sizeof(long); i++)
+ if (q[i] != 0)
+ return false;
+ return true;
+}
+
+
+static void forksupport_prepare(void)
+{
+ if (stm_object_pages == NULL)
+ return;
+
+ /* So far we attempt to check this by walking all stm_thread_local_t,
+ marking the one from the current thread, and verifying that it's not
+ running a transaction. This assumes that the stm_thread_local_t is just
+ a __thread variable, so never changes threads.
+ */
+ s_mutex_lock();
+
+ dprintf(("forksupport_prepare\n"));
+
+ stm_thread_local_t *this_tl = NULL;
+ stm_thread_local_t *tl = stm_all_thread_locals;
+ do {
+ if (pthread_equal(*_get_cpth(tl), pthread_self())) {
+ if (this_tl != NULL)
+ stm_fatalerror("fork(): found several stm_thread_local_t"
+ " from the same thread");
+ this_tl = tl;
+ }
+ tl = tl->next;
+ } while (tl != stm_all_thread_locals);
+
+ if (this_tl == NULL)
+ stm_fatalerror("fork(): found no stm_thread_local_t from this thread");
+ s_mutex_unlock();
+
+ bool was_in_transaction = _stm_in_transaction(this_tl);
+ if (was_in_transaction) {
+ stm_become_inevitable("fork");
+ /* Note that the line above can still fail and abort, which should
+ be fine */
+ }
+ else {
+ stm_start_inevitable_transaction(this_tl);
+ }
+
+ s_mutex_lock();
+ synchronize_all_threads();
+ mutex_pages_lock();
+
+ /* 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, nurseries,
+ and associated read markers
+ */
+ long i;
+ for (i = 1; i <= NB_SEGMENTS; i++) {
+ char *src, *dst;
+ struct stm_priv_segment_info_s *psrc = get_priv_segment(i);
+ dst = big_copy + (((char *)psrc) - stm_object_pages);
+ *(struct stm_priv_segment_info_s *)dst = *psrc;
+
+ src = get_segment_base(i) + FIRST_READMARKER_PAGE * 4096UL;
+ dst = big_copy + (src - stm_object_pages);
+ long j;
+ for (j = 0; j < END_NURSERY_PAGE - FIRST_READMARKER_PAGE; j++) {
+ if (!page_is_null(src))
+ pagecopy(dst, src);
+ src += 4096;
+ dst += 4096;
+ }
+ }
+
+ /* Copy all the data from the two ranges of objects (large, small)
+ into the new mmap
+ */
+ 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)) {
+ /* we reach this point usually twice, because there are
+ more pages after 'uninitialized_page_stop' */
+ 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;
+ }
+
+ char *src = stm_object_pages + pagenum * 4096UL;
+ char *dst = big_copy + pagenum * 4096UL;
+ pagecopy(dst, src);
+
+ struct page_shared_s ps = pages_privatized[pagenum - PAGE_FLAG_START];
+ if (ps.by_segment != 0) {
+ long j;
+ for (j = 0; j < NB_SEGMENTS; j++) {
+ src += NB_PAGES * 4096UL;
+ dst += NB_PAGES * 4096UL;
+ if (ps.by_segment & (1 << j)) {
+ pagecopy(dst, src);
+ }
+ }
+ }
+ pagenum++;
+ }
+
+ assert(fork_big_copy == NULL);
+ fork_big_copy = big_copy;
+ fork_this_tl = this_tl;
+ fork_was_in_transaction = was_in_transaction;
+
+ assert(_has_mutex());
+ printf("forksupport_prepare: from %p %p\n", fork_this_tl,
+ fork_this_tl->creating_pthread[0]);
+}
+
+static void forksupport_parent(void)
+{
+ if (stm_object_pages == NULL)
+ return;
+
+ printf("forksupport_parent: continuing to run %p %p\n", fork_this_tl,
+ fork_this_tl->creating_pthread[0]);
+ assert(_has_mutex());
+ assert(_is_tl_registered(fork_this_tl));
+
+ /* 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;
+ bool was_in_transaction = fork_was_in_transaction;
+
+ mutex_pages_unlock();
+ s_mutex_unlock();
+
+ if (!was_in_transaction) {
+ stm_commit_transaction();
+ }
+
+ dprintf(("forksupport_parent: continuing to run\n"));
+}
+
+static void fork_abort_thread(long i)
+{
+ struct stm_priv_segment_info_s *pr = get_priv_segment(i);
+ dprintf(("forksupport_child: abort in seg%ld\n", i));
+ assert(pr->pub.running_thread->associated_segment_num == i);
+ assert(pr->transaction_state == TS_REGULAR);
+ set_gs_register(get_segment_base(i));
+
+ stm_jmpbuf_t jmpbuf;
+ if (__builtin_setjmp(jmpbuf) == 0) {
+ pr->pub.jmpbuf_ptr = &jmpbuf;
+#ifndef NDEBUG
+ pr->running_pthread = pthread_self();
+#endif
+ stm_abort_transaction();
+ }
+}
+
+static void forksupport_child(void)
+{
+ if (stm_object_pages == NULL)
+ return;
+
+ /* this new process contains no other thread, so we can
+ just release these locks early */
+ mutex_pages_unlock();
+ s_mutex_unlock();
+
+ /* 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);
+ 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;
+
+ /* Unregister all other stm_thread_local_t, mostly as a way to free
+ the memory used by the shadowstacks
+ */
+ 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);
+
+ /* Make all pages shared again.
+ */
+ uintptr_t pagenum, endpagenum;
+ pagenum = END_NURSERY_PAGE; /* starts after the nursery */
+ endpagenum = (uninitialized_page_start - stm_object_pages) / 4096UL;
+
+ while (1) {
+ if (UNLIKELY(pagenum == endpagenum)) {
+ /* we reach this point usually twice, because there are
+ more pages after 'uninitialized_page_stop' */
+ if (endpagenum == NB_PAGES)
+ break; /* done */
+ pagenum = (uninitialized_page_stop - stm_object_pages) / 4096UL;
+ endpagenum = NB_PAGES;
+ if (endpagenum == NB_PAGES)
+ break; /* done */
+ }
+
+ struct page_shared_s ps = pages_privatized[pagenum - PAGE_FLAG_START];
+ long j;
+ for (j = 0; j < NB_SEGMENTS; j++) {
+ if (!(ps.by_segment & (1 << j))) {
+ _page_do_reshare(j + 1, pagenum);
+ }
+ }
+ pagenum++;
+ }
+
+ /* Force the interruption of other running segments
+ */
+ long i;
+ for (i = 1; i <= NB_SEGMENTS; i++) {
+ struct stm_priv_segment_info_s *pr = get_priv_segment(i);
+ if (pr->pub.running_thread != NULL &&
+ pr->pub.running_thread != fork_this_tl) {
+ fork_abort_thread(i);
+ }
+ }
+
+ /* Restore a few things: the new pthread_self(), and the %gs
+ register */
+ int segnum = fork_this_tl->associated_segment_num;
+ assert(1 <= segnum && segnum <= NB_SEGMENTS);
+ *_get_cpth(fork_this_tl) = pthread_self();
+ set_gs_register(get_segment_base(segnum));
+ assert(STM_SEGMENT->segment_num == segnum);
+
+ if (!fork_was_in_transaction) {
+ stm_commit_transaction();
+ }
+
+ /* Done */
+ dprintf(("forksupport_child: running one thread now\n"));
+}
+
+
+static void setup_forksupport(void)
+{
+ static bool fork_support_ready = false;
+
+ if (!fork_support_ready) {
+ int res = pthread_atfork(forksupport_prepare, forksupport_parent,
+ forksupport_child);
+ if (res != 0)
+ stm_fatalerror("pthread_atfork() failed: %m");
+ fork_support_ready = true;
+ }
+}
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit