all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* [PATCH 06/10] add most lisp-level features
@ 2012-08-09 19:41 Tom Tromey
  2012-08-10  1:45 ` Daniel Colascione
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2012-08-09 19:41 UTC (permalink / raw)
  To: Emacs discussions

This adds most of the thread features visible to emacs lisp.

I roughly followed the Bordeaux threads API:

http://trac.common-lisp.net/bordeaux-threads/wiki/ApiDocumentation

... but not identically.  In particular I chose not to implement
interrupt-thread or destroy-thread, but instead a thread-signalling
approach.

I'm still undecided about *default-special-bindings* (which I did not
implement).  I think it would be more emacs-like to capture the let
bindings at make-thread time, but IIRC Stefan didn't like this idea
the first time around.

There are one or two semantics issues pointed out in the patch where I
could use some advice.

I see a couple of bits that should probably be in the previous patch.
Sorry about that.
---
 src/alloc.c     |    3 +
 src/data.c      |   15 +++
 src/emacs.c     |    2 +
 src/lisp.h      |    5 +
 src/systhread.c |   15 +--
 src/thread.c    |  354 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 src/thread.h    |   25 ++++
 7 files changed, 400 insertions(+), 19 deletions(-)

diff --git a/src/alloc.c b/src/alloc.c
index b3dab59..d114202 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3094,6 +3094,9 @@ sweep_vectors (void)
 	      ptrdiff_t nbytes = PSEUDOVECTOR_NBYTES (vector);
 	      ptrdiff_t total_bytes = nbytes;
 
+	      if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
+		finalize_one_thread ((struct thread_state *) vector);
+
 	      next = ADVANCE (vector, nbytes);
 
 	      /* While NEXT is not marked, try to coalesce with VECTOR,
diff --git a/src/data.c b/src/data.c
index c4519fc..162c46e 100644
--- a/src/data.c
+++ b/src/data.c
@@ -94,6 +94,7 @@ static Lisp_Object Qchar_table, Qbool_vector, Qhash_table;
 static Lisp_Object Qsubrp, Qmany, Qunevalled;
 Lisp_Object Qfont_spec, Qfont_entity, Qfont_object;
 static Lisp_Object Qdefun;
+Lisp_Object Qthread;
 
 Lisp_Object Qinteractive_form;
 
@@ -211,6 +212,8 @@ for example, (type-of 1) returns `integer'.  */)
 	return Qfont_entity;
       if (FONT_OBJECT_P (object))
 	return Qfont_object;
+      if (THREADP (object))
+	return Qthread;
       return Qvector;
 
     case Lisp_Float:
@@ -458,6 +461,16 @@ DEFUN ("floatp", Ffloatp, Sfloatp, 1, 1, 0,
   return Qnil;
 }
 
+DEFUN ("threadp", Fthreadp, Sthreadp, 1, 1, 0,
+       doc: /* Return t if OBJECT is a thread.  */)
+  (Lisp_Object object)
+{
+  if (THREADP (object))
+    return Qt;
+  else
+    return Qnil;
+}
+
 \f
 /* Extract and set components of lists */
 
@@ -3089,6 +3102,7 @@ syms_of_data (void)
   DEFSYM (Qchar_table, "char-table");
   DEFSYM (Qbool_vector, "bool-vector");
   DEFSYM (Qhash_table, "hash-table");
+  DEFSYM (Qthread, "thread");
   /* Used by Fgarbage_collect.  */
   DEFSYM (Qinterval, "interval");
   DEFSYM (Qmisc, "misc");
@@ -3131,6 +3145,7 @@ syms_of_data (void)
   defsubr (&Ssubrp);
   defsubr (&Sbyte_code_function_p);
   defsubr (&Schar_or_string_p);
+  defsubr (&Sthreadp);
   defsubr (&Scar);
   defsubr (&Scdr);
   defsubr (&Scar_safe);
diff --git a/src/emacs.c b/src/emacs.c
index ca9f201..9255252 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1552,6 +1552,8 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
       syms_of_ntterm ();
 #endif /* WINDOWSNT */
 
+      syms_of_threads ();
+
       keys_of_casefiddle ();
       keys_of_cmds ();
       keys_of_buffer ();
