]> git.eshelyaron.com Git - emacs.git/commitdiff
Improve event loop on NS so that no polling is used.
authorJan Djärv <jan.h.d@swipnet.se>
Wed, 15 Aug 2012 18:58:19 +0000 (20:58 +0200)
committerJan Djärv <jan.h.d@swipnet.se>
Wed, 15 Aug 2012 18:58:19 +0000 (20:58 +0200)
* nsmenu.m (popupSession): Remove.
(pop_down_menu): Remove endModalSession.
(timeout_handler:): New method.
(runDialogAt:): Get next timeout.  Start a NSTimer with that timeout.
Call runModalForWindow.  Check timer_fired when it returns.
If not set, cancel timer and break out of loop.
Otherwise loop again, with a new timeout.

* nsterm.h (EmacsApp): fd_handler takes id argument.
(EmacsDialogPanel): Add timer_fired and timeout_handler.

* nsterm.m: Include fcntl.h if present.
(fd_entry, t_readfds, inNsSelect): Remove.
(select_writefds, select_valid, select_timeout, selfds)
(select_mutex, apploopnr): Add.
(EV_TRAILER): Call kbd_buffer_store_event_hold only if q_event_ptr.
Otherwise call kbd_buffer_store_event.
(ns_send_appdefined): Remove release of fd_entry.
(ns_read_socket): Always send appdefined.  Remove inNsSelect check.
Increment and decrement apploopnr.
(ns_select): If no file descriptors, just do a NSTimer.
Otherwise copy read/write masks and start select thread (fd_handler).
Start main loop and wait for application defined event.
Inform select thread to stop selecting after main loop is exited.
(ns_term_init): Create selfds pipe and set non-blocking.
Initialize select_mutex. Start the select thread (fd_handler).
(fd_handler:): Loop forever, wait for info from the main thread
to either start or stop selecting.  When select returns, send
and appdefined event.
(sendScrollEventAtLoc:fromEvent:): Check if q_event_ptr is set.
If not call kbd_buffer_store_event.

src/ChangeLog
src/nsmenu.m
src/nsterm.h
src/nsterm.m

index 04a0df774f9b87606a32c3581337f056e64719df..6e49dd44fdecd51f68ae68a320366dc18aceefb7 100644 (file)
@@ -1,5 +1,37 @@
 2012-08-15  Jan Djärv  <jan.h.d@swipnet.se>
 
+       * nsmenu.m (popupSession): Remove.
+       (pop_down_menu): Remove endModalSession.
+       (timeout_handler:): New method.
+       (runDialogAt:): Get next timeout.  Start a NSTimer with that timeout.
+       Call runModalForWindow.  Check timer_fired when it returns.
+       If not set, cancel timer and break out of loop.
+       Otherwise loop again, with a new timeout.
+
+       * nsterm.m: Include fcntl.h if present.
+       (fd_entry, t_readfds, inNsSelect): Remove.
+       (select_writefds, select_valid, select_timeout, selfds)
+       (select_mutex, apploopnr): Add.
+       (EV_TRAILER): Call kbd_buffer_store_event_hold only if q_event_ptr.
+       Otherwise call kbd_buffer_store_event.
+       (ns_send_appdefined): Remove release of fd_entry.
+       (ns_read_socket): Always send appdefined.  Remove inNsSelect check.
+       Increment and decrement apploopnr.
+       (ns_select): If no file descriptors, just do a NSTimer.
+       Otherwise copy read/write masks and start select thread (fd_handler).
+       Start main loop and wait for application defined event.
+       Inform select thread to stop selecting after main loop is exited.
+       (ns_term_init): Create selfds pipe and set non-blocking.
+       Initialize select_mutex. Start the select thread (fd_handler).
+       (fd_handler:): Loop forever, wait for info from the main thread
+       to either start or stop selecting.  When select returns, send
+       and appdefined event.
+       (sendScrollEventAtLoc:fromEvent:): Check if q_event_ptr is set.
+       If not call kbd_buffer_store_event.
+
+       * nsterm.h (EmacsApp): fd_handler takes id argument.
+       (EmacsDialogPanel): Add timer_fired and timeout_handler.
+
        * gtkutil.c (xg_mark_data): Use FRAME_X_P.
 
 2012-08-15  Eli Zaretskii  <eliz@gnu.org>
index 210f4530d7a74c280b4d8be31077491f58823f45..657b9306942f6e5977f30f568dee48644ab19237 100644 (file)
@@ -73,7 +73,6 @@ EmacsMenu *mainMenu, *svcsMenu, *dockMenu;
 
 /* Nonzero means a menu is currently active.  */
 static int popup_activated_flag;
-static NSModalSession popupSession;
 
 /* Nonzero means we are tracking and updating menus.  */
 static int trackingMenu;
