]> git.eshelyaron.com Git - emacs.git/commitdiff
Initial code for buffer locking.
authorTom Tromey <tromey@redhat.com>
Wed, 9 Sep 2009 21:50:06 +0000 (23:50 +0200)
committerGiuseppe Scrivano <gscrivano@gnu.org>
Wed, 9 Sep 2009 21:50:06 +0000 (23:50 +0200)
Add some buffer locking; does not yet actually work.
Make current_buffer per-thread.
Turn thread_state into a vectorlike.
Arrange to populate initial specpdl; put condition-case around
thread body.

src/alloc.c
src/buffer.c
src/buffer.h
src/eval.c
src/image.c
src/thread.c
src/thread.h
src/window.c

index d37295ec89936eb446f003485b9d3c769fe6a9a2..742c17a1b07d4936d64539aa4cf27b7563d7bac3 100644 (file)
@@ -4484,8 +4484,9 @@ mark_stack (bottom, end)
 #endif /* GC_MARK_STACK != 0 */
 
 void
-flush_stack_call_func (func)
-     void (*func) P_ ((char *end));
+flush_stack_call_func (func, arg)
+     void (*func) P_ ((char *end, void *arg));
+     void *arg;
 {
 #if GC_MARK_STACK
   /* jmp_buf may not be aligned enough on darwin-ppc64 */
@@ -4533,7 +4534,7 @@ flush_stack_call_func (func)
 #endif /* not GC_SAVE_REGISTERS_ON_STACK */
 #endif /* GC_MARK_STACK != 0 */
 
-  (*func) (end);
+  (*func) (end, arg);
 }
 
 
index 62c7fad050f34bf1fd38b54e833305a20c12900c..10378c3edf4a986583b9daae72354e11a0a17607 100644 (file)
@@ -37,6 +37,8 @@ extern int errno;
 #include <unistd.h>
 #endif
 
+#include <pthread.h>
+
 #include "lisp.h"
 #include "intervals.h"
 #include "window.h"
@@ -50,8 +52,6 @@ extern int errno;
 #include "keymap.h"
 #include "frame.h"
 
-struct buffer *current_buffer;         /* the current buffer */
-
 /* First buffer in chain of all buffers (in reverse order of creation).
    Threaded through ->next.  */
 
@@ -107,6 +107,10 @@ static char buffer_permanent_local_flags[MAX_PER_BUFFER_VARS];
 
 int last_per_buffer_idx;
 
+/* condition var .. w/ global lock */
+
+static pthread_cond_t buffer_cond;
+
 EXFUN (Fset_buffer, 1);
 void set_buffer_internal P_ ((struct buffer *b));
 void set_buffer_internal_1 P_ ((struct buffer *b));
@@ -331,9 +335,6 @@ get_truename_buffer (filename)
   return Qnil;
 }
 
-/* Incremented for each buffer created, to assign the buffer number. */
-int buffer_count;
-
 DEFUN ("get-buffer-create", Fget_buffer_create, Sget_buffer_create, 1, 1, 0,
        doc: /* Return the buffer specified by BUFFER-OR-NAME, creating a new one if needed.
 If BUFFER-OR-NAME is a string and a live buffer with that name exists,
@@ -1444,6 +1445,10 @@ with SIGHUP.  */)
   if (NILP (b->name))
     return Qnil;
 
+  tem = get_current_thread ();
+  if (!EQ (b->owner, Qnil) && !EQ (b->owner, tem))
+    error ("Buffer locked by another thread");
+
   /* Query if the buffer is still modified.  */
   if (INTERACTIVE && !NILP (b->filename)
       && BUF_MODIFF (b) > BUF_SAVE_MODIFF (b))
@@ -1858,6 +1863,26 @@ set_buffer_internal (b)
     set_buffer_internal_1 (b);
 }
 