diff --git a/src/lisp.h b/src/lisp.h
index 7d0a3dc..c00b775 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -553,6 +553,7 @@ clip_to_bounds (ptrdiff_t lower, EMACS_INT num, ptrdiff_t upper)
 #define XBOOL_VECTOR(a) (eassert (BOOL_VECTOR_P (a)), \
 			 ((struct Lisp_Bool_Vector *) \
 			  XUNTAG (a, Lisp_Vectorlike)))
+#define XTHREAD(a) (eassert (THREADP (a)), (struct thread_state *) XPNTR(a))
 
 /* Construct a Lisp_Object from a value or address.  */
 
@@ -1821,6 +1822,9 @@ typedef struct {
 #define CHECK_OVERLAY(x) \
   CHECK_TYPE (OVERLAYP (x), Qoverlayp, x)
 
+#define CHECK_THREAD(x) \
+  CHECK_TYPE (THREADP (x), Qthreadp, x)
+
 /* Since we can't assign directly to the CAR or CDR fields of a cons
    cell, use these when checking that those fields contain numbers.  */
 #define CHECK_NUMBER_CAR(x) \
@@ -2443,6 +2447,7 @@ extern Lisp_Object Qchar_or_string_p, Qmarkerp, Qinteger_or_marker_p, Qvectorp;
 extern Lisp_Object Qbuffer_or_string_p;
 extern Lisp_Object Qfboundp;
 extern Lisp_Object Qchar_table_p, Qvector_or_char_table_p;
+extern Lisp_Object Qthreadp;
 
 extern Lisp_Object Qcdr;
 
diff --git a/src/systhread.c b/src/systhread.c
index d5f00ba..e6a9199 100644
--- a/src/systhread.c
+++ b/src/systhread.c
@@ -105,19 +105,12 @@ lisp_mutex_lock (lisp_mutex_t *mutex)
     }
 
   self = current_thread;
-  while (mutex->owner != NULL /* && EQ (self->error_symbol, Qnil) */)
+  self->wait_condvar = &mutex->condition;
+  while (mutex->owner != NULL && EQ (self->error_symbol, Qnil))
     pthread_cond_wait (&mutex->condition, &global_lock);
+  self->wait_condvar = NULL;
 
-#if 0
-  if (!EQ (self->error_symbol, Qnil))
-    {
-      Lisp_Object error_symbol = self->error_symbol;
-      Lisp_Object data = self->error_data;
-      self->error_symbol = Qnil;
-      self->error_data = Qnil;
-      Fsignal (error_symbol, error_data);
-    }
-#endif
+  post_acquire_global_lock (self);
 
   mutex->owner = self;
   mutex->count = 1;
diff --git a/src/thread.c b/src/thread.c
index 7d2f81e..5da2e10 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -20,15 +20,70 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <config.h>
 #include <setjmp.h>
 #include "lisp.h"
+#include "character.h"
+#include "buffer.h"
 
-struct thread_state the_only_thread;
+/* FIXME */
+extern void unbind_for_thread_switch (void);
+extern void rebind_for_thread_switch (void);
 
-struct thread_state *current_thread = &the_only_thread;
+static struct thread_state primary_thread;
 
-struct thread_state *all_threads = &the_only_thread;
+struct thread_state *current_thread = &primary_thread;
+
+static struct thread_state *all_threads = &primary_thread;
 
 sys_mutex_t global_lock;
 
+Lisp_Object Qthreadp;
+
+\f
+
+static void
+release_global_lock (void)
+{
+  sys_mutex_unlock (&global_lock);
+}
+
+/* You must call this after acquiring the global lock.
+   acquire_global_lock does it for you.  */
+void
+post_acquire_global_lock (struct thread_state *self)
+{
+  Lisp_Object buffer;
+
+  if (self != current_thread)
+    {
+      unbind_for_thread_switch ();
+      current_thread = self;
+      rebind_for_thread_switch ();
+    }
+
+  /* We need special handling to re-set the buffer.  */
+  XSETBUFFER (buffer, self->m_current_buffer);
+  self->m_current_buffer = 0;
+  set_buffer_internal (XBUFFER (buffer));
+
+  if (!EQ (current_thread->error_symbol, Qnil))
+    {
+      Lisp_Object sym = current_thread->error_symbol;
+      Lisp_Object data = current_thread->error_data;
+
+      current_thread->error_symbol = Qnil;
+      current_thread->error_data = Qnil;
+      Fsignal (sym, data);
+    }
+}
+
+static void
+acquire_global_lock (struct thread_state *self)
+{
+  sys_mutex_lock (&global_lock);
+  post_acquire_global_lock (self);
+}
+
+\f
+
 static void
 mark_one_thread (struct thread_state *thread)
 {
@@ -113,19 +168,302 @@ unmark_threads (void)
       unmark_byte_stack (iter->m_byte_stack_list);
 }
 