@@ -1365,8 +1364,6 @@ pop_down_menu (Lisp_Object arg)
     {
       EmacsDialogPanel *panel = unwind_data->dialog;
       popup_activated_flag = 0;
-      [NSApp endModalSession: popupSession];
-
       [panel close];
       [unwind_data->pool release];
       [[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
@@ -1756,20 +1753,40 @@ void process_dialog (id window, Lisp_Object list)
 }
 
 
+- (void)timeout_handler: (NSTimer *)timedEntry
+{
+  timer_fired = 1;
+  [NSApp abortModal];
+}
+
 - (Lisp_Object)runDialogAt: (NSPoint)p
 {
-  NSInteger ret;
+  NSInteger ret = 0;
 
-  /* initiate a session that will be ended by pop_down_menu */
-  popupSession = [NSApp beginModalSessionForWindow: self];
-  while (popup_activated_flag
-         && (ret = [NSApp runModalSession: popupSession])
-              == NSRunContinuesResponse)
+  while (popup_activated_flag)
     {
-      /* Run this for timers.el, indep of atimers; might not return.
-         TODO: use return value to avoid calling every iteration. */
-      timer_check ();
-      [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
+      NSTimer *tmo = nil;
+      EMACS_TIME next_time = timer_check ();
+
+      if (EMACS_TIME_VALID_P (next_time))
+        {
+          double time = EMACS_TIME_TO_DOUBLE (next_time);
+          tmo = [NSTimer timerWithTimeInterval: time
+                                        target: self
+                                      selector: @selector (timeout_handler:)
+                                      userInfo: 0
+                                       repeats: NO];
+          [[NSRunLoop currentRunLoop] addTimer: tmo
+                                       forMode: NSModalPanelRunLoopMode];
+        }
+      timer_fired = 0;
+      ret = [NSApp runModalForWindow: self];
+      if (! timer_fired)
+        {
+          if (tmo != nil) [tmo invalidate]; /* Cancels timer */
+          break;
+        }
     }
 
   {                            /* FIXME: BIG UGLY HACK!!! */
index de41929e3fd38aa20c949e40082005749ef8fd07..94984b3d35edd6abcd02db160db435ee0b590df0 100644 (file)
@@ -56,7 +56,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 - (void)sendEvent: (NSEvent *)theEvent;
 - (void)showPreferencesWindow: (id)sender;
 - (BOOL) openFile: (NSString *)fileName;
-- (void)fd_handler: (NSTimer *) fdEntry;
+- (void)fd_handler: (id)unused;
 - (void)timeout_handler: (NSTimer *)timedEntry;
 - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg;
 @end
@@ -195,12 +195,14 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    NSTextField *title;
    NSMatrix *matrix;
    int rows, cols;
+   int timer_fired;
    }
 - initFromContents: (Lisp_Object)menu isQuestion: (BOOL)isQ;
 - addButton: (char *)str value: (Lisp_Object)val row: (int)row;
 - addString: (char *)str row: (int)row;
 - addSplit;
 - (Lisp_Object)runDialogAt: (NSPoint)p;
+- (void)timeout_handler: (NSTimer *)timedEntry;
 @end
 
 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
index 5e3c3ac777f8d1b15af3b85d9020391e75c750c6..76e6ee8fb4074cfb7f4dcb77f251ba342fee7dbd 100644 (file)
@@ -39,6 +39,10 @@ GNUstep port and post-20 update by Adrian Robert (arobert@cogsci.ucsd.edu)
 #include <c-strcase.h>
 #include <ftoastr.h>
 
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
 #include "lisp.h"
 #include "blockinput.h"
 #include "sysselect.h"
@@ -184,17 +188,20 @@ static BOOL ns_menu_bar_is_hidden = NO;
 static BOOL send_appdefined = YES;
 static NSEvent *last_appdefined_event = 0;
 static NSTimer *timed_entry = 0;
-static NSTimer *fd_entry = nil;
 static NSTimer *scroll_repeat_entry = nil;
-static fd_set select_readfds, t_readfds;
-static int select_nfds;
+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 EMACS_TIME 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;
 static int n_emacs_events_pending = 0;
 static NSMutableArray *ns_pending_files, *ns_pending_service_names,
   *ns_pending_service_args;
-static BOOL inNsSelect = 0;
 static BOOL ns_do_open_file = NO;
 
 /* Convert modifiers in a NeXTstep event to emacs style modifiers.  */
@@ -252,15 +259,20 @@ static BOOL ns_do_open_file = NO;
 
 /* This is a piece of code which is common to all the event handling
    methods.  Maybe it should even be a function.  */
-#define EV_TRAILER(e)                                         \
-  {                                                           \
-  XSETFRAME (emacs_event->frame_or_window, emacsframe);       \
-  if (e) emacs_event->timestamp = EV_TIMESTAMP (e);           \
-  n_emacs_events_pending++;                                   \
-  kbd_buffer_store_event_hold (emacs_event, q_event_ptr);     \
-  EVENT_INIT (*emacs_event);                                  \
-  ns_send_appdefined (-1);                                    \
-  }
+#define EV_TRAILER(e)                                                   \
+    {                                                                   \
+      XSETFRAME (emacs_event->frame_or_window, emacsframe);             \
+      if (e) emacs_event->timestamp = EV_TIMESTAMP (e);                 \
+      if (q_event_ptr)                                                  \
+        {                                                               \
+          n_emacs_events_pending++;                                     \
+          kbd_buffer_store_event_hold (emacs_event, q_event_ptr);       \
+        }                                                               \
+      else                                                              \
+        kbd_buffer_store_event (emacs_event);                           \
+      EVENT_INIT (*emacs_event);                                        \
+      ns_send_appdefined (-1);                                          \
+    }
 
 void x_set_cursor_type (struct frame *, Lisp_Object, Lisp_Object);
 
@@ -3377,14 +3389,6 @@ ns_send_appdefined (int value)
           timed_entry = nil;
         }
 
-      /* Ditto for file descriptor poller */
-      if (fd_entry)
-        {
-          [fd_entry invalidate];
-          [fd_entry release];
-          fd_entry = nil;
-        }
-
       nxev = [NSEvent otherEventWithType: NSApplicationDefined
                                 location: NSMakePoint (0, 0)
                            modifierFlags: 0
@@ -3402,7 +3406,6 @@ ns_send_appdefined (int value)
     }
 }
 
-
 static int
 ns_read_socket (struct terminal *terminal, int expected,
                 struct input_event *hold_quit)
@@ -3466,24 +3469,14 @@ ns_read_socket (struct terminal *terminal, int expected,
       /* 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 called via ns_select, this is called once with expected=1,
-         because we expect either the timeout or file descriptor activity.
-         In this case the first event through will either be real input or
-         one of these.  read_avail_input() then calls once more with expected=0
-         and in that case we need to return quickly if there is nothing.
-         If we're being called outside of that, it's also OK to return quickly
-         after one iteration through the event loop, since other terms do
-         this and emacs expects it. */
-      if (!(inNsSelect && expected))
+      if (++apploopnr != 1)
         {
-          /* Post an application defined event on the event queue.  When this is
-             received the [NXApp run] will return, thus having processed all
-             events which are currently queued, if any.  */
-          ns_send_appdefined (-1);
+          abort ();
         }
-
       [NSApp run];
+      --apploopnr;
     }
 
   nevents = n_emacs_events_pending;
@@ -3503,65 +3496,89 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds,
    -------------------------------------------------------------------------- */
 {
   int result;
-  double time;
   NSEvent *ev;
-  struct timespec select_timeout;
+  int k, nr = 0;
+  struct input_event event;
+  char c;
 
 /*  NSTRACE (ns_select); */
 
-  if (NSApp == nil || inNsSelect == 1 /* || ([NSApp isActive] == NO &&
-                      [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil
- inMode:NSDefaultRunLoopMode dequeue:NO] == nil) */)
+  for (k = 0; readfds && k < nfds+1; k++)
+    if (FD_ISSET(k, readfds)) ++nr;
+
+  if (NSApp == nil
+      || (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
     return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
 
-  /* Save file descriptor set, which gets overwritten in calls to select ()
-     Note, this is called from process.c, and only readfds is ever set */
-  if (readfds)
+  [outerpool release];
+  outerpool = [[NSAutoreleasePool alloc] init];
+
+  
+  send_appdefined = YES;
+  if (nr > 0)
     {
-      memcpy (&select_readfds, readfds, sizeof (fd_set));
+      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';
+      write (selfds[1], &c, 1);
     }
-  else
-    select_nfds = 0;
-
-    /* Try an initial select for pending data on input files */
-  select_timeout.tv_sec = select_timeout.tv_nsec = 0;
-  result = pselect (nfds, readfds, writefds, exceptfds,
-                   &select_timeout, sigmask);
-  if (result)
-    return result;
-
-  /* if (!timeout || timed_entry || fd_entry)
-       fprintf (stderr, "assertion failed: timeout null or timed_entry/fd_entry non-null in ns_select\n"); */
-
-    /* set a timeout and run the main AppKit event loop while continuing
-       to monitor the files */
-  time = EMACS_TIME_TO_DOUBLE (*timeout);
-  timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
-                                           target: NSApp
-                                         selector: @selector (timeout_handler:)
-                                         userInfo: 0
-                                          repeats: YES] /* for safe removal */
-                                                         retain];
-
-  /* set a periodic task to try the pselect () again */
-  fd_entry = [[NSTimer scheduledTimerWithTimeInterval: 0.1
-                                               target: NSApp
-                                             selector: @selector (fd_handler:)
-                                             userInfo: 0
-                                              repeats: YES]
-               retain];
-
-  /* Let Application dispatch events until it receives an event of the type
-     NX_APPDEFINED, which should only be sent by timeout_handler.
-     We tell read_avail_input() that input is "expected" because we do expect
-     either the timeout or fd handler to fire, and if they don't, the original
-     call from process.c that got us here expects us to wait until some input
-     comes. */
-  inNsSelect = 1;
-  gobble_input (1);
+  else if (nr == 0 && timeout)
+    {
+      /* No file descriptor, just a timeout, no need to wake fd_handler  */
+      double time = EMACS_TIME_TO_DOUBLE (*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);
+    }
+
+  EVENT_INIT (event);
+  BLOCK_INPUT;
+  emacs_event = &event;
+  if (++apploopnr != 1)
+    {
+      abort();
+    }
+  [NSApp run];
+  --apploopnr;
+  emacs_event = NULL;
+  if (nr > 0 && readfds)
+    {
+      c = 's';
+      write (selfds[1], &c, 1);
+    }
+  UNBLOCK_INPUT;
+
   ev = last_appdefined_event;
-  inNsSelect = 0;
 
   if (ev)
     {
@@ -3575,25 +3592,28 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds,
       if (t == -2)
         {
           /* The NX_APPDEFINED event we received was a timeout. */
-          return 0;
+          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;
-          return -1;
+          result = -1;
         }
       else
         {
-          /* Received back from pselect () in fd_handler; copy the results */
-          if (readfds)
-            memcpy (readfds, &select_readfds, sizeof (fd_set));
-          return t;
+          /* 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;
+          if (timeout) *timeout = select_timeout;
+          pthread_mutex_unlock (&select_mutex);
+          result = t;
         }
     }
-  /* never reached, shut compiler up */
-  return 0;
+
+  return result;
 }
 
 
@@ -4024,6 +4044,21 @@ ns_term_init (Lisp_Object display_name)
     {
       baud_rate = 38400;
       Fset_input_interrupt_mode (Qnil);
+
+      if (selfds[0] == -1)
+        {
+          if (pipe (selfds) == -1)
+            {
+              fprintf (stderr, "Failed to create pipe: %s\n",
+                       emacs_strerror (errno));
+              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_initialized = 1;
     }
 
@@ -4039,6 +4074,11 @@ ns_term_init (Lisp_Object display_name)
     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:)
@@ -4547,26 +4587,91 @@ not_in_argv (NSString *arg)
   ns_send_appdefined (-2);
 }
 
-- (void)fd_handler: (NSTimer *) fdEntry
+- (void)fd_handler:(id)unused
 /* --------------------------------------------------------------------------
      Check data waiting on file descriptors and terminate if so
    -------------------------------------------------------------------------- */
 {
   int result;
-  struct timespec select_timeout;
-  /* NSTRACE (fd_handler); */
+  int waiting = 1, nfds;
+  char c;
 
-  if (select_nfds == 0)
-    return;
+  SELECT_TYPE readfds, writefds, *wfds;
+  EMACS_TIME timeout, *tmo;
 
-  memcpy (&t_readfds, &select_readfds, sizeof (fd_set));
+  /* NSTRACE (fd_handler); */
 
-  select_timeout.tv_sec = select_timeout.tv_nsec = 0;
-  result = pselect (select_nfds, &t_readfds, NULL, NULL, &select_timeout, NULL);
-  if (result)
+  for (;;) 
     {
-      memcpy (&select_readfds, &t_readfds, sizeof (fd_set));
-      ns_send_appdefined (result);
+      if (waiting)
+        {
+          SELECT_TYPE fds;
+
+          FD_SET (selfds[0], &fds);
+          result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
+          if (result > 0)
+            {
+              read (selfds[0], &c, 1);
+              if (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))
+                {
+                  read (selfds[0], &c, 1);
+                  if (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;
+        }
     }
 }
 
@@ -6404,8 +6509,13 @@ not_in_argv (NSString *arg)
   XSETINT (emacs_event->x, loc * pixel_height);
   XSETINT (emacs_event->y, pixel_height-20);
 
-  n_emacs_events_pending++;
-  kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
+  if (q_event_ptr)
+    {
+      n_emacs_events_pending++;
+      kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
+    }
+  else
+    kbd_buffer_store_event (emacs_event);
   EVENT_INIT (*emacs_event);
   ns_send_appdefined (-1);
 }