+static void
+acquire_buffer (char *end, void *nb)
+{
+  struct buffer *new_buffer = nb;
+
+  /* FIXME this check should be in the caller, for better
+     single-threaded performance.  */
+  if (other_threads_p ())
+    {
+      /* Let other threads try to acquire a buffer.  */
+      pthread_cond_broadcast (&buffer_cond);
+
+      /* If our desired buffer is locked, wait for it.  */
+      while (!EQ (new_buffer->owner, Qnil)
+            /* We set the owner to Qt to mean it is being killed.  */
+            && !EQ (new_buffer->owner, Qt))
+       pthread_cond_wait (&buffer_cond, &global_lock);
+    }
+}
+
 /* Set the current buffer to B, and do not set windows_or_buffers_changed.
    This is used by redisplay.  */
 
@@ -1878,6 +1903,11 @@ set_buffer_internal_1 (b)
     return;
 
   old_buf = current_buffer;
+  if (current_buffer)
+    current_buffer->owner = Qnil;
+  flush_stack_call_func (acquire_buffer, b);
+  /* FIXME: if buffer is killed */
+  b->owner = get_current_thread ();
   current_buffer = b;
   last_known_column_point = -1;   /* invalidate indentation cache */
 
@@ -5210,6 +5240,7 @@ init_buffer_once ()
   buffer_defaults.cursor_type = Qt;
   buffer_defaults.extra_line_spacing = Qnil;
   buffer_defaults.cursor_in_non_selected_windows = Qt;
+  buffer_defaults.owner = Qnil;
 
 #ifdef DOS_NT
   buffer_defaults.buffer_file_type = Qnil; /* TEXT */
@@ -5354,6 +5385,8 @@ init_buffer ()
   Lisp_Object temp;
   int len;
 
+  pthread_cond_init (&buffer_cond, NULL);
+
 #ifdef USE_MMAP_FOR_BUFFERS
  {
    /* When using the ralloc implementation based on mmap(2), buffer
index b16fc4f6a685cd5c2712b5611a69e313a8e6bd76..85c78893d4e55e513cd02ba48e6a2bba5c79c4ce 100644 (file)
@@ -800,12 +800,12 @@ struct buffer
      t means to use hollow box cursor.
      See `cursor-type' for other values.  */
   Lisp_Object cursor_in_non_selected_windows;
+
+  /* If non-nil, the thread holding a lock on this buffer.  */
+  Lisp_Object owner;
 };
 
 \f
-/* This points to the current buffer.  */
-
-extern struct buffer *current_buffer;
 
 /* This structure holds the default values of the buffer-local variables
    that have special slots in each buffer.
index 92b6b8adf8d4f5b329bf99f987abb3371106b191..e0ab399caa3b711151011949ab247e7d95e60345 100644 (file)
@@ -3218,7 +3218,7 @@ DEFUN ("fetch-bytecode", Ffetch_bytecode, Sfetch_bytecode,
   return object;
 }
 \f
-void
+static void
 grow_specpdl ()
 {
   register int count = SPECPDL_INDEX ();
@@ -3268,14 +3268,14 @@ specbind (symbol, value)
       if (BUFFER_LOCAL_VALUEP (valcontents)
          || BUFFER_OBJFWDP (valcontents))
        {
-         Lisp_Object where, current_buffer;
+         Lisp_Object where, self_buffer;
 
-         current_buffer = Fcurrent_buffer ();
+         self_buffer = Fcurrent_buffer ();
 
          /* For a local variable, record both the symbol and which
             buffer's or frame's value we are saving.  */
          if (!NILP (Flocal_variable_p (symbol, Qnil)))
-           where = current_buffer;
+           where = self_buffer;
          else if (BUFFER_LOCAL_VALUEP (valcontents)
                   && XBUFFER_LOCAL_VALUE (valcontents)->found_for_frame)
            where = XBUFFER_LOCAL_VALUE (valcontents)->frame;
@@ -3285,7 +3285,7 @@ specbind (symbol, value)
          /* We're not using the `unused' slot in the specbinding
             structure because this would mean we have to do more
             work for simple variables.  */
