From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Eli Zaretskii Newsgroups: gmane.emacs.devel Subject: Re: User interaction from multiple threads Date: Fri, 17 Aug 2018 11:56:36 +0300 Message-ID: <83lg95gym3.fsf@gnu.org> References: <838t59j821.fsf@gnu.org> <87bma4528z.fsf@md5i.com> <834lfuie1i.fsf@gnu.org> <874lft3dg2.fsf@md5i.com> NNTP-Posting-Host: blaine.gmane.org X-Trace: blaine.gmane.org 1534496135 30237 195.159.176.226 (17 Aug 2018 08:55:35 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Fri, 17 Aug 2018 08:55:35 +0000 (UTC) Cc: emacs-devel@gnu.org To: Michael Welsh Duggan Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Fri Aug 17 10:55:31 2018 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1fqaXb-0007gS-34 for ged-emacs-devel@m.gmane.org; Fri, 17 Aug 2018 10:55:31 +0200 Original-Received: from localhost ([::1]:60693 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fqaZf-0001hf-Sj for ged-emacs-devel@m.gmane.org; Fri, 17 Aug 2018 04:57:39 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:48711) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fqaYu-0001hX-OY for emacs-devel@gnu.org; Fri, 17 Aug 2018 04:56:53 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fqaYp-00019s-OU for emacs-devel@gnu.org; Fri, 17 Aug 2018 04:56:52 -0400 Original-Received: from fencepost.gnu.org ([2001:4830:134:3::e]:52164) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fqaYp-00019U-Jg; Fri, 17 Aug 2018 04:56:47 -0400 Original-Received: from [176.228.60.248] (port=1509 helo=home-c4e4a596f7) by fencepost.gnu.org with esmtpsa (TLS1.2:RSA_AES_256_CBC_SHA1:256) (Exim 4.82) (envelope-from ) id 1fqaYp-0000F4-60; Fri, 17 Aug 2018 04:56:47 -0400 In-reply-to: <874lft3dg2.fsf@md5i.com> (message from Michael Welsh Duggan on Thu, 16 Aug 2018 22:59:57 -0400) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2001:4830:134:3::e X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:228631 Archived-At: > From: Michael Welsh Duggan > Cc: Michael Welsh Duggan , emacs-devel@gnu.org > Date: Thu, 16 Aug 2018 22:59:57 -0400 Let me start responding from the end: > No problem. As my employer makes it unfeasibly difficult to contribute > code to the FSF (would have to have a separate disclaimer for every > feature), participating in discussions like this is the least I can do. Does your contract allow you to share ideas? How about share ideas that are later attributed to you when those ideas are implemented in code? If you can do that, then describing an idea of the implementation in sufficient detail will allow someone else to write the code relatively easily, and AFAIU won't be copyrightable or subject to IP restrictions. (But IANAL.) > >> #1 If "waiting for input" means in read-from-minibuffer or something > >> similar, I believe that input should go to the the thread. The other > >> thread will have to wait. If "waiting for input" means idle, it > >> should go the the other thread. > > > > "Waiting for input" means waiting for user to type something. That is > > normal Emacs situation when the user does nothing, so unconditionally > > sending input to the main thread and letting the other thread wait > > would mean that other thread might wait indefinitely. > > To clarify, I believe the command-loop (main-thread), aside from a large > amount of maintainance, is fundamentally a loop around > read-key-sequence, lookup-key, and command-execute. *This* > read-key-sequence needs to be interruptable by other threads as long as > no keys have yet been entered into the sequence. This is the point I > considered to be "idling." If any other thread, or even the main > thread, calls read-key-sequence outside of the main command-loop, that > one should get the input mutex immediately. I deliberately avoided saying anything about being "idle", because in the context of this discussion, the meaning is overloaded several times, and is likely to cause confusion. So let me now describe the low-level details which are IMO pertinent to the issue at hand. (I suggest to review the relevant code parts: thread_select and the functions it calls, and wait_reading_process_output.) When any thread in Emacs is about to become "idle", i.e. has nothing else to do except wait for input (in particular, but not necessarily, because it needs the user to respond to some prompt), it releases the global lock and calls pselect. The descriptors on which pselect will wait are computed by compute_input_wait_mask, which skips any descriptors already being waited for by some other thread; this has the side effect of having only the main thread wait on keyboard input, at least in most cases. The command loop causes pselect to be called with a very large timeout (unless there are active timers, in which case the timeout is computed to end when the first timer expires). So the "idling" thread will be parked inside the pselect call until that timeout expires. And since the global lock is released and up for grabs, another thread may take the lock and begin/continue running during that "idle wait". When that other thread attempts to read from the minibuffer (a.k.a. "become idle"), it will also release the global lock and call pselect. What will happen then depends on whether or not the main thread still waits: . If the main thread still waits, the non-main thread will NOT wait on keyboard input, and we now have 2 threads parked inside pselect; user input will go to the main thread. . If the main thread's timeout expired, it is waiting to take the global lock, so it will now take it and loop back to the next pselect call. We therefore have a race between the main and non-main thread: there's a small probability that the non-main thread will call compute_input_wait_mask first, in which case it will wait on the keyboard input. (This situation could be triggered with timers that fire at high frequency, I think.) Therefore, your idea of "interrupting" an "idling" main thread would need to somehow cause it exit the pselect call, before the non-main thread is about to prompt the user, and also prevent it from calling another read_key_sequence until the non-main thread finishes its interaction with the user. Interrupting the pselect call could be emulated by using a very short timeout, but that is unfriendly to laptops when Emacs is _really_ idle. Another way to interrupt pselect is with some signal and setjmp/longjmp. We then need a mutex to prevent the read_key_sequence call during user interaction, so that the interacting thread could arrange for waiting on the keyboard descriptor. Does this make sense, given the details in the related code? > You probably understand what I mean at this point, but to clarify, in > case: Another thread should not be blocked for input because the main > thread is waiting for an event, unless the main thread is waiting for > that event because it explicitly called read-key-sequence or some other > higher-level input function (such as read-from-minibuffer) from anything > other than the command loop. But the instant the command loop's > read-key-sequence reads a single key, it must have the input mutex until > it has finished reading a key sequence (or is quit). The last part will be non-trivial to translate into code, because I'm not sure the related code is aware of where we are in the process of reading a key sequence, and there are many different paths through that code. But I may be wrong. > Now, another command -- say, read-from-minibuffer -- could end up > calling the command loop, which will look for events. That command > should have set the input mutex beforehand, and, to be clear, this > should be a recursive mutex, so it won't be interrupted in this context. We will need to enhance the mutexes we use, because currently I think we use non-recursive mutexes on Posix-ish platforms, and recursive ones on MS-Windows. > A call to recursive-edit, however, would not (I think) grab the input > mutex, and as such would still yield to an input request from another > thread. Not sure about that, we need to review the various uses of recursive-edit first. For example, read-from-minibuffer uses recursive-edit, and I think we do need that to lock out input from other threads. > I have no idea what would be caused by a non-main thread calling > recursive-edit. That way lies madness. But someone should consider > what it might mean. Well, read-from-minibuffer calls (a subroutine of) recursive-edit, so we should definitely support that madness... > >> #2 You should neveer break a key sequence. We do not preempt the main > >> thread. The other thread will have to wait. > > > > Do we need to tell the user the other thread wants to prompt? > > No. Just wait for the current input sequence to finish, then allow the > prompt. I suppose there could be indicators in the mode line, or > something like that, but I don't think we need that initialy, and I have > no idea whether that would actually be desirable anyway. I think an indication is extremely desirable, but I agree it could be tricky to produce, even on the mode line. Thanks.