bug#25265: make-thread crashes in OS X 10.6

classic Classic list List threaded Threaded
35 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Charles A. Roelli
Running Emacs -Q on the latest commit (e36a3882),

M-: (make-thread (lambda () "string"))

appears to hang Emacs immediately.

Here's the output of "bt" and "thread apply all bt":

(gdb) bt
#0  0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
#1  0x00000001000cd2b6 in handle_sigsegv (sig=1884515520, siginfo=0x70, arg=0x100621fe8) at sysdep.c:1800
#2  <signal handler called>
#3  0x000000010012d7dc in unbind_to (count=Cannot access memory at address 0x0
) at eval.c:3499
#4  0x00000001001af2c4 in ns_select (nfds=1606412240, readfds=0x7fff5fbfeb80, writefds=0x7fff5fbfeb00, exceptfds=0x7fff5fbfe7d0, timeout=0x7fff5fbfe7d0, sigmask=0x7fff5fbfe7d0) at nsterm.m:4210
#5  0x000000010018d210 in really_call_select (arg=0x7fff5fbfe840) at thread.c:520
#6  0x000000010010ca06 in flush_stack_call_func (func=0, arg=0x5fbfeae800000000) at alloc.c:5137
#7  0x000000010018d1a7 in thread_select (func=0x5fbfeae800000000, max_fds=0, rfds=0x7fff5fbfe828, wfds=0x0, efds=0x7fff5fbfeae8, timeout=0x0, sigmask=0x0) at thread.c:543
#8  0x00000001001752ad in wait_reading_process_output (time_limit=140734799801728, nsecs=0, read_kbd=1606413696, wait_for_cell=140734799801728, wait_proc=0x7fff5fbfed80, just_wait_proc=0, do_display=true) at process.c:5344
#9  0x000000010000471a in sit_for (timeout=30, display_option=0, reading=false) at dispnew.c:5763
#10 0x00000001000bf6f2 in read_char (commandflag=1606414816, map=140734799802848, prev_event=4294967295, used_mouse_menu=0x7fff5fbff1e0, end_time=0x7fff5fbff1e0) at keyboard.c:2725
#11 0x00000001000c26ee in read_key_sequence () at keyboard.c:2599
#12 0x00000001000c4afd in command_loop_1 () at keyboard.c:1373
#13 0x000000010012e2a4 in internal_condition_case (bfun=0x1000c3690 <command_loop_1>, handlers=499667000, hfun=0x7fff5fbff500) at eval.c:1334
#14 0x00000001000c4d50 in command_loop_2 (ignore=499667000) at keyboard.c:1115
#15 0x000000010012e32f in internal_catch (tag=499667000, func=0x1000c4d20 <command_loop_2>, arg=499667000) at eval.c:1100
#16 0x00000001000c4ec6 in command_loop [inlined] () at /Code/emacs-build-threads/src/keyboard.c:1094
#17 0x00000001000c4ec6 in recursive_edit_1 () at keyboard.c:1800
#18 0x00000001000b60df in Frecursive_edit () at keyboard.c:771
#19 0x00000001000b2993 in main (argc=4002, argv=0x7fff5fbff780) at emacs.c:1691

Program received signal SIGSTOP, Stopped (signal).
0x00007fff838f8430 in malloc_gdb_po_unsafe ()
Unsafe to run code: malloc zone lock is held for some zone..
Canceling call as the malloc lock is held so it isn't safe to call the runtime.
Issue the command:
    set objc-non-blocking-mode off
to override this check if you are sure your call doesn't use the malloc libraries or the ObjC runtime.
(gdb) set objc-non-blocking-mode off
(gdb) continue
Continuing.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000040
0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
1767  char *bot = stack_bottom;

(gdb) thread apply all bt

Thread 4 (process 17249):
#0  0x00007fff8385f932 in select$DARWIN_EXTSN ()
#1  0x000000010019fa79 in -[EmacsApp fd_handler:] (self=0x8, _cmd=0x102380ad0, unused=0x0) at nsterm.m:5512
#2  0x00007fff89967114 in __NSThread__main__ ()
#3  0x00007fff83854fd6 in _pthread_start ()
#4  0x00007fff83854e89 in thread_start ()

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000040
[Switching to process 17249 thread 0x903]
0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
1767  char *bot = stack_bottom;
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (backtrace_top) will be abandoned.

Thread 3 (process 17249):
#0  0x00007fff8385f932 in select$DARWIN_EXTSN ()
#1  0x0000000100ad3983 in g_poll (fds=0x101a37ce0, nfds=28839360, timeout=0) at gpoll.c:385
#2  0x0000000100ac6dfd in g_main_context_iterate () at gmain.c:3198
#3  0x0000000100ac6ee2 in g_main_context_iteration (context=0x6, may_block=1) at gmain.c:3869
#4  0x0000000100ac5e71 in glib_worker_main (data=0x6) at gmain.c:5622
#5  0x0000000100aeb75a in g_thread_proxy (data=0x6) at gthread.c:764
#6  0x00007fff83854fd6 in _pthread_start ()
#7  0x00007fff83854e89 in thread_start ()

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000040
[Switching to process 17249 thread 0x903]
0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
1767  char *bot = stack_bottom;
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (backtrace_top) will be abandoned.

Thread 2 (process 17249):
#0  0x00007fff8383544b in pthread_workqueue_additem_np ()
#1  0x00007fff83834d0a in _dispatch_queue_wakeup_global ()
#2  0x00007fff83834c28 in _dispatch_wakeup ()
#3  0x00007fff8394fd3f in _dispatch_queue_push_list_slow ()
#4  0x00007fff83834c9c in _dispatch_wakeup ()
#5  0x00007fff8394fd3f in _dispatch_queue_push_list_slow ()
#6  0x00007fff83834c9c in _dispatch_wakeup ()
#7  0x00007fff83836ebe in _dispatch_run_timers ()
#8  0x00007fff83836aa2 in _dispatch_mgr_invoke ()
#9  0x00007fff838367b4 in _dispatch_queue_invoke ()
#10 0x00007fff838362de in _dispatch_worker_thread2 ()
#11 0x00007fff83835c08 in _pthread_wqthread ()
#12 0x00007fff83835aa5 in start_wqthread ()

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000070
backtrace_top () at eval.c:202
202  while (backtrace_p (pdl) && pdl->kind != SPECPDL_BACKTRACE)
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (backtrace_top) will be abandoned.

