[pypy-svn] pypy default: Free memory allocated to shadow stacks after a fork().
arigo
commits-noreply at bitbucket.org
Wed Feb 2 16:33:32 CET 2011
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r41563:d2d23acf30a9
Date: 2011-02-01 15:00 +0100
http://bitbucket.org/pypy/pypy/changeset/d2d23acf30a9/
Log: Free memory allocated to shadow stacks after a fork(). (not tested
so far...)
diff --git a/pypy/module/thread/ll_thread.py b/pypy/module/thread/ll_thread.py
--- a/pypy/module/thread/ll_thread.py
+++ b/pypy/module/thread/ll_thread.py
@@ -1,6 +1,6 @@
from pypy.rpython.lltypesystem import rffi
-from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.rpython.tool import rffi_platform as platform
from pypy.translator.tool.cbuild import ExternalCompilationInfo
import py, os
@@ -152,7 +152,7 @@
# ____________________________________________________________
#
# Thread integration.
-# These are three completely ad-hoc operations at the moment.
+# These are five completely ad-hoc operations at the moment.
def gc_thread_prepare():
"""To call just before thread.start_new_thread(). This
@@ -179,3 +179,20 @@
if we_are_translated():
llop.gc_thread_die(lltype.Void)
gc_thread_die._always_inline_ = True
+
+def gc_thread_before_fork():
+ """To call just before fork(). Prepares for forking, after
+ which only the current thread will be alive.
+ """
+ if we_are_translated():
+ return llop.gc_thread_before_fork(llmemory.Address)
+ else:
+ return llmemory.NULL
+
+def gc_thread_after_fork(result_of_fork, opaqueaddr):
+ """To call just after fork().
+ """
+ if we_are_translated():
+ llop.gc_thread_after_fork(lltype.Void, result_of_fork, opaqueaddr)
+ else:
+ assert opaqueaddr == llmemory.NULL
diff --git a/pypy/rpython/lltypesystem/lloperation.py b/pypy/rpython/lltypesystem/lloperation.py
--- a/pypy/rpython/lltypesystem/lloperation.py
+++ b/pypy/rpython/lltypesystem/lloperation.py
@@ -466,6 +466,8 @@
# allocating non-GC structures only
'gc_thread_run' : LLOp(),
'gc_thread_die' : LLOp(),
+ 'gc_thread_before_fork':LLOp(), # returns an opaque address
+ 'gc_thread_after_fork': LLOp(), # arguments: (result_of_fork, opaqueaddr)
'gc_assume_young_pointers': LLOp(canrun=True),
'gc_writebarrier_before_copy': LLOp(canrun=True),
'gc_heap_stats' : LLOp(canunwindgc=True),
diff --git a/pypy/rpython/module/ll_os.py b/pypy/rpython/module/ll_os.py
--- a/pypy/rpython/module/ll_os.py
+++ b/pypy/rpython/module/ll_os.py
@@ -1531,14 +1531,17 @@
@registering_if(os, 'fork')
def register_os_fork(self):
+ from pypy.module.thread import ll_thread
eci = self.gcc_profiling_bug_workaround('pid_t _noprof_fork(void)',
'return fork();')
os_fork = self.llexternal('_noprof_fork', [], rffi.PID_T,
compilation_info = eci,
- threadsafe = False)
+ _nowrapper = True)
def fork_llimpl():
+ opaqueaddr = ll_thread.gc_thread_before_fork()
childpid = rffi.cast(lltype.Signed, os_fork())
+ ll_thread.gc_thread_after_fork(childpid, opaqueaddr)
if childpid == -1:
raise OSError(rposix.get_errno(), "os_fork failed")
return rffi.cast(lltype.Signed, childpid)
diff --git a/pypy/translator/c/gc.py b/pypy/translator/c/gc.py
--- a/pypy/translator/c/gc.py
+++ b/pypy/translator/c/gc.py
@@ -92,6 +92,12 @@
def OP_GC_THREAD_DIE(self, funcgen, op):
return ''
+ def OP_GC_THREAD_BEFORE_FORK(self, funcgen, op):
+ return '%s = NULL;' % funcgen.expr(op.result)
+
+ def OP_GC_THREAD_AFTER_FORK(self, funcgen, op):
+ return ''
+
def OP_GC_ASSUME_YOUNG_POINTERS(self, funcgen, op):
return ''
diff --git a/pypy/rpython/memory/gctransform/framework.py b/pypy/rpython/memory/gctransform/framework.py
--- a/pypy/rpython/memory/gctransform/framework.py
+++ b/pypy/rpython/memory/gctransform/framework.py
@@ -960,6 +960,22 @@
if hasattr(self.root_walker, 'thread_die_ptr'):
hop.genop("direct_call", [self.root_walker.thread_die_ptr])
+ def gct_gc_thread_before_fork(self, hop):
+ assert self.translator.config.translation.thread
+ if hasattr(self.root_walker, 'thread_before_fork_ptr'):
+ hop.genop("direct_call", [self.root_walker.thread_before_fork_ptr],
+ resultvar=hop.spaceop.result)
+ else:
+ c_null = rmodel.inputconst(llmemory.Address, llmemory.NULL)
+ hop.genop("same_as", [c_null],
+ resultvar=hop.spaceop.result)
+
+ def gct_gc_thread_after_fork(self, hop):
+ assert self.translator.config.translation.thread
+ if hasattr(self.root_walker, 'thread_after_fork_ptr'):
+ hop.genop("direct_call", [self.root_walker.thread_after_fork_ptr]
+ + hop.spaceop.args)
+
def gct_gc_get_type_info_group(self, hop):
return hop.cast_result(self.c_type_info_group)
@@ -1435,7 +1451,7 @@
gcdata.active_thread = new_aid
def collect_stack(aid, stacktop, callback):
- if stacktop != llmemory.NULL and aid != get_aid():
+ if stacktop != llmemory.NULL and aid != gcdata.active_thread:
# collect all valid stacks from the dict (the entry
# corresponding to the current thread is not valid)
gc = self.gc
@@ -1447,11 +1463,41 @@
addr += sizeofaddr
def collect_more_stacks(callback):
+ ll_assert(get_aid() == gcdata.active_thread,
+ "collect_more_stacks(): invalid active_thread")
gcdata.thread_stacks.foreach(collect_stack, callback)
+ def _free_if_not_current(aid, stacktop, _):
+ if stacktop != llmemory.NULL and aid != gcdata.active_thread:
+ end = stacktop - sizeofaddr
+ base = end.address[0]
+ llmemory.raw_free(base)
+
+ def thread_after_fork(result_of_fork, opaqueaddr):
+ # we don't need a thread_before_fork in this case, so
+ # opaqueaddr == NULL. This is called after fork().
+ if result_of_fork == 0:
+ # We are in the child process. Assumes that only the
+ # current thread survived, so frees the shadow stacks
+ # of all the other ones.
+ gcdata.thread_stacks.foreach(_free_if_not_current, None)
+ # Clears the dict (including the current thread, which
+ # was an invalid entry anyway and will be recreated by
+ # the next call to save_away_current_stack()).
+ gcdata.thread_stacks.clear()
+ # Finally, reset the stored thread IDs, in case it
+ # changed because of fork().
+ aid = get_aid()
+ gcdata.main_thread = aid
+ gcdata.active_thread = aid
+
self.thread_setup = thread_setup
self.thread_prepare_ptr = getfn(thread_prepare, [], annmodel.s_None)
self.thread_run_ptr = getfn(thread_run, [], annmodel.s_None,
inline=True)
self.thread_die_ptr = getfn(thread_die, [], annmodel.s_None)
+ self.thread_after_fork_ptr = getfn(thread_after_fork,
+ [annmodel.SomeInteger(),
+ annmodel.SomeAddress()],
+ annmodel.s_None)
self.collect_stacks_from_other_threads = collect_more_stacks
More information about the Pypy-commit
mailing list