+\f
+
+static void
+yield_callback (void *ignore)
+{
+  struct thread_state *self = current_thread;
+
+  release_global_lock ();
+  sys_thread_yield ();
+  acquire_global_lock (self);
+}
+
+DEFUN ("thread-yield", Fthread_yield, Sthread_yield, 0, 0, 0,
+       doc: /* Yield the CPU to another thread.  */)
+     (void)
+{
+  flush_stack_call_func (yield_callback, NULL);
+  return Qnil;
+}
+
+static Lisp_Object
+invoke_thread_function (void)
+{
+  Lisp_Object iter;
+
+  int count = SPECPDL_INDEX ();
+
+  Ffuncall (1, &current_thread->function);
+  return unbind_to (count, Qnil);
+}
+
+static Lisp_Object
+do_nothing (Lisp_Object whatever)
+{
+  return whatever;
+}
+
+static void *
+run_thread (void *state)
+{
+  char stack_pos;
+  struct thread_state *self = state;
+  struct thread_state **iter;
+
+  self->m_stack_bottom = &stack_pos;
+  self->stack_top = self->m_stack_bottom = &stack_pos;
+  self->thread_id = sys_thread_self ();
+
+  acquire_global_lock (self);
+
+  /* It might be nice to do something with errors here.  */
+  internal_condition_case (invoke_thread_function, Qt, do_nothing);
+
+  unbind_for_thread_switch ();
+
+  /* Unlink this thread from the list of all threads.  */
+  for (iter = &all_threads; *iter != self; iter = &(*iter)->next_thread)
+    ;
+  *iter = (*iter)->next_thread;
+
+  self->m_last_thing_searched = Qnil;
+  self->m_saved_last_thing_searched = Qnil;
+  self->name = Qnil;
+  self->function = Qnil;
+  self->error_symbol = Qnil;
+  self->error_data = Qnil;
+  xfree (self->m_specpdl);
+  self->m_specpdl = NULL;
+  self->m_specpdl_ptr = NULL;
+  self->m_specpdl_size = 0;
+
+  sys_cond_broadcast (&self->thread_condvar);
+
+  release_global_lock ();
+
+  return NULL;
+}
+
 void