-         specpdl_ptr->symbol = Fcons (symbol, Fcons (where, current_buffer));
+         specpdl_ptr->symbol = Fcons (symbol, Fcons (where, self_buffer));
 
          /* If SYMBOL is a per-buffer variable which doesn't have a
             buffer-local value here, make the `let' change the global
index 19aa87692837e7fefc316403057b4a935c67b7d1..4480cc52fbdf370bf85c5482864cf79594d0fef6 100644 (file)
@@ -5555,6 +5555,15 @@ png_image_p (object)
 
 #ifdef HAVE_PNG
 
+/* png.h has a struct with a field named current_buffer.  */
+#undef current_buffer
+
+#if defined HAVE_LIBPNG_PNG_H
+# include <libpng/png.h>
+#else
+# include <png.h>
+#endif
+
 #ifdef HAVE_NTGUI
 /* PNG library details.  */
 
index 44ee3cd71ed85473c37b6212309fbc7efaeaec2e..2f47c7e10372b676bb2af3958cf2dd01c27b360d 100644 (file)
@@ -7,7 +7,7 @@ void mark_byte_stack P_ ((struct byte_stack *));
 void mark_backtrace P_ ((struct backtrace *));
 void mark_catchlist P_ ((struct catchtag *));
 void mark_stack P_ ((char *, char *));
-void flush_stack_call_func P_ ((void (*) (char *)));
+void flush_stack_call_func P_ ((void (*) (char *, void *), void *));
 
 
 static struct thread_state primary_thread;
@@ -16,13 +16,14 @@ static struct thread_state *all_threads = &primary_thread;
 
 __thread struct thread_state *current_thread = &primary_thread;
 
-static pthread_mutex_t global_lock;
+pthread_mutex_t global_lock;
 
 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++)
     {
@@ -54,24 +55,29 @@ mark_one_thread (struct thread_state *thread)
 
   mark_backtrace (thread->m_backtrace_list);
 
-  if (thread->func)
-    mark_object (thread->func);
+  XSETBUFFER (tem, thread->m_current_buffer);
+  mark_object (tem);
 }
 
 static void
-mark_threads_continuation (char *end)
+mark_threads_callback (char *end, void *ignore)
 {
   struct thread_state *iter;
 
   current_thread->stack_top = end;
-  for (iter = all_threads; iter; iter = iter->next)
-    mark_one_thread (iter);
+  for (iter = all_threads; iter; iter = iter->next_thread)
+    {
+      Lisp_Object thread_obj;
+      XSETTHREAD (thread_obj, iter);
+      mark_object (thread_obj);
+      mark_one_thread (iter);
+    }
 }
 
 void
 mark_threads (void)
 {
-  flush_stack_call_func (mark_threads_continuation);
+  flush_stack_call_func (mark_threads_callback, NULL);
 }
 
 void
@@ -79,12 +85,12 @@ unmark_threads (void)
 {
   struct thread_state *iter;
 
-  for (iter = all_threads; iter; iter = iter->next)
+  for (iter = all_threads; iter; iter = iter->next_thread)
     unmark_byte_stack (iter->m_byte_stack_list);
 }
 
 static void