Thread 1 (process 17249):
#0  0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
#1  0x00000001000cd2b6 in handle_sigsegv (sig=1884515520, siginfo=0x70, arg=0x100621fe8) at sysdep.c:1800
#2  <signal handler called>
#3  0x000000010012d7dc in unbind_to (count=Cannot access memory at address 0x0
) at eval.c:3499
#4  0x00000001001af2c4 in ns_select (nfds=1606412240, readfds=0x7fff5fbfeb80, writefds=0x7fff5fbfeb00, exceptfds=0x7fff5fbfe7d0, timeout=0x7fff5fbfe7d0, sigmask=0x7fff5fbfe7d0) at nsterm.m:4210
#5  0x000000010018d210 in really_call_select (arg=0x7fff5fbfe840) at thread.c:520
#6  0x000000010010ca06 in flush_stack_call_func (func=0, arg=0x5fbfeae800000000) at alloc.c:5137
#7  0x000000010018d1a7 in thread_select (func=0x5fbfeae800000000, max_fds=0, rfds=0x7fff5fbfe828, wfds=0x0, efds=0x7fff5fbfeae8, timeout=0x0, sigmask=0x0) at thread.c:543
#8  0x00000001001752ad in wait_reading_process_output (time_limit=140734799801728, nsecs=0, read_kbd=1606413696, wait_for_cell=140734799801728, wait_proc=0x7fff5fbfed80, just_wait_proc=0, do_display=true) at process.c:5344
#9  0x000000010000471a in sit_for (timeout=30, display_option=0, reading=false) at dispnew.c:5763
#10 0x00000001000bf6f2 in read_char (commandflag=1606414816, map=140734799802848, prev_event=4294967295, used_mouse_menu=0x7fff5fbff1e0, end_time=0x7fff5fbff1e0) at keyboard.c:2725
#11 0x00000001000c26ee in read_key_sequence () at keyboard.c:2599
#12 0x00000001000c4afd in command_loop_1 () at keyboard.c:1373
#13 0x000000010012e2a4 in internal_condition_case (bfun=0x1000c3690 <command_loop_1>, handlers=499667000, hfun=0x7fff5fbff500) at eval.c:1334
#14 0x00000001000c4d50 in command_loop_2 (ignore=499667000) at keyboard.c:1115
#15 0x000000010012e32f in internal_catch (tag=499667000, func=0x1000c4d20 <command_loop_2>, arg=499667000) at eval.c:1100
#16 0x00000001000c4ec6 in command_loop [inlined] () at /Code/emacs-build-threads/src/keyboard.c:1094
#17 0x00000001000c4ec6 in recursive_edit_1 () at keyboard.c:1800
#18 0x00000001000b60df in Frecursive_edit () at keyboard.c:771
#19 0x00000001000b2993 in main (argc=4002, argv=0x7fff5fbff780) at emacs.c:1691

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000070
backtrace_top () at eval.c:202
202  while (backtrace_p (pdl) && pdl->kind != SPECPDL_BACKTRACE)
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (backtrace_top) will be abandoned.
(gdb) bt
#0  backtrace_top () at eval.c:202
#1  <function called from gdb>
#2  0x00000001000cd2b6 in stack_overflow [inlined] () at /Code/emacs-build-threads/src/sysdep.c:1767
#3  0x00000001000cd2b6 in handle_sigsegv (sig=1884515520, siginfo=0x70, arg=0x100621fe8) at sysdep.c:202
#4  <signal handler called>
#5  0x000000010012d7dc in unbind_to (count=Cannot access memory at address 0x0
) at eval.c:3499
#6  0x00000001001af2c4 in ns_select (nfds=1606412240, readfds=0x7fff5fbfeb80, writefds=0x7fff5fbfeb00, exceptfds=0x7fff5fbfe7d0, timeout=0x7fff5fbfe7d0, sigmask=0x7fff5fbfe7d0) at nsterm.m:4210
#7  0x000000010018d210 in really_call_select (arg=0x7fff5fbfe840) at thread.c:520
#8  0x000000010010ca06 in flush_stack_call_func (func=0, arg=0x5fbfeae800000000) at alloc.c:5137
#9  0x000000010018d1a7 in thread_select (func=0x5fbfeae800000000, max_fds=0, rfds=0x7fff5fbfe828, wfds=0x0, efds=0x7fff5fbfeae8, timeout=0x0, sigmask=0x0) at thread.c:543
#10 0x00000001001752ad in wait_reading_process_output (time_limit=140734799801728, nsecs=0, read_kbd=1606413696, wait_for_cell=140734799801728, wait_proc=0x7fff5fbfed80, just_wait_proc=0, do_display=true) at process.c:5344
#11 0x000000010000471a in sit_for (timeout=30, display_option=0, reading=false) at dispnew.c:5763
#12 0x00000001000bf6f2 in read_char (commandflag=1606414816, map=140734799802848, prev_event=4294967295, used_mouse_menu=0x7fff5fbff1e0, end_time=0x7fff5fbff1e0) at keyboard.c:2725
#13 0x00000001000c26ee in read_key_sequence () at keyboard.c:2599
#14 0x00000001000c4afd in command_loop_1 () at keyboard.c:1373
#15 0x000000010012e2a4 in internal_condition_case (bfun=0x1000c3690 <command_loop_1>, handlers=499667000, hfun=0x7fff5fbff500) at eval.c:1334
#16 0x00000001000c4d50 in command_loop_2 (ignore=499667000) at keyboard.c:1115
#17 0x000000010012e32f in internal_catch (tag=499667000, func=0x1000c4d20 <command_loop_2>, arg=499667000) at eval.c:1100
#18 0x00000001000c4ec6 in command_loop [inlined] () at /Code/emacs-build-threads/src/keyboard.c:1094
#19 0x00000001000c4ec6 in recursive_edit_1 () at keyboard.c:202
#20 0x00000001000b60df in Frecursive_edit () at keyboard.c:771
#21 0x00000001000b2993 in main (argc=4002, argv=0x7fff5fbff780) at emacs.c:1691

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000070
[Switching to process 17249 thread 0x1703]
backtrace_top () at eval.c:202
202  while (backtrace_p (pdl) && pdl->kind != SPECPDL_BACKTRACE)
The program being debugged was signaled while in a function called from GDB.
GDB remains in the frame where the signal was received.
To change this behavior use "set unwindonsignal on"
Evaluation of the expression containing the function (backtrace_top) will be abandoned.

=========
===END===
=========

Thanks,
Charles




Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> From: [hidden email] (Charles A. Roelli)
> Date: Sat, 24 Dec 2016 12:06:45 +0100
>
> Running Emacs -Q on the latest commit (e36a3882),
>
> M-: (make-thread (lambda () "string"))
>
> appears to hang Emacs immediately.

I cannot reproduce this, neither on GNU/Linux nor on MS-Windows.  So I
guess this is NS-specific.

I see that ns_select calls block/unblock_input, which makes it unsafe
when more than one thread is running.  It's quite possible that this
is the reason.

I hope some NS expert could take a look at this and fix this.

Thanks.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Sat, 24 Dec 2016 19:51:13 +0200
> From: Eli Zaretskii <[hidden email]>
> Cc: [hidden email]
>
> I see that ns_select calls block/unblock_input, which makes it unsafe
> when more than one thread is running.  It's quite possible that this
> is the reason.
>
> I hope some NS expert could take a look at this and fix this.

One idea is to write an implementation of block_input and
unblock_input that uses atomic increment and decrement functions, then
use those only in ns_select.

Could someone with access to NS please try that?



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Sun, Dec 25, 2016 at 05:52:37PM +0200, Eli Zaretskii wrote:

> > Date: Sat, 24 Dec 2016 19:51:13 +0200
> > From: Eli Zaretskii <[hidden email]>
> > Cc: [hidden email]
> >
> > I see that ns_select calls block/unblock_input, which makes it unsafe
> > when more than one thread is running.  It's quite possible that this
> > is the reason.
> >
> > I hope some NS expert could take a look at this and fix this.
>
> One idea is to write an implementation of block_input and
> unblock_input that uses atomic increment and decrement functions, then
> use those only in ns_select.
>
> Could someone with access to NS please try that?

I’ve just tried the below patch and it doesn’t make any difference: it
still hangs immediately.

Back in October Ken Raeburn listed some things that need done in
ns_select which are probably relevant:

https://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00525.html

2 files changed, 23 insertions(+)
src/blockinput.h |  8 ++++++++
src/keyboard.c   | 15 +++++++++++++++

modified   src/blockinput.h
@@ -49,10 +49,18 @@ extern volatile int interrupt_input_blocked;
 
 /* Begin critical section. */
 
+#ifdef NS_IMPL_COCOA
+#include <libkern/OSAtomic.h>
+#endif
+
 INLINE void
 block_input (void)
 {
+#ifdef NS_IMPL_COCOA
+  OSAtomicIncrement32(&interrupt_input_blocked);
+#else
   interrupt_input_blocked++;
+#endif
 }
 
 extern void unblock_input (void);
modified   src/keyboard.c
@@ -54,6 +54,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/ioctl.h>
 #endif /* not MSDOS */
 
+#ifdef NS_IMPL_COCOA
+#include <libkern/OSAtomic.h>
+#endif
+
 #if defined USABLE_FIONREAD && defined USG5_4
 # include <sys/filio.h>
 #endif