-init_threads_once (void)
+finalize_one_thread (struct thread_state *state)
 {
-  the_only_thread.header.size
+  sys_cond_destroy (&state->thread_condvar);
+}
+
+DEFUN ("make-thread", Fmake_thread, Smake_thread, 1, 2, 0,
+       doc: /* Start a new thread and run FUNCTION in it.
+When the function exits, the thread dies.
+If NAME is given, it names the new thread.  */)
+  (Lisp_Object function, Lisp_Object name)
+{
+  sys_thread_t thr;
+  struct thread_state *new_thread;
+  Lisp_Object result;
+
+  /* Can't start a thread in temacs.  */
+  if (!initialized)
+    abort ();
+
+  new_thread = ALLOCATE_PSEUDOVECTOR (struct thread_state, m_gcprolist,
+				      PVEC_THREAD);
+  memset ((char *) new_thread + offsetof (struct thread_state, m_gcprolist),
+	  0, sizeof (struct thread_state) - offsetof (struct thread_state,
+						      m_gcprolist));
+
+  new_thread->function = function;
+  new_thread->name = name;
+  new_thread->m_last_thing_searched = Qnil; /* copy from parent? */
+  new_thread->m_saved_last_thing_searched = Qnil;
+  new_thread->m_current_buffer = current_thread->m_current_buffer;
+  new_thread->error_symbol = Qnil;
+  new_thread->error_data = Qnil;
+
+  new_thread->m_specpdl_size = 50;
+  new_thread->m_specpdl = xmalloc (new_thread->m_specpdl_size
+				   * sizeof (struct specbinding));
+  new_thread->m_specpdl_ptr = new_thread->m_specpdl;
+
+  sys_cond_init (&new_thread->thread_condvar);
+
+  /* We'll need locking here eventually.  */
+  new_thread->next_thread = all_threads;
+  all_threads = new_thread;
+
+  if (! sys_thread_create (&thr, run_thread, new_thread))
+    {
+      /* Restore the previous situation.  */
+      all_threads = all_threads->next_thread;
+      error ("Could not start a new thread");
+    }
+
+  /* FIXME: race here where new thread might not be filled in?  */
+  XSETTHREAD (result, new_thread);
+  return result;
+}
+
+DEFUN ("current-thread", Fcurrent_thread, Scurrent_thread, 0, 0, 0,
+       doc: /* Return the current thread.  */)
+  (void)
+{
+  Lisp_Object result;
+  XSETTHREAD (result, current_thread);
+  return result;
+}
+
+DEFUN ("thread-name", Fthread_name, Sthread_name, 1, 1, 0,
+       doc: /* Return the name of the THREAD.
+The name is the same object that was passed to `make-thread'.  */)
+     (Lisp_Object thread)
+{
+  struct thread_state *tstate;
+
+  CHECK_THREAD (thread);
+  tstate = XTHREAD (thread);
+
+  return tstate->name;
+}
+
+static void
+thread_signal_callback (void *arg)
+{
+  struct thread_state *tstate = arg;
+  struct thread_state *self = current_thread;
+
+  sys_cond_broadcast (tstate->wait_condvar);
+  post_acquire_global_lock (self);
+}
+
+DEFUN ("thread-signal", Fthread_signal, Sthread_signal, 3, 3, 0,
+       doc: /* FIXME */)
+  (Lisp_Object thread, Lisp_Object error_symbol, Lisp_Object data)
+{
+  struct thread_state *tstate;
+
+  CHECK_THREAD (thread);
+  tstate = XTHREAD (thread);
+
+  if (tstate == current_thread)
+    Fsignal (error_symbol, data);
+
+  /* What to do if thread is already signalled?  */
+  /* What if error_symbol is Qnil?  */
+  tstate->error_symbol = error_symbol;
+  tstate->error_data = data;
+
+  if (tstate->wait_condvar)
+    flush_stack_call_func (thread_signal_callback, tstate);
+
+  return Qnil;
+}
+
+DEFUN ("thread-alive-p", Fthread_alive_p, Sthread_alive_p, 1, 1, 0,
+       doc: /* FIXME */)
+  (Lisp_Object thread)
+{
+  struct thread_state *tstate;
+
+  CHECK_THREAD (thread);
+  tstate = XTHREAD (thread);
+
+  /* m_specpdl is set when the thread is created and cleared when the
+     thread dies.  */
+  return tstate->m_specpdl == NULL ? Qnil : Qt;
+}
+
+static void
+thread_join_callback (void *arg)
+{
+  struct thread_state *tstate = arg;
+  struct thread_state *self = current_thread;
+
+  self->wait_condvar = &tstate->thread_condvar;
+  while (tstate->m_specpdl != NULL && EQ (self->error_symbol, Qnil))
+    sys_cond_wait (self->wait_condvar, &global_lock);
+
+  self->wait_condvar = NULL;
+  post_acquire_global_lock (self);
+}
+
+DEFUN ("thread-join", Fthread_join, Sthread_join, 1, 1, 0,
+       doc: /* FIXME */)
+  (Lisp_Object thread)
+{
+  struct thread_state *tstate;
+
+  CHECK_THREAD (thread);
+  tstate = XTHREAD (thread);
+
+  if (tstate->m_specpdl != NULL)
+    flush_stack_call_func (thread_join_callback, tstate);
+
+  return Qnil;
+}
+
+DEFUN ("all-threads", Fall_threads, Sall_threads, 0, 0, 0,
+       doc: /* Return a list of all threads.  */)
+     (void)
+{
+  Lisp_Object result = Qnil;
+  struct thread_state *iter;
+
+  for (iter = all_threads; iter; iter = iter->next_thread)
+    {
+      Lisp_Object thread;
+
+      XSETTHREAD (thread, iter);
+      result = Fcons (thread, result);
+    }
+
+  return result;
+}
+
+\f
+
+static void
+init_primary_thread (void)
+{
+  primary_thread.header.size
     = PSEUDOVECSIZE (struct thread_state, m_gcprolist);
-  XSETPVECTYPE (&the_only_thread, PVEC_THREAD);
-  the_only_thread.m_last_thing_searched = Qnil;
-  the_only_thread.m_saved_last_thing_searched = Qnil;
+  XSETPVECTYPE (&primary_thread, PVEC_THREAD);
+  primary_thread.m_last_thing_searched = Qnil;
+  primary_thread.m_saved_last_thing_searched = Qnil;
+  primary_thread.name = Qnil;
+  primary_thread.function = Qnil;
+  primary_thread.error_symbol = Qnil;
+  primary_thread.error_data = Qnil;
+
+  sys_cond_init (&primary_thread.thread_condvar);
+}
+
+void
+init_threads_once (void)
+{
+  init_primary_thread ();
 }
 
 void
 init_threads (void)
 {
+  init_primary_thread ();
+
   sys_mutex_init (&global_lock);
   sys_mutex_lock (&global_lock);
 }
