Eli Zaretskii <address@hidden
> schrieb am Mi., 26. Apr. 2017 um 07:32 Uhr:
> From: Philipp Stephani <address@hidden>
> Date: Sun, 23 Apr 2017 15:52:36 +0000
> Cc: address@hidden, address@hidden
> > - Using objects across threads requires careful synchronization.
> Not sure what synchronization you have in mind. There's only one
> running thread at any given time, and a thread cannot be preempted
> while it runs Lisp, so synchronization seems unnecessary, as a thread
> cannot modify a Lisp object that another thread is modifying.
> However, that's just an implementation detail, and the mapping between Emacs and OS threads is
> unspecified (for good reason).
It isn't unspecified: the ELisp manual explicitly documents that only
one Lisp thread can run at any given time.
That is true for a *Lisp* thread, not for an *OS* thread. On the OS thread level we might theoretically have multiple threads per Lisp thread, or miss some synchronization barriers, etc.
> To be forward-compatible, either we have to formally document this and then
> never ever change the implementation, or document that module authors can't rely on it and have to perform
> synchronization themselves.
Since it is documented already, module authors can rely on that.
It's not documented because it can't be documented without talking about the behavior of the C module API, and that is itself not documented yet. If we choose to guarantee that unsynchronized accesses to module functions don't introduce data races, then we need to say that explicitly in the module documentation, and stick to it. (I.e. once that decision has been made, there's no going back.)
> I meant this as a general statement: if an API is more restrictive (i.e. has stronger preconditions), its
> implementation can be more flexible because it has to deal with fewer corner cases.
I don't think it's right for us to favor our flexibility over that of
the Lisp programmers who will want to use these features.
That's a matter for debate. Wide contracts, such as guaranteeing some behavior, are undoubtedly great for clients, but on the other hand are not as future-proof as narrow contracts, because it often turns out that the wide contract guarantees some behavior that is considered undesirable with more experience (e.g. signed integer overflow in Java vs. C).
> Specifically, the modules API is definitely not intended to restrict modules to be single-threaded. It only
> restricts under which circumstances environments can be used: the lifetime is restricted to the storage
> duration of the environment pointer, and this patch adds an additional restriction that environments are
I think we only care that module functions run in the context of some
Lisp thread, and we don't care which Lisp thread is that. So I
proposed a simplified implementation of the test, which I hope you
will agree with.
If we want to guarantee that environment functions can be called from arbitrary threads without introducing data races, then the only assertion that's necessary is whether (current_thread->thread_id == GetCurrentThreadId ()); without that undefined behavior would be almost guaranteed.
> Note that it's not enough to check whether the current OS thread matches *some* Emacs thread, it has to be
> *the current* Emacs thread, otherwise the interpreter state is inconsistent.
Given the documented implementation and restrictions on Lisp threads,
the condition you want to enforce is guaranteed, as long as the
current thread is some Lisp thread.
If only the current thread can be scheduled, then these conditions should indeed be equivalent. However, checking whether the current thread is the current Emacs thread is much simpler to check (a single equality test).
> > - The Emacs module API is modelled after JNI, which has the same restriction
> > (http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html).
> Themodule API is modelled after JNI, but the threads model is a far
> cry from Java threads. So I don't think this argument is relevant,
> unless you can show specific problems with our implementation of
> It's generally not possible to show specific problems because there is not enough experience with the
> combination of multithreading and modules. Also, thread synchronization problems (data races) typically lead
> to subtle undefined behavior, which isn't easily observed.
> By contrast, there's lots of experience with JNI, and it makes sense to base a new design on an existing
Once again, I think that the JNI experience related to threads is not
relevant for Emacs, due to a very restrictive form of threading we
As said, if we make the guarantee that calling environment functions cannot introduce data races, then yes, the thread affinity restriction is not necessary. I'd just want to make sure that everyone understands the advantages and disadvantages of both approaches.