@@ -7183,7 +7187,18 @@ unblock_input_to (int level)
 void
 unblock_input (void)
 {
+#ifdef NS_IMPL_COCOA
+  OSAtomicDecrement32(&interrupt_input_blocked);
+  if (interrupt_input_blocked == 0)
+    {
+      if (pending_signals && !fatal_error_in_progress)
+ process_pending_signals ();
+    }
+  else if (interrupt_input_blocked < 0)
+    emacs_abort ();
+#else  
   unblock_input_to (interrupt_input_blocked - 1);
+#endif
 }
 
 /* Undo any number of BLOCK_INPUT calls,


--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Mon, 26 Dec 2016 13:09:17 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> I’ve just tried the below patch and it doesn’t make any difference: it
> still hangs immediately.

Can you try and figure out why it hangs?

> Back in October Ken Raeburn listed some things that need done in
> ns_select which are probably relevant:
>
> https://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00525.html

Hmm... none of the changes Ken mentioned were in the concurrency
branch when I merged it.  Perhaps Ken didn't push the changes back
then.

The problems in xg_select were solved in a different way, I believe.
The problems in ns_select are the issue at hand here.

Wrt static variables used by ns_select: either they should become
members of struct thread_state, or the calls to release_global_lock
and acquire_global_lock should be moved into ns_select (which was what
Ken was proposing, AFAIU).  I'm not sure which possibility is better,
nor whether this is all that needs to be done, because I don't
understand well enough the semantics of some parts of ns_select, in
particular where it says

    [NSApp run];

Can this part and its surrounding code be made thread-safe?  The call
to record_unwind_protect in particular confuses me: that seems to hint
the code wants to be interruptible here, which might require stuff
like maybe_reacquire_global_lock I added to keyboard.c, to avoid
problems when Emacs gets SIGINT on a TTY frame.

Thanks.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Mon, Dec 26, 2016 at 05:52:30PM +0200, Eli Zaretskii wrote:
> > Date: Mon, 26 Dec 2016 13:09:17 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
> >
> > I’ve just tried the below patch and it doesn’t make any difference: it
> > still hangs immediately.
>
> Can you try and figure out why it hangs?

I’m not having any luck with this. My entirely uneducated guess is
that the main thread can’t access the contents of current_thread. It
crashes on this line:

    while (specpdl_ptr != specpdl + count)

with EXC_BAD_ACCESS. Only three things are being accessed: count,
which is a function argument, and specpdl and specpdl_ptr are
#defines to variables within current_thread.

I can’t access anything within current_thread using lldb, but I can
see current_thread itself. I don’t know if that signifies anything.

I’ll put the complete backtrace at the bottom of this email.

> > Back in October Ken Raeburn listed some things that need done in
> > ns_select which are probably relevant:
> >
> > https://lists.gnu.org/archive/html/emacs-devel/2016-10/msg00525.html
>
> Hmm... none of the changes Ken mentioned were in the concurrency
> branch when I merged it.  Perhaps Ken didn't push the changes back
> then.
>
> The problems in xg_select were solved in a different way, I believe.
> The problems in ns_select are the issue at hand here.
>
> Wrt static variables used by ns_select: either they should become
> members of struct thread_state, or the calls to release_global_lock
> and acquire_global_lock should be moved into ns_select (which was what
> Ken was proposing, AFAIU).  I'm not sure which possibility is better,
> nor whether this is all that needs to be done, because I don't
> understand well enough the semantics of some parts of ns_select, in
> particular where it says
>
>     [NSApp run];
>
> Can this part and its surrounding code be made thread-safe?

I think this particular method call has to be done *only* from the
main thread. I imagine that could be a problem.

> The call to record_unwind_protect in particular confuses me: that
> seems to hint the code wants to be interruptible here, which might
> require stuff like maybe_reacquire_global_lock I added to
> keyboard.c, to avoid problems when Emacs gets SIGINT on a TTY frame.

--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Mon, 26 Dec 2016 20:56:32 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> > Can you try and figure out why it hangs?
>
> I’m not having any luck with this. My entirely uneducated guess is
> that the main thread can’t access the contents of current_thread. It
> crashes on this line:
>
>     while (specpdl_ptr != specpdl + count)
>
> with EXC_BAD_ACCESS. Only three things are being accessed: count,
> which is a function argument, and specpdl and specpdl_ptr are
> #defines to variables within current_thread.

The loop above is in unbind_to, right?

> I can’t access anything within current_thread using lldb, but I can
> see current_thread itself. I don’t know if that signifies anything.

I have bad experience with using lldb (not on OS X, though), so I
don't trust it.  Can you use GDB instead?  Failing that, is it
possible to just fprintf these values to stderr?

It is strange that this is where it hangs, since these are
thread-specific variables.  Maybe the problem is that apploopnr is a
static variable, and some other thread stomps on it?

> I’ll put the complete backtrace at the bottom of this email.

I don't see any backtraces attached.

> >     [NSApp run];
> >
> > Can this part and its surrounding code be made thread-safe?
>
> I think this particular method call has to be done *only* from the
> main thread. I imagine that could be a problem.

It could be a problem, yes.  But what does this do, exactly, and why
does it need to be called as part of ns_select?

Also, why does ns_select sometimes call pselect instead -- is that for
non-interactive or non-GUI sessions or something?

Thanks.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Tue, Dec 27, 2016 at 09:30:28AM +0200, Eli Zaretskii wrote:

> > Date: Mon, 26 Dec 2016 20:56:32 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
> >
> > I’m not having any luck with this. My entirely uneducated guess is
> > that the main thread can’t access the contents of current_thread. It
> > crashes on this line:
> >
> >     while (specpdl_ptr != specpdl + count)
> >
> > with EXC_BAD_ACCESS. Only three things are being accessed: count,
> > which is a function argument, and specpdl and specpdl_ptr are
> > #defines to variables within current_thread.
>
> The loop above is in unbind_to, right?

Yes.

> > I can’t access anything within current_thread using lldb, but I can
> > see current_thread itself. I don’t know if that signifies anything.
>
> I have bad experience with using lldb (not on OS X, though), so I
> don't trust it.  Can you use GDB instead?  Failing that, is it
> possible to just fprintf these values to stderr?

OK, I’ve ended up just using printf and it looks like in this thread
current_thread is null. GDB says it’s null too. Here’s what I put in just
before the while loop:

  printf("AT: %d\n", current_thread);
  fflush(stdout);

And here’s the output from GDB. You can see where current_thread
switches from non‐null to null, just before Emacs hangs:

AT: 7842432
AT: 43712208
AT: 43712208
AT: 0

Thread 2 received signal SIGSEGV, Segmentation fault.
0x0000000100200920 in unbind_to (count=0, value=0) at eval.c:3502
3502  while (specpdl_ptr != specpdl + count)

(gdb) p current_thread
$1 = (struct thread_state *) 0x0

> > I’ll put the complete backtrace at the bottom of this email.
>
> I don't see any backtraces attached.

I’ve actually put it in this time. Sorry about that.

> > >     [NSApp run];
> > >
> > > Can this part and its surrounding code be made thread-safe?
> >
> > I think this particular method call has to be done *only* from the
> > main thread. I imagine that could be a problem.
>
> It could be a problem, yes.  But what does this do, exactly, and why
> does it need to be called as part of ns_select?

It runs an event loop that picks up all GUI events and then dispatches
them. It’s part of NextStep. It’s unclear to me why it’s run in
ns_select, but presumably it’s because it needs to be run somewhere
and whoever wrote it thought that was a good place.

If we need to, I expect we could move it to its own thread. That seems
to be a known pattern in the GNUStep/Cocoa world.

> Also, why does ns_select sometimes call pselect instead -- is that for
> non-interactive or non-GUI sessions or something?

It looks that way. It uses it if NSApp isn’t defined, which will
happen if Emacs isn’t being run as a GUI application. I’m pretty sure
that Emacs doesn’t even use ns_select when it’s run in the terminal,
though. It also uses it when timeouts are set to zero. I don’t know
what that’s about.

The backtrace:

Thread 2 received signal SIGSEGV, Segmentation fault.
0x0000000100200933 in unbind_to (count=4, value=0) at eval.c:3499
3499  while (specpdl_ptr != specpdl + count)
(gdb) thread apply all bt

Thread 13 (Thread 0x1a33 of process 45034):
#0  0x00007fffb03594e2 in ?? ()
#1  0x00007fffb0441791 in ?? ()
#2  0x0000000000000000 in ?? ()

Thread 12 (Thread 0x190f of process 45034):
#0  0x00007fffb04411e0 in ?? ()
#1  0x0000000000000000 in ?? ()

Thread 11 (Thread 0x113f of process 45034):
#0  0x00007fffb03594e2 in ?? ()
#1  0x00007fffb0441791 in ?? ()
#2  0x0000000000000000 in ?? ()

Thread 10 (Thread 0x1d03 of process 45034):
#0  0x00007fffb035138a in ?? ()
#1  0x00007fffb03507d7 in ?? ()
#2  0x0000000000000000 in ?? ()

Thread 6 (Thread 0x1507 of process 45034):
#0  0x00007fffb04411e0 in ?? ()
#1  0x000000000ee6b280 in ?? ()
---Type <return> to continue, or q <return> to quit---
#2  0x000000003b99b0ae in ?? ()
#3  0x00007000029d1aa0 in ?? ()
#4  0x00007000029d1a9c in ?? ()
#5  0x00007000029d17b0 in ?? ()
#6  0x00007000029d1a30 in ?? ()
#7  0xe9bfbe3cda25000b in ?? ()
#8  0x0000000000000000 in ?? ()

Thread 5 (Thread 0x1407 of process 45034):
#0  0x00007fffb0358f4a in ?? ()
#1  0x0000000100b0cc0e in ?? ()
#2  0x0000000000000000 in ?? ()

Thread 4 (Thread 0x120b of process 45034):
#0  0x00007fffb0358f4a in ?? ()
#1  0x00000001002cca9a in -[EmacsApp fd_handler:] (self=0x1016217e0,
    _cmd=0x100371b45, unused=0x0) at nsterm.m:5512
#2  0x00007fff9c6f6c6d in ?? ()
#3  0x0000000000000000 in ?? ()

Thread 2 (Thread 0x1707 of process 45034):
#0  0x0000000100200933 in unbind_to (count=4, value=0) at eval.c:3499
#1  0x00000001002c6962 in ns_select (nfds=0, readfds=0x7fff5fbfe5a8,
---Type <return> to continue, or q <return> to quit---
    writefds=0x7fff5fbfe528, exceptfds=0x0, timeout=0x7fff5fbfe500,
    sigmask=0x0) at nsterm.m:4210
#2  0x00000001002a4e3f in really_call_select (arg=0x7fff5fbfdee8)
    at thread.c:520
#3  0x00000001001d5295 in flush_stack_call_func (
    func=0x1002a4dc0 <really_call_select>, arg=0x7fff5fbfdee8) at alloc.c:5137
#4  0x00000001002a4da6 in thread_select (func=0x1002c62d0 <ns_select>,
    max_fds=0, rfds=0x7fff5fbfe5a8, wfds=0x7fff5fbfe528, efds=0x0,
    timeout=0x7fff5fbfe500, sigmask=0x0) at thread.c:543
#5  0x0000000100274571 in wait_reading_process_output (time_limit=30, nsecs=0,
    read_kbd=-1, do_display=true, wait_for_cell=0, wait_proc=0x0,
    just_wait_proc=0) at process.c:5344
#6  0x00000001000092b4 in sit_for (timeout=122, reading=true, display_option=1)
    at dispnew.c:5763
#7  0x0000000100140c5e in read_char (commandflag=1, map=4337270851,
    prev_event=0, used_mouse_menu=0x7fff5fbff037, end_time=0x0)
    at keyboard.c:2729
#8  0x000000010013c0f0 in read_key_sequence (keybuf=0x7fff5fbff360,
    bufsize=30, prompt=0, dont_downcase_last=false,
    can_return_switch_frame=true, fix_current_buffer=true,
    prevent_redisplay=false) at keyboard.c:9154
#9  0x000000010013ac32 in command_loop_1 () at keyboard.c:1377
#10 0x000000010020455f in internal_condition_case (
---Type <return> to continue, or q <return> to quit---
    bfun=0x10013a690 <command_loop_1>, handlers=17232,
    hfun=0x1001505c0 <cmd_error>) at eval.c:1334
#11 0x00000001001504bc in command_loop_2 (ignore=0) at keyboard.c:1119
#12 0x0000000100203cf8 in internal_catch (tag=43536,
    func=0x100150490 <command_loop_2>, arg=0) at eval.c:1100
#13 0x0000000100139be8 in command_loop () at keyboard.c:1098
#14 0x0000000100139a50 in recursive_edit_1 () at keyboard.c:704
#15 0x0000000100139d88 in Frecursive_edit () at keyboard.c:775
#16 0x0000000100137a7d in main (argc=2, argv=0x7fff5fbff9a0) at emacs.c:1690
(gdb) p current_thread.m_specpdl
Cannot access memory at address 0x70
(gdb) p current_thread->m_specpdl
Cannot access memory at address 0x70
(gdb)


--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Tue, 27 Dec 2016 10:44:24 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> OK, I’ve ended up just using printf and it looks like in this thread
> current_thread is null.

This happens in only one place: run_thread, after a thread exits.  And
yes, it's a bad idea to rely on current_thread being non-NULL in code
that is run when other threads could be running.

More generally, this means calling unbind_to, i.e. unwinding the Lisp
stack, is not going to work in this context at all, because that in
effect runs Lisp when more than one thread could run their code.

> > > >     [NSApp run];
> > > >
> > > > Can this part and its surrounding code be made thread-safe?
> > >
> > > I think this particular method call has to be done *only* from the
> > > main thread. I imagine that could be a problem.
> >
> > It could be a problem, yes.  But what does this do, exactly, and why
> > does it need to be called as part of ns_select?
>
> It runs an event loop that picks up all GUI events and then dispatches
> them. It’s part of NextStep. It’s unclear to me why it’s run in
> ns_select, but presumably it’s because it needs to be run somewhere
> and whoever wrote it thought that was a good place.
>
> If we need to, I expect we could move it to its own thread. That seems
> to be a known pattern in the GNUStep/Cocoa world.

That's probably the only reasonable way.  But why does it use
record_unwind_protect and unbind_to in the first place?  What happens
if the user presses C-g while the event loop runs?

> I’m pretty sure that Emacs doesn’t even use ns_select when it’s run
> in the terminal, though.

Actually, I think it has to use ns_select, because subprocesses are
still supported on a text terminal, and reading their output calls
ns_select.  Likewise network connections.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Tue, Dec 27, 2016 at 01:13:11PM +0200, Eli Zaretskii wrote:

> > Date: Tue, 27 Dec 2016 10:44:24 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
>
> > > > >     [NSApp run];
> > > > >
> > > > > Can this part and its surrounding code be made thread-safe?
> > > >
> > > > I think this particular method call has to be done *only* from the
> > > > main thread. I imagine that could be a problem.
> > >
> > > It could be a problem, yes.  But what does this do, exactly, and why
> > > does it need to be called as part of ns_select?
> >
> > It runs an event loop that picks up all GUI events and then dispatches
> > them. It’s part of NextStep. It’s unclear to me why it’s run in
> > ns_select, but presumably it’s because it needs to be run somewhere
> > and whoever wrote it thought that was a good place.
> >
> > If we need to, I expect we could move it to its own thread. That seems
> > to be a known pattern in the GNUStep/Cocoa world.
>
> That's probably the only reasonable way.  But why does it use
> record_unwind_protect and unbind_to in the first place?  What happens
> if the user presses C-g while the event loop runs?

It was added in change 08f27aa39c498d34418c7420b33bf2db913827c3 which
relates to bug 18345. Previously it was simply apploopnr--, but
sometimes users would find themselves in a situation where that line
was never run (hitting C‐g while the event loop runs?) so the next
time ns_select (or ns_read_socket) ran it aborted. I take it unbind_to
fixes that particular problem.

Is there a safer way of ensuring that apploopnr is decremented?

I’m slowly beginning to understand what’s going on in ns_select. It
seems the idea is that it should detect both input on file descriptors
(using pselect in the background), and NS events coming from [NSApp
run].

The following is for my own future reference, if nothing else.

There is another thread that runs in a loop (fd_handler), and when
it’s signalled from ns_select, it runs pselect. At the same time
ns_select sets up a timer, then it calls [NSApp run].

If either the pselect thread, or the timer run out before any input is
detected, they send a user defined event to the NSApp event loop so it
ends.

Similarly if the pselect thread detects input it sends an event to the
NSApp loop, which then ends.

If there’s NS input, it’s processed by the NSApp loop, which then
ends.

Whatever happens, the NSApp loop eventually returns control back to
ns_select, which works out what happened by examining the last NS
event and returns the relevant value.

I have my doubts this is thread safe. The communication between
ns_select and fd_handler is done by setting static variables and then
running:

    c = 'g';
    emacs_write_sig (selfds[1], &c, 1);

I don’t really know what that does. Sends something on an fd?

Then it writes back to the same global variables for ns_select to pick
up.

Now I’ve written it down it seems a lot less complicated...
--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Wed, 28 Dec 2016 19:36:33 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> > That's probably the only reasonable way.  But why does it use
> > record_unwind_protect and unbind_to in the first place?  What happens
> > if the user presses C-g while the event loop runs?
>
> It was added in change 08f27aa39c498d34418c7420b33bf2db913827c3 which
> relates to bug 18345. Previously it was simply apploopnr--, but
> sometimes users would find themselves in a situation where that line
> was never run (hitting C‐g while the event loop runs?) so the next
> time ns_select (or ns_read_socket) ran it aborted. I take it unbind_to
> fixes that particular problem.
>
> Is there a safer way of ensuring that apploopnr is decremented?

There could be, but I see that ns_select and ns_read_socket use it for
some kind of synchronization between them, which I don't really
understand, so I cannot answer that question.

> I’m slowly beginning to understand what’s going on in ns_select. It
> seems the idea is that it should detect both input on file descriptors
> (using pselect in the background), and NS events coming from [NSApp
> run].

What do "NS events" include?  Do they include, for example, keyboard
input?

Also, does C-g cause a signal or some other async event on NS, which
could potentially interrupt the [NSApp run] stuff?  Or could SIGIO do
that (does NS use SIGIO for input?)?

If nothing could interrupt/QUIT [NSApp run], then I don't really
understand why we have unwind_protect there.  The bug in question
cites some undisclosed Lisp for reproducing the problem, so I wonder
how could that cause [NSApp run] to be interrupted and longjmp to
higher level.

> There is another thread that runs in a loop (fd_handler), and when
> it’s signalled from ns_select, it runs pselect. At the same time
> ns_select sets up a timer, then it calls [NSApp run].

(I think ns_select only sets up a timer when there are no descriptors
to watch, to avoid waking up the fd_handler thread in that case.)

So this means there are 2 jobs to be done here: the pselect call and
the [NSApp run] call.

> If either the pselect thread, or the timer run out before any input is
> detected, they send a user defined event to the NSApp event loop so it
> ends.
>
> Similarly if the pselect thread detects input it sends an event to the
> NSApp loop, which then ends.
>
> If there’s NS input, it’s processed by the NSApp loop

Processed how?  Shouldn't Emacs be involved in this processing?  IOW,
these events should be read by Emacs, via the read_socket_hook.

> Whatever happens, the NSApp loop eventually returns control back to
> ns_select, which works out what happened by examining the last NS
> event and returns the relevant value.
>
> I have my doubts this is thread safe.

It isn't.  Unless each Emacs application thread will have its own
fd_handler thread.

One possible solution might be to let only one thread, say the main
thread, to call [NSApp run].  The other threads, when they get into
ns_select, will behave as if Emacs runs in non-GUI mode, and will only
call pselect.  Not sure what this will mean from the POV of all
threads being equal (since the delicate dance between ns_select and
ns_read_socket is still unclear to me), but at least it might avoid
crashes and hangs.  Can you try something like that?

> The communication between ns_select and fd_handler is done by
> setting static variables and then running:
>
>     c = 'g';
>     emacs_write_sig (selfds[1], &c, 1);
>
> I don’t really know what that does. Sends something on an fd?

Yes.  I guess 'g' stands for GO and 's' stands for SUSPEND.  These are
commands to the fd_handler thread, or so it seems.

Thanks.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Thu, Dec 29, 2016 at 07:12:54PM +0200, Eli Zaretskii wrote:

> > Date: Wed, 28 Dec 2016 19:36:33 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
> >
> > I’m slowly beginning to understand what’s going on in ns_select. It
> > seems the idea is that it should detect both input on file descriptors
> > (using pselect in the background), and NS events coming from [NSApp
> > run].
>
> What do "NS events" include?  Do they include, for example, keyboard
> input?

As far as I can tell it’s mouse and keyboard input, as well as
messages from the ‘window manager’, telling it to close the frame,
etc.

> > There is another thread that runs in a loop (fd_handler), and when
> > it’s signalled from ns_select, it runs pselect. At the same time
> > ns_select sets up a timer, then it calls [NSApp run].
>
> (I think ns_select only sets up a timer when there are no descriptors
> to watch, to avoid waking up the fd_handler thread in that case.)
>
> So this means there are 2 jobs to be done here: the pselect call and
> the [NSApp run] call.

Correct on both counts.

> > If there’s NS input, it’s processed by the NSApp loop
>
> Processed how?  Shouldn't Emacs be involved in this processing?  IOW,
> these events should be read by Emacs, via the read_socket_hook.

Ah! Is this the missing piece of the puzzle? When the [NSApp run] loop
receives an event, say keyboard input, it creates an emacs_event and
then raises SIGIO (via hold_event). SIGIO causes ns_read_socket to be
run, which ALSO tries to run [NSApp run].

Am I right in thinking that raising SIGIO will cause ns_read_socket to
be potentially run immediately? Asynchronously?

I’ve just commented out the section of ns_read_socket that calls
[NSApp run] and I can’t see any difference in behaviour. I suspect
that someone’s doubled up on it when they didn’t need to.

> One possible solution might be to let only one thread, say the main
> thread, to call [NSApp run].  The other threads, when they get into
> ns_select, will behave as if Emacs runs in non-GUI mode, and will only
> call pselect.  Not sure what this will mean from the POV of all
> threads being equal (since the delicate dance between ns_select and
> ns_read_socket is still unclear to me), but at least it might avoid
> crashes and hangs.  Can you try something like that?

Yes, I will. Am I right in thinking that if we remove all the NSApp
junk from ns_select it will literally just be calling pselect with
the same arguments?

So, my plan of action:

Run [NSApp run] in it’s own thread with no flow control (unless it’s
important that emacs events are only created at specific times?)

Replace ns_select with pselect.

Thanks for helping with this, I don’t think I’d be able to work it out
on my own.
--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Fri, 30 Dec 2016 18:45:32 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> > > If there’s NS input, it’s processed by the NSApp loop
> >
> > Processed how?  Shouldn't Emacs be involved in this processing?  IOW,
> > these events should be read by Emacs, via the read_socket_hook.
>
> Ah! Is this the missing piece of the puzzle? When the [NSApp run] loop
> receives an event, say keyboard input, it creates an emacs_event and
> then raises SIGIO (via hold_event). SIGIO causes ns_read_socket to be
> run, which ALSO tries to run [NSApp run].
>
> Am I right in thinking that raising SIGIO will cause ns_read_socket to
> be potentially run immediately? Asynchronously?

I very much hope not.  We don't run any non-trivial code from a signal
handler.  I'd expect SIGIO just to set a flag, and then the resulting
input be processed when we call unblock_input next time, and the
blocking level gets to zero.  Then we run process_pending_signals,
which calls handle_async_input, and that's where ns_read_socket will
be called by gobble_input.

> I’ve just commented out the section of ns_read_socket that calls
> [NSApp run] and I can’t see any difference in behaviour. I suspect
> that someone’s doubled up on it when they didn’t need to.

I cannot help you here.  Maybe it's needed for Emacs to be more
responsive?  If you run "git log -L" on ns_read_socket, does the
history tell anything about why this call was added?  Perhaps some
discussion or bug report?

> > One possible solution might be to let only one thread, say the main
> > thread, to call [NSApp run].  The other threads, when they get into
> > ns_select, will behave as if Emacs runs in non-GUI mode, and will only
> > call pselect.  Not sure what this will mean from the POV of all
> > threads being equal (since the delicate dance between ns_select and
> > ns_read_socket is still unclear to me), but at least it might avoid
> > crashes and hangs.  Can you try something like that?
>
> Yes, I will. Am I right in thinking that if we remove all the NSApp
> junk from ns_select it will literally just be calling pselect with
> the same arguments?

It looks like that, yes.

> So, my plan of action:
>
> Run [NSApp run] in it’s own thread with no flow control (unless it’s
> important that emacs events are only created at specific times?)

How will that thread communicate the events to Emacs?

> Thanks for helping with this, I don’t think I’d be able to work it out
> on my own.

Thank you for digging into the problem.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Alan Third
On Fri, Dec 30, 2016 at 11:08:54PM +0200, Eli Zaretskii wrote:

> > Date: Fri, 30 Dec 2016 18:45:32 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
> >
> > Ah! Is this the missing piece of the puzzle? When the [NSApp run] loop
> > receives an event, say keyboard input, it creates an emacs_event and
> > then raises SIGIO (via hold_event). SIGIO causes ns_read_socket to be
> > run, which ALSO tries to run [NSApp run].
> >
> > Am I right in thinking that raising SIGIO will cause ns_read_socket to
> > be potentially run immediately? Asynchronously?
>
> I very much hope not.  We don't run any non-trivial code from a signal
> handler.  I'd expect SIGIO just to set a flag, and then the resulting
> input be processed when we call unblock_input next time, and the
> blocking level gets to zero.  Then we run process_pending_signals,
> which calls handle_async_input, and that's where ns_read_socket will
> be called by gobble_input.

OK. I am, again, none the wiser.

> > I’ve just commented out the section of ns_read_socket that calls
> > [NSApp run] and I can’t see any difference in behaviour. I suspect
> > that someone’s doubled up on it when they didn’t need to.
>
> I cannot help you here.  Maybe it's needed for Emacs to be more
> responsive?  If you run "git log -L" on ns_read_socket, does the
> history tell anything about why this call was added?  Perhaps some
> discussion or bug report?

It’s been there since the NS port was integrated, it seems.

> > So, my plan of action:
> >
> > Run [NSApp run] in it’s own thread with no flow control (unless it’s
> > important that emacs events are only created at specific times?)
>
> How will that thread communicate the events to Emacs?

I’m hoping using the same method as it does now. It creates emacs
events from the NS events, and then fires SIGIO.

I have run into a problem almost right away, though. I don’t know how
to go about creating this thread.

The NSApp loop needs to run in the ‘main’ thread on macOS. I
understand the main thread is always the original thread, so if I want
to use it for the NSApp loop, I need to move Emacs’ normal operation
into a child thread.

I don’t have any experience with pthreads and can’t see any obvious
way of doing it (and I think, from my experiments, it breaks unexec or
something). Is it possible, or can you think of a better way of
handling this?

Thanks!
--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: make-thread crashes in OS X 10.6

Eli Zaretskii
> Date: Fri, 30 Dec 2016 22:05:44 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> > > Am I right in thinking that raising SIGIO will cause ns_read_socket to
> > > be potentially run immediately? Asynchronously?
> >
> > I very much hope not.  We don't run any non-trivial code from a signal
> > handler.  I'd expect SIGIO just to set a flag, and then the resulting
> > input be processed when we call unblock_input next time, and the
> > blocking level gets to zero.  Then we run process_pending_signals,
> > which calls handle_async_input, and that's where ns_read_socket will
> > be called by gobble_input.
>
> OK. I am, again, none the wiser.

Don't give up ;-)

> > > So, my plan of action:
> > >
> > > Run [NSApp run] in it’s own thread with no flow control (unless it’s
> > > important that emacs events are only created at specific times?)
> >
> > How will that thread communicate the events to Emacs?
>
> I’m hoping using the same method as it does now. It creates emacs
> events from the NS events, and then fires SIGIO.
>
> I have run into a problem almost right away, though. I don’t know how
> to go about creating this thread.
>
> The NSApp loop needs to run in the ‘main’ thread on macOS. I
> understand the main thread is always the original thread, so if I want
> to use it for the NSApp loop, I need to move Emacs’ normal operation
> into a child thread.

How about a more modest goal instead?  The code in ns_select can
detect when it is run by the main thread: we have a function,
main_thread_p, for that.  (For it to work, you need to pass
current_thread to it, so we will have to make sure the first part of
ns_select, up to the pselect call, is run with the global lock owned
by a single thread.  The easiest way of achieving that is to do for
ns_select the same change I did yesterday for xg_select: call
ns_select directly in process.c:wait_reading_process_output, then
change ns_select to call thread_select instead of pselect.)

When ns_select detects it's run by a non-main thread, it will only
call thread_select and return its result.  Otherwise, it will call
thread_select with a very small timeout and with NULL descriptor sets
(to let other threads an opportunity to run, and when that returns, do
the [NSApp run] dance with the surrounding code.  Note that the latter
runs after the main thread has re-acquired the global lock, so no
other Lisp threads could be active at that point.

Would that work?

Thanks.



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: [PATCH] Rework NS event handling (bug#25265)

Alan Third
* src/nsterm.m (unwind_apploopnr): Remove.
(ns_read_socket): Remove references to apploopnr.  Make processing the
NS event loop conditional on being in the main thread.
(ns_select): Remove references to apploopnr.  Remove all fd_handler
related stuff.  Check if there are events waiting on the NS event
queue rather than running the event loop.  Remove unused variables and
code.
(fd_handler): Remove.
(ns_term_init): Remove creation of fd_handler thread.
(hold_event, EmacsApp:sendEvent, EmacsView:mouseMoved,
EmacsView:windowDidExpose): Remove send_appdefined.
(ns_send_appdefined): Always check the event queue for
applicationDefined events rather than relying on send_appdefined var.
* src/nsterm.h: Remove reference to fd_handler method.
---
 src/nsterm.h |   1 -
 src/nsterm.m | 380 +++++++++++------------------------------------------------
 2 files changed, 68 insertions(+), 313 deletions(-)

diff --git a/src/nsterm.h b/src/nsterm.h
index 35c6e1a..dc222a7 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -392,7 +392,6 @@ char const * nstrace_fullscreen_type_name (int);
 - (void)sendEvent: (NSEvent *)theEvent;
 - (void)showPreferencesWindow: (id)sender;
 - (BOOL) openFile: (NSString *)fileName;
-- (void)fd_handler: (id)unused;
 - (void)timeout_handler: (NSTimer *)timedEntry;
 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg;
 #ifdef NS_IMPL_GNUSTEP
diff --git a/src/nsterm.m b/src/nsterm.m
index 7e6ec85..98fd8ab 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -279,18 +279,10 @@ - (NSColor *)colorUsingDefaultColorSpace
 /*static int debug_lock = 0; */
 
 /* event loop */