+
+void
+syms_of_threads (void)
+{
+  defsubr (&Sthread_yield);
+  defsubr (&Smake_thread);
+  defsubr (&Scurrent_thread);
+  defsubr (&Sthread_name);
+  defsubr (&Sthread_signal);
+  defsubr (&Sthread_alive_p);
+  defsubr (&Sthread_join);
+  defsubr (&Sall_threads);
+
+  Qthreadp = intern_c_string ("threadp");
+  staticpro (&Qthreadp);
+}
diff --git a/src/thread.h b/src/thread.h
index df26b88..3b53331 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -34,6 +34,16 @@ struct thread_state
   Lisp_Object m_saved_last_thing_searched;
 #define saved_last_thing_searched (current_thread->m_saved_last_thing_searched)
 
+  /* The thread's name.  */
+  Lisp_Object name;
+
+  /* The thread's function.  */
+  Lisp_Object function;
+
+  /* If non-nil, this thread has been signalled.  */
+  Lisp_Object error_symbol;
+  Lisp_Object error_data;
+
   /* m_gcprolist must be the first non-lisp field.  */
   /* Recording what needs to be marked for gc.  */
   struct gcpro *m_gcprolist;
@@ -142,6 +152,18 @@ struct thread_state
   /*re_char*/ unsigned char *m_whitespace_regexp;
 #define whitespace_regexp (current_thread->m_whitespace_regexp)
 
+  /* The OS identifier for this thread.  */
+  sys_thread_t thread_id;
+
+  /* The condition variable for this thread.  This is associated with
+     the global lock.  This thread broadcasts to it when it exits.  */
+  sys_cond_t thread_condvar;
+
+  /* This thread might be waiting for some condition.  If so, this
+     points to the condition.  If the thread is interrupted, the
+     interrupter should broadcast to this condition.  */
+  sys_cond_t *wait_condvar;
+
   /* Threads are kept on a linked list.  */
   struct thread_state *next_thread;
 };
@@ -149,10 +171,13 @@ struct thread_state
 extern struct thread_state *current_thread;
 
 extern sys_mutex_t global_lock;
+extern void post_acquire_global_lock (struct thread_state *);
 
 extern void unmark_threads (void);
+extern void finalize_one_thread (struct thread_state *state);
 
 extern void init_threads_once (void);
 extern void init_threads (void);
+extern void syms_of_threads (void);
 
 #endif /* THREAD_H */
-- 
1.7.7.6




^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-09 19:41 [PATCH 06/10] add most lisp-level features Tom Tromey
@ 2012-08-10  1:45 ` Daniel Colascione
  2012-08-13 14:51   ` Tom Tromey
  0 siblings, 1 reply; 8+ messages in thread
From: Daniel Colascione @ 2012-08-10  1:45 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Emacs discussions

