emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 02/10] make GC thread-ready


From: Tom Tromey
Subject: [PATCH 02/10] make GC thread-ready
Date: Thu, 09 Aug 2012 13:37:43 -0600
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1 (gnu/linux)

This parameterizes the GC a bit to make it thread-ready.

The basic idea is that whenever a thread "exits lisp" -- that is,
releases the global lock in favor of another thread -- it must save
its stack boundaries in the thread object.  This way the boundaries
are always available for marking.  This is the purpose of
flush_stack_call_func.

I haven't tested this under all the possible GC configurations.
There is a new FIXME in a spot that i didn't convert.

Arguably all_threads should go in the previous patch.
---
 src/alloc.c    |   78 ++++++++++++++++++------------------------------------
 src/bytecode.c |   11 +++-----
 src/eval.c     |   13 +++++++++
 src/lisp.h     |   18 +++++++++---
 src/thread.c   |   79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/thread.h   |    5 +++
 6 files changed, 140 insertions(+), 64 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index 0fdf7da..b3dab59 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -379,7 +379,6 @@ static struct mem_node mem_z;
 
 static struct Lisp_Vector *allocate_vectorlike (ptrdiff_t);
 static void lisp_free (void *);
-static void mark_stack (void);
 static int live_vector_p (struct mem_node *, void *);
 static int live_buffer_p (struct mem_node *, void *);
 static int live_string_p (struct mem_node *, void *);
@@ -4857,8 +4856,27 @@ dump_zombies (void)
    would be necessary, each one starting with one byte more offset
    from the stack start.  */
 
-static void
-mark_stack (void)
+void
+mark_stack (char *bottom, char *end)
+{
+  /* This assumes that the stack is a contiguous region in memory.  If
+     that's not the case, something has to be done here to iterate
+     over the stack segments.  */
+  mark_memory (bottom, end);
+
+  /* Allow for marking a secondary stack, like the register stack on the
+     ia64.  */
+#ifdef GC_MARK_SECONDARY_STACK
+  GC_MARK_SECONDARY_STACK ();
+#endif
+
+#if GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS
+  check_gcpros ();
+#endif
+}
+
+void
+flush_stack_call_func (void (*func) (void *arg), void *arg)
 {
   void *end;
 
@@ -4914,20 +4932,8 @@ mark_stack (void)
 #endif /* not GC_SAVE_REGISTERS_ON_STACK */
 #endif /* not HAVE___BUILTIN_UNWIND_INIT */
 
-  /* This assumes that the stack is a contiguous region in memory.  If
-     that's not the case, something has to be done here to iterate
-     over the stack segments.  */
-  mark_memory (stack_bottom, end);
-
-  /* Allow for marking a secondary stack, like the register stack on the
-     ia64.  */
-#ifdef GC_MARK_SECONDARY_STACK
-  GC_MARK_SECONDARY_STACK ();
-#endif
-
-#if GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS
-  check_gcpros ();
-#endif
+  current_thread->stack_top = end;
+  (*func) (arg);
 }
 
 #endif /* GC_MARK_STACK != 0 */
@@ -5449,11 +5455,7 @@ See Info node `(elisp)Garbage Collection'.  */)
   for (i = 0; i < staticidx; i++)
     mark_object (*staticvec[i]);
 
-  for (bind = specpdl; bind != specpdl_ptr; bind++)
-    {
-      mark_object (bind->symbol);
-      mark_object (bind->old_value);
-    }
+  mark_threads ();
   mark_terminals ();
   mark_kboards ();
   mark_ttys ();
@@ -5465,40 +5467,12 @@ See Info node `(elisp)Garbage Collection'.  */)
   }
 #endif
 
