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.
#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 */
#endif /* not GC_SAVE_REGISTERS_ON_STACK */
#endif /* GC_MARK_STACK != 0 */
- (*func) (end);
+ (*func) (end, arg);
}
#include <unistd.h>
#endif
+#include <pthread.h>
+
#include "lisp.h"
#include "intervals.h"
#include "window.h"
#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. */
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));
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,
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))
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. */
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 */
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 */
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
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.
return object;
}
\f
-void
+static void
grow_specpdl ()
{
register int count = SPECPDL_INDEX ();
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;
/* 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
#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. */
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;
__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++)
{
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
{
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);
/* 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,
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, ¤t_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;
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);
{
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
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)
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;
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));
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;
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
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);
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)
{