[-- Attachment #1: Type: text/plain, Size: 1089 bytes --]

On 8/9/2012 12:41 PM, Tom Tromey wrote:
> This adds most of the thread features visible to emacs lisp.
> 
> I roughly followed the Bordeaux threads API:
> 
> http://trac.common-lisp.net/bordeaux-threads/wiki/ApiDocumentation
> 
> ... but not identically.  In particular I chose not to implement
> interrupt-thread or destroy-thread, but instead a thread-signalling
> approach.

Thank you. Functions like that lead to nothing but trouble.

> I'm still undecided about *default-special-bindings* (which I did not
> implement).  I think it would be more emacs-like to capture the let
> bindings at make-thread time, but IIRC Stefan didn't like this idea
> the first time around.

I'm with Stefan here. Capturing the bindings at thread-creation time makes the
binding in effect for a thread worker hard to reason about. Say Gnus binds *foo*
and calls into bar-lib, which internally uses threads. bar-lib's thread worker
has no idea *foo* is bound, and if a non-Gnus caller uses bar-lib, *foo* won't
be bound. I feel like this approach would lead to hard-to-find bugs.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 259 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-10  1:45 ` Daniel Colascione
@ 2012-08-13 14:51   ` Tom Tromey
  2012-08-13 20:46     ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2012-08-13 14:51 UTC (permalink / raw)
  To: Daniel Colascione; +Cc: Emacs discussions

>>>>> "Daniel" == Daniel Colascione <dancol@dancol.org> writes:

Tom> I'm still undecided about *default-special-bindings* (which I did not
Tom> implement).  I think it would be more emacs-like to capture the let
Tom> bindings at make-thread time, but IIRC Stefan didn't like this idea
Tom> the first time around.

Daniel> I'm with Stefan here. Capturing the bindings at thread-creation
Daniel> time makes the binding in effect for a thread worker hard to
Daniel> reason about. Say Gnus binds *foo* and calls into bar-lib, which
Daniel> internally uses threads. bar-lib's thread worker has no idea
Daniel> *foo* is bound, and if a non-Gnus caller uses bar-lib, *foo*
Daniel> won't be bound. I feel like this approach would lead to
Daniel> hard-to-find bugs.

The reason I am not totally sold on this approach is that Emacs already
uses dynamic binding in most code, and it is actually used.

For example suppose you were reworking some code to run in a separate
thread.  You can be faced with the mirror image of the above problem:
your callers may have some bindings in effect that you need to capture,
but you don't know which ones.

Or to put it another way, you already have the "hard to reason about"
problem any time you write in Emacs Lisp.  Why should make-thread have
different semantics from other primitives?

Yet one more observation here.  The Bordeaux approach lets you specify
which bindings to capture.  It seems to me that some such mechanism is
necessary, at the very least.  Otherwise, passing a local variable into
a thread seems difficult, unless you assume that only lexical-binding:t
code will want to make threads.  I guess it could be done with
backquote, kind of ugly though.

It seems weird to have this set the global "wrongly":

   (let ((local 23))
      (make-thread (lambda () (setq global local))))

Especially if you wrap this kind of thing up some macro, say
"with-worker-thread" or the like.

I'm not totally convinced by these ideas either though.  Maybe
everything important really will use lexical binding.

Tom



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-13 14:51   ` Tom Tromey
@ 2012-08-13 20:46     ` Stefan Monnier
  2012-08-13 21:02       ` Tom Tromey
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Monnier @ 2012-08-13 20:46 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Daniel Colascione, Emacs discussions

> Yet one more observation here.  The Bordeaux approach lets you specify
> which bindings to capture.

AFAIK, you can easily do that manually with the "no capture" behavior
as well.

> It seems to me that some such mechanism is necessary, at the very
> least.  Otherwise, passing a local variable into a thread seems
> difficult, unless you assume that only lexical-binding:t code will
> want to make threads.

Converting to lexical-binding is fairly easy in my experience (at least
for code whose byte-compilation doesn't spit out hundreds of warnings).
So, yes, I assume that lexical-binding will be a common case for
threaded code and I'm happy to make things a bit harder for the
dynamic-binding case if it can make the whole approach simpler
and cleaner.

The concurrency code makes dynamic binding more expensive anyway
(either because of the need to unwind&rewind them during context switch,
or because accessing those vars requires fetching the current
thread-local value), so it naturally likes lexical-binding.

> Maybe everything important really will use lexical binding.

At least the `make-thread' code will likely be in a lexical-binding
file, yes.


        Stefan



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-13 20:46     ` Stefan Monnier
@ 2012-08-13 21:02       ` Tom Tromey
  2012-08-14  1:13         ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2012-08-13 21:02 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Daniel Colascione, Emacs discussions