-thread_yield_continuation (char *end)
+thread_yield_callback (char *end, void *ignore)
 {
   current_thread->stack_top = end;
   pthread_mutex_unlock (&global_lock);
@@ -98,8 +104,8 @@ thread_yield (void)
   /* Note: currently it is safe to check this here, but eventually it
      will require a lock to ensure non-racy operation.  */
   /* Only yield if there is another thread to yield to.  */
-  if (all_threads->next)
-    flush_stack_call_func (thread_yield_continuation);
+  if (all_threads->next_thread)
+    flush_stack_call_func (thread_yield_callback, NULL);
 }
 
 DEFUN ("yield", Fyield, Syield, 0, 0, 0,
@@ -109,12 +115,43 @@ DEFUN ("yield", Fyield, Syield, 0, 0, 0,
   thread_yield ();
 }
 
+static Lisp_Object
+invoke_thread_function (void)
+{
+  Lisp_Object iter;
+
+  int count = SPECPDL_INDEX ();
+
+  /* Set up specpdl.  */
+  for (iter = current_thread->initial_specpdl;
+       !EQ (iter, Qnil);
+       iter = XCDR (iter))
+    {
+      /* We may bind a variable twice -- but it doesn't matter because
+        there is no way to undo these bindings without exiting the
+        thread.  */
+      specbind (XCAR (XCAR (iter)), XCDR (XCAR (iter)));
+    }
+  current_thread->initial_specpdl = Qnil;
+
+  Ffuncall (1, &current_thread->func);
+  return unbind_to (count, Qnil);
+}
+
+static Lisp_Object
+do_nothing (Lisp_Object whatever)
+{
+  return whatever;
+}
+
 static void *
 run_thread (void *state)
 {
   char stack_bottom_variable;
   struct thread_state *self = state;
   struct thread_state **iter;
+  struct gcpro gcpro1;
+  Lisp_Object buffer;
 
   self->stack_bottom = &stack_bottom_variable;
 
@@ -128,16 +165,23 @@ run_thread (void *state)
 
   pthread_mutex_lock (&global_lock);
 
-  /* FIXME: unwind protect here.  */
-  Ffuncall (1, &self->func);
+  /* We need special handling to set the initial buffer.  Our parent
+     thread is very likely to be using this same buffer so we will
+     typically wait for the parent thread to release it first.  */
+  XSETBUFFER (buffer, self->m_current_buffer);
+  GCPRO1 (buffer);
+  self->m_current_buffer = 0;
+  set_buffer_internal (XBUFFER (buffer));
+
+  /* It might be nice to do something with errors here.  */
+  internal_condition_case (invoke_thread_function, Qt, do_nothing);
 
   /* Unlink this thread from the list of all threads.  */
-  for (iter = &all_threads; *iter != self; iter = &(*iter)->next)
+  for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread)
     ;
-  *iter = (*iter)->next;
+  *iter = (*iter)->next_thread;
 
   xfree (self->m_specpdl);
-  /* FIXME: other cleanups here.  */
   xfree (self);
 
   pthread_mutex_unlock (&global_lock);
@@ -153,22 +197,67 @@ When the function exits, the thread dies.  */)
 {
   pthread_t thr;
   struct thread_state *new_thread;
+  struct specbinding *p;
 
   /* Can't start a thread in temacs.  */
   if (!initialized)
     abort ();
 
-  new_thread = xmalloc (sizeof (struct thread_state));
-  memset (new_thread, 0, sizeof (struct thread_state));
+  new_thread = (struct thread_state *) allocate_pseudovector (VECSIZE (struct thread_state),
+                                                             2, PVEC_THREAD);
+  memset (new_thread, OFFSETOF (struct thread_state,
+                               m_gcprolist),
+         sizeof (struct thread_state) - OFFSETOF (struct thread_state,
+                                                  m_gcprolist));
 
   new_thread->func = function;
+  new_thread->initial_specpdl = Qnil;
+
+  for (p = specpdl; p != specpdl_ptr; ++p)
+    {
+      if (p->func)
+       {
+         Lisp_Object sym = p->symbol;
+         if (!SYMBOLP (sym))
+           sym = XCAR (sym);
+         new_thread->initial_specpdl
+           = Fcons (Fcons (sym, find_symbol_value (sym)),
+                    new_thread->initial_specpdl);
+       }
+    }
 
   /* We'll need locking here.  */
-  new_thread->next = all_threads;
+  new_thread->next_thread = all_threads;
   all_threads = new_thread;
 
   /* FIXME check result */
   pthread_create (&thr, NULL, run_thread, new_thread);
+
+  return Qnil;
+}
+
+/* Get the current thread as a lisp object.  */
+Lisp_Object
+get_current_thread (void)
+{
+  Lisp_Object result;
+  XSETTHREAD (result, current_thread);
+  return result;
+}
+
+/* Get the main thread as a lisp object.  */
+Lisp_Object
+get_main_thread (void)
+{
+  Lisp_Object result;
+  XSETTHREAD (result, &primary_thread);
+  return result;
+}
+
+int
+other_threads_p (void)
+{
+  return all_threads->next_thread != NULL;
 }
 
 void