-static BOOL send_appdefined = YES;
 #define NO_APPDEFINED_DATA (-8)
 static int last_appdefined_event_data = NO_APPDEFINED_DATA;
 static NSTimer *timed_entry = 0;
 static NSTimer *scroll_repeat_entry = nil;
-static fd_set select_readfds, select_writefds;
-enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
-static int select_nfds = 0, select_valid = 0;
-static struct timespec select_timeout = { 0, 0 };
-static int selfds[2] = { -1, -1 };
-static pthread_mutex_t select_mutex;
-static int apploopnr = 0;
 static NSAutoreleasePool *outerpool;
 static struct input_event *emacs_event = NULL;
 static struct input_event *q_event_ptr = NULL;
@@ -457,7 +449,6 @@ - (NSColor *)colorUsingDefaultColorSpace
   hold_event_q.q[hold_event_q.nr++] = *event;
   /* Make sure ns_read_socket is called, i.e. we have input.  */
   raise (SIGIO);
-  send_appdefined = YES;
 }
 
 static Lisp_Object
@@ -3872,31 +3863,17 @@ overwriting cursor (usually when cursor on a tab) */
       return;
     }
 
-  /* Only post this event if we haven't already posted one.  This will end
-       the [NXApp run] main loop after having processed all events queued at
-       this moment.  */
-
-#ifdef NS_IMPL_COCOA
-  if (! send_appdefined)
-    {
-      /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
-         in certain situations (rapid incoming events).
-         So check if we have one, if not add one.  */
-      NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
-                                          untilDate:[NSDate distantPast]
-                                             inMode:NSDefaultRunLoopMode
-                                            dequeue:NO];
-      if (! appev) send_appdefined = YES;
-    }
-#endif
-
-  if (send_appdefined)
+  /* Only post this event if we haven't already posted one.  This will
+     end the [NXApp run] main loop after having processed all events
+     queued at this moment.  */
+  NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
+                                      untilDate:[NSDate distantPast]
+                                         inMode:NSDefaultRunLoopMode
+                                        dequeue:NO];
+  if (! appev)
     {
       NSEvent *nxev;
 
-      /* We only need one NX_APPDEFINED event to stop NXApp from running.  */
-      send_appdefined = NO;
-
       /* Don't need wakeup timer any more */
       if (timed_entry)
         {
@@ -4011,14 +3988,6 @@ in certain situations (rapid incoming events).
 }
 #endif /* NS_IMPL_COCOA */
 
-static void
-unwind_apploopnr (Lisp_Object not_used)
-{
-  --apploopnr;
-  n_emacs_events_pending = 0;
-  ns_finish_events ();
-  q_event_ptr = NULL;
-}
 
 static int
 ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
@@ -4029,13 +3998,10 @@ in certain situations (rapid incoming events).
    -------------------------------------------------------------------------- */
 {
   struct input_event ev;
-  int nevents;
+  int nevents = 0;
 
   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
 
-  if (apploopnr > 0)
-    return -1; /* Already within event loop. */
-
 #ifdef HAVE_NATIVE_FS
   check_native_fs ();
 #endif
@@ -4052,54 +4018,49 @@ in certain situations (rapid incoming events).
       return i;
     }
 
-  block_input ();
-  n_emacs_events_pending = 0;
-  ns_init_events (&ev);
-  q_event_ptr = hold_quit;
-
-  /* we manage autorelease pools by allocate/reallocate each time around
-     the loop; strict nesting is occasionally violated but seems not to
-     matter.. earlier methods using full nesting caused major memory leaks */
-  [outerpool release];
-  outerpool = [[NSAutoreleasePool alloc] init];
-
-  /* If have pending open-file requests, attend to the next one of those. */
-  if (ns_pending_files && [ns_pending_files count] != 0
-      && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
+  if ([NSThread isMainThread])
     {
-      [ns_pending_files removeObjectAtIndex: 0];
-    }
-  /* Deal with pending service requests. */
-  else if (ns_pending_service_names && [ns_pending_service_names count] != 0
-    && [(EmacsApp *)
-         NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
-                      withArg: [ns_pending_service_args objectAtIndex: 0]])
-    {
-      [ns_pending_service_names removeObjectAtIndex: 0];
-      [ns_pending_service_args removeObjectAtIndex: 0];
-    }
-  else
-    {
-      ptrdiff_t specpdl_count = SPECPDL_INDEX ();
-      /* Run and wait for events.  We must always send one NX_APPDEFINED event
-         to ourself, otherwise [NXApp run] will never exit.  */
-      send_appdefined = YES;
-      ns_send_appdefined (-1);
-
-      if (++apploopnr != 1)
+      block_input ();
+      n_emacs_events_pending = 0;
+      ns_init_events (&ev);
+      q_event_ptr = hold_quit;
+
+      /* we manage autorelease pools by allocate/reallocate each time around
+         the loop; strict nesting is occasionally violated but seems not to
+         matter.. earlier methods using full nesting caused major memory leaks */
+      [outerpool release];
+      outerpool = [[NSAutoreleasePool alloc] init];
+
+      /* If have pending open-file requests, attend to the next one of those. */
+      if (ns_pending_files && [ns_pending_files count] != 0
+          && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
         {
-          emacs_abort ();
+          [ns_pending_files removeObjectAtIndex: 0];
         }
-      record_unwind_protect (unwind_apploopnr, Qt);
-      [NSApp run];
-      unbind_to (specpdl_count, Qnil);  /* calls unwind_apploopnr */
-    }
+      /* Deal with pending service requests. */
+      else if (ns_pending_service_names && [ns_pending_service_names count] != 0
+               && [(EmacsApp *)
+                    NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
+                                 withArg: [ns_pending_service_args objectAtIndex: 0]])
+        {
+          [ns_pending_service_names removeObjectAtIndex: 0];
+          [ns_pending_service_args removeObjectAtIndex: 0];
+        }
+      else
+        {
+          /* Run and wait for events.  We must always send one NX_APPDEFINED event
+             to ourself, otherwise [NXApp run] will never exit.  */
+          ns_send_appdefined (-1);
 
-  nevents = n_emacs_events_pending;
-  n_emacs_events_pending = 0;
-  ns_finish_events ();
-  q_event_ptr = NULL;
-  unblock_input ();
+          [NSApp run];
+        }
+
+      nevents = n_emacs_events_pending;
+      n_emacs_events_pending = 0;
+      ns_finish_events ();
+      q_event_ptr = NULL;
+      unblock_input ();
+    }
 
   return nevents;
 }
@@ -4114,15 +4075,11 @@ in certain situations (rapid incoming events).
    -------------------------------------------------------------------------- */
 {
   int result;
-  int t, k, nr = 0;
-  struct input_event event;
-  char c;
+  NSDate *timeout_date = nil;
+  NSEvent *ns_event;
 
   NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
 
-  if (apploopnr > 0)
-    return -1; /* Already within event loop. */
-
 #ifdef HAVE_NATIVE_FS
   check_native_fs ();
 #endif
@@ -4135,121 +4092,34 @@ in certain situations (rapid incoming events).
       return -1;
     }
 
-  for (k = 0; k < nfds+1; k++)
-    {
-      if (readfds && FD_ISSET(k, readfds)) ++nr;
-      if (writefds && FD_ISSET(k, writefds)) ++nr;
-    }
-
   if (NSApp == nil
+      || ![NSThread isMainThread]
       || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
-    return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
+    return pselect(nfds, readfds, writefds,
+                   exceptfds, timeout, sigmask);
+
+  result = pselect(nfds, readfds, writefds, exceptfds,
+                   &(struct timespec){.tv_sec = 0, .tv_nsec = 100},
+                   sigmask);
 
   [outerpool release];
   outerpool = [[NSAutoreleasePool alloc] init];
 
-
-  send_appdefined = YES;
-  if (nr > 0)
-    {
-      pthread_mutex_lock (&select_mutex);
-      select_nfds = nfds;
-      select_valid = 0;
-      if (readfds)
-        {
-          select_readfds = *readfds;
-          select_valid += SELECT_HAVE_READ;
-        }
-      if (writefds)
-        {
-          select_writefds = *writefds;
-          select_valid += SELECT_HAVE_WRITE;
-        }
-
-      if (timeout)
-        {
-          select_timeout = *timeout;
-          select_valid += SELECT_HAVE_TMO;
-        }
-
-      pthread_mutex_unlock (&select_mutex);
-
-      /* Inform fd_handler that select should be called */
-      c = 'g';
-      emacs_write_sig (selfds[1], &c, 1);
-    }
-  else if (nr == 0 && timeout)
+  if (timeout)
     {
-      /* No file descriptor, just a timeout, no need to wake fd_handler  */
       double time = timespectod (*timeout);
-      timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
-                                                      target: NSApp
-                                                    selector:
-                                  @selector (timeout_handler:)
-                                                    userInfo: 0
-                                                     repeats: NO]
-                      retain];
-    }
-  else /* No timeout and no file descriptors, can this happen?  */
-    {
-      /* Send appdefined so we exit from the loop */
-      ns_send_appdefined (-1);
-    }
-
-  block_input ();
-  ns_init_events (&event);
-  if (++apploopnr != 1)
-    {
-      emacs_abort ();
-    }
-
-  {
-    ptrdiff_t specpdl_count = SPECPDL_INDEX ();
-    record_unwind_protect (unwind_apploopnr, Qt);
-    [NSApp run];
-    unbind_to (specpdl_count, Qnil);  /* calls unwind_apploopnr */
-  }
-
-  ns_finish_events ();
-  if (nr > 0 && readfds)
-    {
-      c = 's';
-      emacs_write_sig (selfds[1], &c, 1);
+      timeout_date = [NSDate dateWithTimeIntervalSinceNow:time];
     }