-#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
-     || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
-  mark_stack ();
-#else
-  {
-    register struct gcpro *tail;
-    for (tail = gcprolist; tail; tail = tail->next)
-      for (i = 0; i < tail->nvars; i++)
-       mark_object (tail->var[i]);
-  }
-  mark_byte_stack ();
-  {
-    struct catchtag *catch;
-    struct handler *handler;
-
-  for (catch = catchlist; catch; catch = catch->next)
-    {
-      mark_object (catch->tag);
-      mark_object (catch->val);
-    }
-  for (handler = handlerlist; handler; handler = handler->next)
-    {
-      mark_object (handler->handler);
-      mark_object (handler->var);
-    }
-  }
-  mark_backtrace ();
-#endif
-
 #ifdef HAVE_WINDOW_SYSTEM
   mark_fringe_data ();
 #endif
 
 #if GC_MARK_STACK == GC_USE_GCPROS_CHECK_ZOMBIES
+  FIXME;
   mark_stack ();
 #endif
 
@@ -5548,7 +5522,7 @@ See Info node `(elisp)Garbage Collection'.  */)
 
   /* Clear the mark bits that we set in certain root slots.  */
 
-  unmark_byte_stack ();
+  unmark_threads ();
   VECTOR_UNMARK (&buffer_defaults);
   VECTOR_UNMARK (&buffer_local_symbols);
 
diff --git a/src/bytecode.c b/src/bytecode.c
index 0194594..d61e37d 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -335,12 +335,11 @@ struct byte_stack
 
 #if BYTE_MARK_STACK
 void
-mark_byte_stack (void)
+mark_byte_stack (struct byte_stack *stack)
 {
-  struct byte_stack *stack;
   Lisp_Object *obj;
 
-  for (stack = byte_stack_list; stack; stack = stack->next)
+  for (; stack; stack = stack->next)
     {
       /* If STACK->top is null here, this means there's an opcode in
         Fbyte_code that wasn't expected to GC, but did.  To find out
@@ -364,11 +363,9 @@ mark_byte_stack (void)
    counters.  Called when GC has completed.  */
 
 void
-unmark_byte_stack (void)
+unmark_byte_stack (struct byte_stack *stack)
 {
-  struct byte_stack *stack;
-
-  for (stack = byte_stack_list; stack; stack = stack->next)
+  for (; stack; stack = stack->next)
     {
       if (stack->byte_string_start != SDATA (stack->byte_string))
        {
diff --git a/src/eval.c b/src/eval.c
index 768cdc1..49ead49 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -165,6 +165,19 @@ init_eval (void)
   when_entered_debugger = -1;
 }
 
+#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
+     || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
+void
+mark_catchlist (struct catchtag *catch)
+{
+  for (; catch; catch = catch->next)
+    {
+      mark_object (catch->tag);
+      mark_object (catch->val);
+    }
+}
+#endif
+
 /* Unwind-protect function used by call_debugger.  */
 
 static Lisp_Object
diff --git a/src/lisp.h b/src/lisp.h
index 2cf8499..db8d9b4 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -2714,6 +2714,10 @@ extern void mark_object (Lisp_Object);
 #if defined REL_ALLOC && !defined SYSTEM_MALLOC
 extern void refill_memory_reserve (void);
 #endif
+#if GC_MARK_STACK
+extern void mark_stack (char *, char *);
+#endif
+extern void flush_stack_call_func (void (*func) (void *arg), void *arg);
 extern const char *pending_malloc_warning;
 extern Lisp_Object zero_vector;
 extern EMACS_INT consing_since_gc;
@@ -2901,6 +2905,10 @@ extern Lisp_Object Vautoload_queue;
 extern Lisp_Object Vsignaling_function;
 extern Lisp_Object inhibit_lisp_code;
 extern int handling_signal;
+#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
+     || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
+extern void mark_catchlist (struct catchtag *);
+#endif
 /* To run a normal hook, use the appropriate function from the list below.
    The calling convention:
 
@@ -2950,11 +2958,11 @@ extern Lisp_Object safe_call (ptrdiff_t, Lisp_Object, 
...);
 extern Lisp_Object safe_call1 (Lisp_Object, Lisp_Object);
 extern Lisp_Object safe_call2 (Lisp_Object, Lisp_Object, Lisp_Object);
 extern void init_eval (void);
-#if BYTE_MARK_STACK
-extern void mark_backtrace (void);
-#endif
 extern void syms_of_eval (void);
 
+/* Defined in thread.c.  */
+extern void mark_threads (void);
+
 /* Defined in editfns.c.  */
 extern Lisp_Object Qfield;
 extern void insert1 (Lisp_Object);
@@ -3210,9 +3218,9 @@ extern int read_bytecode_char (int);
 extern Lisp_Object Qbytecode;
 extern void syms_of_bytecode (void);
 #if BYTE_MARK_STACK
-extern void mark_byte_stack (void);
+extern void mark_byte_stack (struct byte_stack *);
 #endif
-extern void unmark_byte_stack (void);
+extern void unmark_byte_stack (struct byte_stack *);
 extern Lisp_Object exec_byte_code (Lisp_Object, Lisp_Object, Lisp_Object,
                                   Lisp_Object, ptrdiff_t, Lisp_Object *);
 
diff --git a/src/thread.c b/src/thread.c
index 0bd97b4..ba2d663 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -24,3 +24,82 @@ along with GNU Emacs.  If not, see 
<http://www.gnu.org/licenses/>.  */
 struct thread_state the_only_thread;
 
 struct thread_state *current_thread = &the_only_thread;
+
+struct thread_state *all_threads = &the_only_thread;
+
+static void
+mark_one_thread (struct thread_state *thread)
+{
+  register struct specbinding *bind;
+  struct handler *handler;
+  Lisp_Object tem;
+
+  for (bind = thread->m_specpdl; bind != thread->m_specpdl_ptr; bind++)
+    {
+      mark_object (bind->symbol);
+      mark_object (bind->old_value);
+    }
+
+#if (GC_MARK_STACK == GC_MAKE_GCPROS_NOOPS \
+     || GC_MARK_STACK == GC_MARK_STACK_CHECK_GCPROS)
+  mark_stack (thread->m_stack_bottom, thread->stack_top);
+#else
+  {
+    register struct gcpro *tail;
+    for (tail = thread->m_gcprolist; tail; tail = tail->next)
+      for (i = 0; i < tail->nvars; i++)
+       mark_object (tail->var[i]);
+  }
+
+#if BYTE_MARK_STACK
+  if (thread->m_byte_stack_list)
+    mark_byte_stack (thread->m_byte_stack_list);
+#endif
+
+  mark_catchlist (thread->m_catchlist);
+
+  for (handler = thread->m_handlerlist; handler; handler = handler->next)
+    {
+      mark_object (handler->handler);
+      mark_object (handler->var);
+    }
+
+  mark_backtrace (thread->m_backtrace_list);
+#endif
+
+  if (thread->m_current_buffer)
+    {
+      XSETBUFFER (tem, thread->m_current_buffer);
+      mark_object (tem);
+    }
+
+  mark_object (thread->m_last_thing_searched);
+
+  if (thread->m_saved_last_thing_searched)
+    mark_object (thread->m_saved_last_thing_searched);
+}
+
+static void
+mark_threads_callback (void *ignore)
+{
+  struct thread_state *iter;
+
+  for (iter = all_threads; iter; iter = iter->next_thread)
+    mark_one_thread (iter);
+}
+
+void
+mark_threads (void)
+{
+  flush_stack_call_func (mark_threads_callback, NULL);
+}
+
+void
+unmark_threads (void)
+{
+  struct thread_state *iter;
+
+  for (iter = all_threads; iter; iter = iter->next_thread)
+    if (iter->m_byte_stack_list)
+      unmark_byte_stack (iter->m_byte_stack_list);
+}
diff --git a/src/thread.h b/src/thread.h
index b2eb04d..6d61d0e 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -133,8 +133,13 @@ struct thread_state
   /* Regexp to use to replace spaces, or NULL meaning don't.  */
   /*re_char*/ unsigned char *m_whitespace_regexp;
 #define whitespace_regexp (current_thread->m_whitespace_regexp)
+
+  /* Threads are kept on a linked list.  */
+  struct thread_state *next_thread;
 };
 
 extern struct thread_state *current_thread;
 
+extern void unmark_threads (void);
+
 #endif /* THREAD_H */
-- 
1.7.7.6




reply via email to

[Prev in Thread] Current Thread [Next in Thread]