I'm not really trying to continue this sub-thread more than it needs to
be...

Tom> Yet one more observation here.  The Bordeaux approach lets you specify
Tom> which bindings to capture.

Stefan> AFAIK, you can easily do that manually with the "no capture" behavior
Stefan> as well.

All I could think of is backquote, which seems pretty gross.

  (make-thread
    `(lambda ()
       (let ((default-directory ,default-directory))
         ...)))

I'm not really a fan of the Bordeaux approach either though.
Better to have a convenience macro, I think.

Tom> Maybe everything important really will use lexical binding.

Stefan> At least the `make-thread' code will likely be in a lexical-binding
Stefan> file, yes.

It occurs to me now that even lexically bound code will use some
variables which are always dynamic.  It still seems odd not to capture
these.

Tom



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-13 21:02       ` Tom Tromey
@ 2012-08-14  1:13         ` Stefan Monnier
  2012-08-14 14:46           ` Tom Tromey
  0 siblings, 1 reply; 8+ messages in thread
From: Stefan Monnier @ 2012-08-14  1:13 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Daniel Colascione, Emacs discussions

> All I could think of is backquote, which seems pretty gross.
>   (make-thread
>     `(lambda ()
>        (let ((default-directory ,default-directory))
>          ...)))

   (let ((foo inhibit-read-only))
     (make-thread (lambda () (let ((inhibit-read-only foo)) ...)))

Of course, this presumes the use of lexical-binding, otherwise you need
to use lexical-let.

> It occurs to me now that even lexically bound code will use some
> variables which are always dynamic.

Such as?


        Stefan



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-14  1:13         ` Stefan Monnier
@ 2012-08-14 14:46           ` Tom Tromey
  2012-08-14 15:05             ` Stefan Monnier
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2012-08-14 14:46 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Daniel Colascione, Emacs discussions

Stefan>    (let ((foo inhibit-read-only))
Stefan>      (make-thread (lambda () (let ((inhibit-read-only foo)) ...)))

Stefan> Of course, this presumes the use of lexical-binding, otherwise you need
Stefan> to use lexical-let.

Ok, thanks.

Tom> It occurs to me now that even lexically bound code will use some
Tom> variables which are always dynamic.

Stefan> Such as?

I thought anything defvar'd was dynamically bound, so there are
thousands of examples, including all those DEFVARs in the C code.  Is
this not correct?

Tom



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 06/10] add most lisp-level features
  2012-08-14 14:46           ` Tom Tromey
@ 2012-08-14 15:05             ` Stefan Monnier
  0 siblings, 0 replies; 8+ messages in thread
From: Stefan Monnier @ 2012-08-14 15:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Daniel Colascione, Emacs discussions

Stefan> Such as?

> I thought anything defvar'd was dynamically bound, so there are
> thousands of examples, including all those DEFVARs in the C code.  Is
> this not correct?

Anything declared with `defvar' is a variable which will be
dynamically-bound by `let'.  If you don't let-bind it, then it's just
a plain old global variable (tho potentially buffer-local, of course).

Yes, dynamic-scoping is very common, and will stay common in
lexical-binding code, but I don't think that should be a problem.


        Stefan



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-08-14 15:05 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-08-09 19:41 [PATCH 06/10] add most lisp-level features Tom Tromey
2012-08-10  1:45 ` Daniel Colascione
2012-08-13 14:51   ` Tom Tromey
2012-08-13 20:46     ` Stefan Monnier
2012-08-13 21:02       ` Tom Tromey
2012-08-14  1:13         ` Stefan Monnier
2012-08-14 14:46           ` Tom Tromey
2012-08-14 15:05             ` Stefan Monnier

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.