-  unblock_input ();
 
-  t = last_appdefined_event_data;
+  /* Listen for a new NSEvent. */
+  ns_event = [NSApp nextEventMatchingMask:NSEventMaskAny
+                                untilDate:timeout_date
+                                   inMode:NSDefaultRunLoopMode
+                                  dequeue:NO];
 
-  if (t != NO_APPDEFINED_DATA)
+  if (ns_event != nil)
     {
-      last_appdefined_event_data = NO_APPDEFINED_DATA;
-
-      if (t == -2)
-        {
-          /* The NX_APPDEFINED event we received was a timeout. */
-          result = 0;
-        }
-      else if (t == -1)
-        {
-          /* The NX_APPDEFINED event we received was the result of
-             at least one real input event arriving.  */
-          errno = EINTR;
-          result = -1;
-        }
-      else
-        {
-          /* Received back from select () in fd_handler; copy the results */
-          pthread_mutex_lock (&select_mutex);
-          if (readfds) *readfds = select_readfds;
-          if (writefds) *writefds = select_writefds;
-          pthread_mutex_unlock (&select_mutex);
-          result = t;
-        }
-    }
-  else
-    {
-      errno = EINTR;
-      result = -1;
+      raise (SIGIO);
     }
 
   return result;
@@ -4765,21 +4635,6 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
   baud_rate = 38400;
   Fset_input_interrupt_mode (Qnil);
 