index a415727fab21f3adbd10a1b7a1761bce3b2aa2ed..8f7b02ba2dae6467d47439d94ee7a4707c447652 100644 (file)
@@ -1,6 +1,16 @@
 
 struct thread_state
 {
+  EMACS_UINT size;
+  struct Lisp_Vector *next;
+
+  /* The function we are evaluating, or 0 in the main thread.  */
+  Lisp_Object func;
+
+  /* An alias of symbols and values that we use to populate the
+     initial specpdl.  */
+  Lisp_Object initial_specpdl;
+
   /* Recording what needs to be marked for gc.  */
   struct gcpro *m_gcprolist;
 #define gcprolist (current_thread->m_gcprolist)
@@ -54,10 +64,11 @@ struct thread_state
   int m_lisp_eval_depth;
 #define lisp_eval_depth (current_thread->m_lisp_eval_depth)
 
-  /* The function we are evaluating, or 0 in the main thread.  */
-  Lisp_Object func;
+  /* This points to the current buffer.  */
+  struct buffer *m_current_buffer;
+#define current_buffer (current_thread->m_current_buffer)
 
-  struct thread_state *next;
+  struct thread_state *next_thread;
 };
 
 extern __thread struct thread_state *current_thread;
@@ -67,3 +78,11 @@ extern void init_threads P_ ((void));
 extern void thread_yield P_ ((void));
 
 extern void syms_of_threads P_ ((void));
+
+extern Lisp_Object get_current_thread P_ ((void));
+
+extern Lisp_Object get_main_thread P_ ((void));
+
+extern pthread_mutex_t global_lock;
+
+extern int other_threads_p P_ ((void));
index a3964f3fb74bd41d5dd951a5fe7afb2be3dee80a..60b50e0998f9ad846bdc8053209374adf2ad61ba 100644 (file)
@@ -5915,7 +5915,7 @@ struct save_window_data
     struct Lisp_Vector *next_from_Lisp_Vector_struct;
     Lisp_Object selected_frame;
     Lisp_Object current_window;
-    Lisp_Object current_buffer;
+    Lisp_Object m_current_buffer;
     Lisp_Object minibuf_scroll_window;
     Lisp_Object minibuf_selected_window;
     Lisp_Object root_window;
@@ -6001,7 +6001,7 @@ the return value is nil.  Otherwise the value is t.  */)
   data = (struct save_window_data *) XVECTOR (configuration);
   saved_windows = XVECTOR (data->saved_windows);
 
-  new_current_buffer = data->current_buffer;
+  new_current_buffer = data->m_current_buffer;
   if (NILP (XBUFFER (new_current_buffer)->name))
     new_current_buffer = Qnil;
   else
@@ -6526,7 +6526,7 @@ redirection (see `redirect-frame-focus').  */)
   data->frame_tool_bar_lines = FRAME_TOOL_BAR_LINES (f);
   data->selected_frame = selected_frame;
   data->current_window = FRAME_SELECTED_WINDOW (f);
-  XSETBUFFER (data->current_buffer, current_buffer);
+  XSETBUFFER (data->m_current_buffer, current_buffer);
   data->minibuf_scroll_window = minibuf_level > 0 ? Vminibuf_scroll_window : Qnil;
   data->minibuf_selected_window = minibuf_level > 0 ? minibuf_selected_window : Qnil;
   data->root_window = FRAME_ROOT_WINDOW (f);
@@ -7047,7 +7047,7 @@ compare_window_configurations (c1, c2, ignore_positions)
     return 0;
   /* Don't compare the current_window field directly.
      Instead see w1_is_current and w2_is_current, below.  */
-  if (! EQ (d1->current_buffer, d2->current_buffer))
+  if (! EQ (d1->m_current_buffer, d2->m_current_buffer))
     return 0;
   if (! ignore_positions)
     {