-  if (selfds[0] == -1)
-    {
-      if (emacs_pipe (selfds) != 0)
-        {
-          fprintf (stderr, "Failed to create pipe: %s\n",
-                   emacs_strerror (errno));
-          emacs_abort ();
-        }
-
-      fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
-      FD_ZERO (&select_readfds);
-      FD_ZERO (&select_writefds);
-      pthread_mutex_init (&select_mutex, NULL);
-    }
-
   ns_pending_files = [[NSMutableArray alloc] init];
   ns_pending_service_names = [[NSMutableArray alloc] init];
   ns_pending_service_args = [[NSMutableArray alloc] init];
@@ -4792,11 +4647,6 @@ Needs to be here because ns_initialize_display_info () uses AppKit classes.
     return NULL;
   [NSApp setDelegate: NSApp];
 
-  /* Start the select thread.  */
-  [NSThread detachNewThreadSelector:@selector (fd_handler:)
-                           toTarget:NSApp
-                         withObject:nil];
-
   /* debugging: log all notifications */
   /*   [[NSNotificationCenter defaultCenter] addObserver: NSApp
                                          selector: @selector (logNotification:)
@@ -5178,10 +5028,6 @@ - (void)sendEvent: (NSEvent *)theEvent
           last_appdefined_event_data = [theEvent data1];
           [self stop: self];
         }
-      else
-        {
-          send_appdefined = YES;
-        }
     }
 
 
@@ -5484,95 +5330,6 @@ - (void)sendFromMainThread:(id)unused
   ns_send_appdefined (nextappdefined);
 }
 
-- (void)fd_handler:(id)unused
-/* --------------------------------------------------------------------------
-     Check data waiting on file descriptors and terminate if so
-   -------------------------------------------------------------------------- */
-{
-  int result;
-  int waiting = 1, nfds;
-  char c;
-
-  fd_set readfds, writefds, *wfds;
-  struct timespec timeout, *tmo;
-  NSAutoreleasePool *pool = nil;
-
-  /* NSTRACE ("fd_handler"); */
-
-  for (;;)
-    {
-      [pool release];
-      pool = [[NSAutoreleasePool alloc] init];
-
-      if (waiting)
-        {
-          fd_set fds;
-          FD_ZERO (&fds);
-          FD_SET (selfds[0], &fds);
-          result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
-          if (result > 0 && read (selfds[0], &c, 1) == 1 && c == 'g')
-    waiting = 0;
-        }
-      else
-        {
-          pthread_mutex_lock (&select_mutex);
-          nfds = select_nfds;
-
-          if (select_valid & SELECT_HAVE_READ)
-            readfds = select_readfds;
-          else
-            FD_ZERO (&readfds);
-
-          if (select_valid & SELECT_HAVE_WRITE)
-            {
-              writefds = select_writefds;
-              wfds = &writefds;
-            }
-          else
-            wfds = NULL;
-          if (select_valid & SELECT_HAVE_TMO)
-            {
-              timeout = select_timeout;
-              tmo = &timeout;
-            }
-          else
-            tmo = NULL;
-
-          pthread_mutex_unlock (&select_mutex);
-
-          FD_SET (selfds[0], &readfds);
-          if (selfds[0] >= nfds) nfds = selfds[0]+1;
-
-          result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
-
-          if (result == 0)
-            ns_send_appdefined (-2);
-          else if (result > 0)
-            {
-              if (FD_ISSET (selfds[0], &readfds))
-                {
-                  if (read (selfds[0], &c, 1) == 1 && c == 's')
-    waiting = 1;
-                }
-              else
-                {
-                  pthread_mutex_lock (&select_mutex);
-                  if (select_valid & SELECT_HAVE_READ)
-                    select_readfds = readfds;
-                  if (select_valid & SELECT_HAVE_WRITE)
-                    select_writefds = writefds;
-                  if (select_valid & SELECT_HAVE_TMO)
-                    select_timeout = timeout;
-                  pthread_mutex_unlock (&select_mutex);
-
-                  ns_send_appdefined (result);
-                }
-            }
-          waiting = 1;
-        }
-    }
-}
-
 
 
 /* ==========================================================================
@@ -6361,7 +6118,7 @@ - (void)mouseMoved: (NSEvent *)e
                       help_echo_object, help_echo_pos);
     }
 
-  if (emacsframe->mouse_moved && send_appdefined)
+  if (emacsframe->mouse_moved)
     ns_send_appdefined (-1);
 }
 
@@ -7058,8 +6815,7 @@ - (void)windowDidExpose: sender
   SET_FRAME_VISIBLE (emacsframe, 1);
   SET_FRAME_GARBAGED (emacsframe);
 
-  if (send_appdefined)
-    ns_send_appdefined (-1);
+  ns_send_appdefined (-1);
 }
 
 
--

Here we go. This seems to work. It turns out there are trade‐offs
between the GUI and network performance. If I remove
NSApp:nextEventMatchingMask from ns_select, eww loads web pages
*significantly* faster, but the scrollbars become effectively
unusable.

--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: [PATCH] Rework NS event handling (bug#25265)

Eli Zaretskii
> Date: Sat, 31 Dec 2016 16:09:30 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> Here we go. This seems to work. It turns out there are trade‐offs
> between the GUI and network performance. If I remove
> NSApp:nextEventMatchingMask from ns_select, eww loads web pages
> *significantly* faster, but the scrollbars become effectively
> unusable.

Wow, thanks a lot!

I cannot review the patch, as I don't really understand the details.
So I suggest to push this and see if users report bugs.

(I presume you have run all the tests, including those in
test/src/thread-tests.el and those posted in the few recent
discussions related to thread-related problems.  If not, I very much
recommend that you do that, to see if the new code passes these
"entry-level" tests.)



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: [PATCH] Rework NS event handling (bug#25265)

Alan Third
On Sat, Dec 31, 2016 at 06:25:58PM +0200, Eli Zaretskii wrote:

> > Date: Sat, 31 Dec 2016 16:09:30 +0000
> > From: Alan Third <[hidden email]>
> > Cc: [hidden email], [hidden email]
> >
> > Here we go. This seems to work. It turns out there are trade‐offs
> > between the GUI and network performance. If I remove
> > NSApp:nextEventMatchingMask from ns_select, eww loads web pages
> > *significantly* faster, but the scrollbars become effectively
> > unusable.
>
> Wow, thanks a lot!
>
> I cannot review the patch, as I don't really understand the details.
> So I suggest to push this and see if users report bugs.

I forgot to say before, I think there may be slightly more lag then
before when using the scroll bars. I don’t really use them so I don’t
know if people will be bothered by it. With NSTrace turned on I can
actually crash Emacs by waving the scroll bars about, but I can’t with
NSTrace off. Everything else seems fast enough. If I push it I suppose
we’ll find out soon enough if it’s acceptable.

> (I presume you have run all the tests, including those in
> test/src/thread-tests.el and those posted in the few recent
> discussions related to thread-related problems.  If not, I very much
> recommend that you do that, to see if the new code passes these
> "entry-level" tests.)

I hadn’t, but I have now. Make check reports 3 unexpected results as
usual, and the log for thread-tests.el shows 27/27 passed.

I tried this from one of the other threads (I’ve not looked through
them completely yet):

(dotimes (x 10) (make-thread (lambda ()
                               (let ((n (random 10)))
                                 (with-current-buffer "z"
                                   (sleep-for n)
                                   (insert (format "Foo:%d\n" n)))))))

and it seems to do what it’s supposed to do.
--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: [PATCH] Rework NS event handling (bug#25265)

Alan Third
In reply to this post by Alan Third
On Sat, Dec 31, 2016 at 04:09:30PM +0000, Alan Third wrote:

> * src/nsterm.m (unwind_apploopnr): Remove.
> (ns_read_socket): Remove references to apploopnr.  Make processing the
> NS event loop conditional on being in the main thread.
> (ns_select): Remove references to apploopnr.  Remove all fd_handler
> related stuff.  Check if there are events waiting on the NS event
> queue rather than running the event loop.  Remove unused variables and
> code.
> (fd_handler): Remove.
> (ns_term_init): Remove creation of fd_handler thread.
> (hold_event, EmacsApp:sendEvent, EmacsView:mouseMoved,
> EmacsView:windowDidExpose): Remove send_appdefined.
> (ns_send_appdefined): Always check the event queue for
> applicationDefined events rather than relying on send_appdefined var.
> * src/nsterm.h: Remove reference to fd_handler method.

OK, I’m running into performance bugs with this almost straight away.

It all looks OK until I start flyspell-mode. Then it appears that
redisplay is only called every two or three keypresses. It looks like
Emacs is still going fine, though, as messages to the modeline appear,
even if the action isn’t immediately displayed in the buffer.

For example, I open up an org file and start flyspell-mode, then I hit
the down arrow which should take me to a heading but the cursor
doesn’t move. Then I hit TAB, and I get a message in the modeline
telling me that the section associated with the heading has been
expanded, but the buffer is still displayed with the cursor on the
previous line and the section not expanded. Finally I hit the down
arrow again and the buffer updates to display the expanded section and
the cursor where I’d expect it.

emacsclient runs with a delay, which I guess corresponds to the
timeout on the NS event queue check.

I’m not at all sure how to fix these problems.

One option I thought about is to wrap the fd’s in ns_select with
NSFileHandle, which should then mean we could look for notifications
on the NS event queue, and emulate select that way. Unfortunately as
far as I can see NSFileHandle only provides an event for ‘data
available for reading’, and it looks like we’d need to be able to spot
write availability too.

(See under Notifications:
https://developer.apple.com/reference/foundation/nsfilehandle)
--
Alan Third



Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

bug#25265: [PATCH] Rework NS event handling (bug#25265)

Eli Zaretskii
> Date: Sun, 1 Jan 2017 15:03:52 +0000
> From: Alan Third <[hidden email]>
> Cc: [hidden email], [hidden email]
>
> OK, I’m running into performance bugs with this almost straight away.
>
> It all looks OK until I start flyspell-mode. Then it appears that
> redisplay is only called every two or three keypresses. It looks like
> Emacs is still going fine, though, as messages to the modeline appear,
> even if the action isn’t immediately displayed in the buffer.
>
> For example, I open up an org file and start flyspell-mode, then I hit
> the down arrow which should take me to a heading but the cursor
> doesn’t move. Then I hit TAB, and I get a message in the modeline
> telling me that the section associated with the heading has been
> expanded, but the buffer is still displayed with the cursor on the
> previous line and the section not expanded. Finally I hit the down
> arrow again and the buffer updates to display the expanded section and
> the cursor where I’d expect it.
>
> emacsclient runs with a delay, which I guess corresponds to the
> timeout on the NS event queue check.
>
> I’m not at all sure how to fix these problems.

Do you understand the problems, though?

The crucial question is why isn't redisplay being called as it was
called before the changes?  One way of answering that would be to run
the previous version under a debugger, put a breakpoint in
redisplay_internal, show a backtrace to see who called it, and
continue.  Then do the same in the new version, and see if you can
spot any differences.

Do you really need to type several keys to get redisplay?  Or is it
enough to type just one and wait for some time?  IOW, if you type one
key, then wait, does redisplay ever happen?

Thanks.



12
Loading...