all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* 1024 file descriptors should be enough for anyone
@ 2022-05-04 15:22 Robert Pluim
  2022-05-05  0:55 ` Po Lu
                   ` (2 more replies)
  0 siblings, 3 replies; 26+ messages in thread
From: Robert Pluim @ 2022-05-04 15:22 UTC (permalink / raw)
  To: emacs-devel

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


But for those people who insist on using LSP with large codebases,
Emacs's use of 'select' is somewhat limiting.

Attached an initial attempt at using poll() instead of select(), and
bumping the open filed descriptor limit to 10240. It passes 'make
check' on GNU/Linux, FreeBSD 13, and macOS, and M-x eww
works. MS-Windows should still build with no change in functionality.

I haven't tried --with-pgtk or a lucid build, but I donʼt anticipate
any major issues there.

Comments welcome (I can stick it on a branch if needed). Things that
could be done:

- make the upper limit either statically or dynamically configurable
  (on GNU/Linux in theory you could have 1e6 open descriptors)
- a feature flag
- dynamic switching between poll and select

Thanks

Robert
-- 

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Allow-the-use-of-poll-instead-of-select.patch --]
[-- Type: text/x-diff, Size: 35853 bytes --]

From d32be58131e424450a621ffa23726dcbac090812 Mon Sep 17 00:00:00 2001
From: Robert Pluim <rpluim@gmail.com>
Date: Wed, 4 May 2022 17:09:07 +0200
Subject: [PATCH] Allow the use of poll instead of select
To: emacs-devel@gnu.org

As a bonus, increase the maximum number of open files allowed to
10 x FD_SETSIZE (which is what poll is limited to on macOS).

* configure.ac (--with-poll): New option.
(USE_POLL): New variable.
(EMACS_CONFIG_FEATURES): Add USE_POLL.
* etc/NEWS: Document --with-poll.
---
 configure.ac    |  17 +++-
 etc/NEWS        |   9 ++
 src/nsterm.m    |  11 ++-
 src/process.c   | 249 ++++++++++++++++++++++++++++++++++--------------
 src/sysdep.c    |   4 +-
 src/syspoll.h   |  34 +++++++
 src/sysselect.h |  36 ++++++-
 src/thread.c    |   3 +
 src/xgselect.c  |   4 +
 src/xmenu.c     |   3 +
 src/xterm.c     |   3 +
 11 files changed, 291 insertions(+), 82 deletions(-)
 create mode 100644 src/syspoll.h

diff --git a/configure.ac b/configure.ac
index 484ce980a5..4785f7365e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1668,6 +1668,18 @@ AC_DEFUN
   [The type of system you are compiling for; sets 'system-type'.])
 AC_SUBST([SYSTEM_TYPE])
 
+OPTION_DEFAULT_OFF([poll],
+  [(experimental) use poll() instead of select() for checking file
+  descriptor status.])
+if test "$with_poll" = yes; then
+  AC_CHECK_HEADERS(sys/poll.h)
+  if test "x$ac_cv_header_sys_poll_h" = xyes; then
+    AC_DEFINE([USE_POLL], 1, [Define to 1 if you use poll.])
+    USE_POLL=yes
+  else
+    USE_POLL=no
+  fi
+fi
 
 pre_PKG_CONFIG_CFLAGS=$CFLAGS
 pre_PKG_CONFIG_LIBS=$LIBS
@@ -6345,7 +6357,7 @@ AC_DEFUN
 emacs_config_features=
 for opt in ACL BE_APP CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
  HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
- M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PGTK PNG RSVG SECCOMP \
+ M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PGTK PNG POLL RSVG SECCOMP \
  SOUND SQLITE3 THREADS TIFF TOOLKIT_SCROLL_BARS \
  UNEXEC WEBP X11 XAW3D XDBE XFT XIM XINPUT2 XPM XWIDGETS X_TOOLKIT \
  ZLIB; do
@@ -6355,7 +6367,7 @@ AC_DEFUN
       UNEXEC) val=${with_unexec} ;;
       GLIB) val=${emacs_cv_links_glib} ;;
       NOTIFY|ACL) eval val=\${${opt}_SUMMARY} ;;
-      TOOLKIT_SCROLL_BARS|X_TOOLKIT) eval val=\${USE_$opt} ;;
+      TOOLKIT_SCROLL_BARS|X_TOOLKIT|POLL) eval val=\${USE_$opt} ;;
       THREADS) val=${threads_enabled} ;;
       *) eval val=\${HAVE_$opt} ;;
     esac
@@ -6426,6 +6438,7 @@ AC_DEFUN
   Which dumping strategy does Emacs use?                  ${with_dumping}
   Does Emacs have native lisp compiler?                   ${HAVE_NATIVE_COMP}
   Does Emacs use version 2 of the the X Input Extension?  ${HAVE_XINPUT2}
+  Does Emacs use poll()?                                  ${USE_POLL}
 "])
 
 if test -n "${EMACSDATA}"; then
diff --git a/etc/NEWS b/etc/NEWS
index 6637eda00c..f689bc8d8d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -39,6 +39,15 @@ C++ compiler to be present on your system.  If Emacs is not built with
 the option '--with-be-app', the resulting Emacs will only run in
 text-mode terminals.
 
+** Emacs can now use poll() and larger file descriptor sets.
+There is experimental support for using poll() instead of select() to
+check file descriptor statuses, which can be requested by using the
+'--with-poll' option to the 'configure' script.  As part of this
+feature, the maximum number of open files supported has been increased
+to 10xFD_SETSIZE (which is typically 1024).  Note that there may be
+ulimit or kernel limits that still restrict the number of simultaneous
+open files.  This feature is not supported on MS-Windows.
+
 +++
 ** Cairo drawing support has been enabled for Haiku builds.
 To enable Cairo support, ensure that the Cairo and FreeType
diff --git a/src/nsterm.m b/src/nsterm.m
index dfb7c5d202..31b13bc570 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -46,6 +46,9 @@ Updated by Christian Limpach (chris@nice.ch)
 #include "lisp.h"
 #include "blockinput.h"
 #include "sysselect.h"
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
 #include "nsterm.h"
 #include "systime.h"
 #include "character.h"
@@ -4469,7 +4472,7 @@ in certain situations (rapid incoming events).
       return -1;
     }
 
-  eassert (nfds <= FD_SETSIZE);
+  eassert (nfds <= EMACS_MAX_FD);
   for (k = 0; k < nfds; k++)
     {
       if (readfds && FD_ISSET(k, readfds)) ++nr;
@@ -5704,15 +5707,15 @@ - (void)applicationDidFinishLaunching: (NSNotification *)notification
      maximum number of open files for the process in their first call.
      We make dummy calls to them and then reduce the resource limit
      here, since pselect cannot handle file descriptors that are
-     greater than or equal to FD_SETSIZE.  */
+     greater than or equal to EMACS_MAX_FD.  */
   CFSocketGetTypeID ();
   CFFileDescriptorGetTypeID ();
   [[NSFileHandle alloc] init];
   struct rlimit rlim;
   if (getrlimit (RLIMIT_NOFILE, &rlim) == 0
-      && rlim.rlim_cur > FD_SETSIZE)
+      && rlim.rlim_cur > EMACS_MAX_FD)
     {
-      rlim.rlim_cur = FD_SETSIZE;
+      rlim.rlim_cur = EMACS_MAX_FD;
       setrlimit (RLIMIT_NOFILE, &rlim);
     }
   if ([NSApp activationPolicy] == NSApplicationActivationPolicyProhibited) {
diff --git a/src/process.c b/src/process.c
index 08a02ad942..80d9cc461f 100644
--- a/src/process.c
+++ b/src/process.c
@@ -48,7 +48,7 @@ #define PIPECONN1_P(p) false
 #ifdef HAVE_SETRLIMIT
 # include <sys/resource.h>
 
-/* If NOFILE_LIMIT.rlim_cur is greater than FD_SETSIZE, then
+/* If NOFILE_LIMIT.rlim_cur is greater than EMACS_MAX_FD, then
    NOFILE_LIMIT is the initial limit on the number of open files,
    which should be restored in child processes.  */
 static struct rlimit nofile_limit;
@@ -113,6 +113,9 @@ #define HAVE_LOCAL_SOCKETS
 #include "blockinput.h"
 #include "atimer.h"
 #include "sysselect.h"
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
 #include "syssignal.h"
 #include "syswait.h"
 #ifdef HAVE_GNUTLS
@@ -299,7 +302,7 @@ network_lookup_address_info_1 (Lisp_Object host, const char *service,
 static void child_signal_notify (void);
 
 /* Indexed by descriptor, gives the process (if any) for that descriptor.  */
-static Lisp_Object chan_process[FD_SETSIZE];
+static Lisp_Object chan_process[EMACS_MAX_FD];
 static void wait_for_socket_fds (Lisp_Object, char const *);
 
 /* Alist of elements (NAME . PROCESS).  */
@@ -311,18 +314,18 @@ network_lookup_address_info_1 (Lisp_Object host, const char *service,
    output from the process is to read at least one char.
    Always -1 on systems that support FIONREAD.  */
 
-static int proc_buffered_char[FD_SETSIZE];
+static int proc_buffered_char[EMACS_MAX_FD];
 
 /* Table of `struct coding-system' for each process.  */
-static struct coding_system *proc_decode_coding_system[FD_SETSIZE];
-static struct coding_system *proc_encode_coding_system[FD_SETSIZE];
+static struct coding_system *proc_decode_coding_system[EMACS_MAX_FD];
+static struct coding_system *proc_encode_coding_system[EMACS_MAX_FD];
 
 #ifdef DATAGRAM_SOCKETS
 /* Table of `partner address' for datagram sockets.  */
 static struct sockaddr_and_len {
   struct sockaddr *sa;
   ptrdiff_t len;
-} datagram_address[FD_SETSIZE];
+} datagram_address[EMACS_MAX_FD];
 #define DATAGRAM_CHAN_P(chan)	(datagram_address[chan].sa != 0)
 #define DATAGRAM_CONN_P(proc)                                           \
   (PROCESSP (proc) &&                                                   \
@@ -460,8 +463,112 @@ make_lisp_proc (struct Lisp_Process *p)
   /* If this fd is currently being selected on by a thread, this
      points to the thread.  Otherwise it is NULL.  */
   struct thread_state *waiting_thread;
-} fd_callback_info[FD_SETSIZE];
+} fd_callback_info[EMACS_MAX_FD];
+
+#ifdef USE_POLL
+struct pollfd pollfds[EMACS_MAX_FD];
+
+/* Convert a read set and a write set to the corresponding array of
+   struct pollfd.  maxfds is the highest fd set in the sets.  Returns
+   the number of file descriptors set in the array.  rset and wset can
+   be NULL, in which case they will be treated as if they were empty
+   sets.  */
+
+int
+fd_sets_to_pollfds (fd_set *rset, fd_set *wset, int maxfds)
+{
+  int poll_idx = 0;
+  fd_set dummy_rset;
+  fd_set dummy_wset;
 
+  if (!rset)
+    {
+      FD_ZERO (&dummy_rset);
+      rset = &dummy_rset;
+    }
+  if (!wset)
+    {
+      FD_ZERO (&dummy_wset);
+      wset = &dummy_wset;
+    }
+  for (int i = 0; i < maxfds; i++)
+    {
+      short flag = 0;
+      if (FD_ISSET (i, rset))
+	flag |= POLLIN;
+      if (FD_ISSET (i, wset))
+	flag |= POLLOUT;
+      if (flag != 0)
+	{
+	  pollfds[poll_idx].fd = i;
+	  pollfds[poll_idx].events = flag;
+	  poll_idx++;
+	}
+    }
+  return poll_idx;
+}
+
+/* Convert an array of struct pollfd to the corresponding read and
+   write fd_sets.  poll_count is the number of file descriptors set in
+   the array.  rset and wset can be NULL, in which case they're
+   treated as if they were empty.  */
+
+void
+pollfds_to_fd_sets (fd_set *rset, fd_set *wset, int poll_count)
+{
+  fd_set dummy_rset;
+  fd_set dummy_wset;
+
+  if (!rset)
+    rset = &dummy_rset;
+  FD_ZERO (rset);
+  if (!wset)
+    wset = &dummy_wset;
+  FD_ZERO (wset);
+  for (int i = 0; i < poll_count; i++)
+    {
+      if (pollfds[i].revents & (POLLIN|POLLHUP))
+	FD_SET (pollfds[i].fd, rset);
+      if (pollfds[i].revents & POLLOUT)
+	FD_SET (pollfds[i].fd, wset);
+    }
+}
+
+/* Convert a struct timespec to the corresponding timeout in
+   milliseconds.  A NULL timespec is treated as infinity.  */
+
+int
+timespec_to_timeout (const struct timespec *ts)
+{
+  if (!ts)
+    return -1;
+  return (ts->tv_sec * 1000 + ts->tv_nsec / 1000000);
+}
+
+/* Wrapper around poll() with the calling convention of pselect.
+   Converts arguments as appropriate.  */
+int
+emacs_pselect (int nfds, fd_set *readfds, fd_set *writefds,
+	       fd_set *errorfds, const struct timespec *timeout,
+	       const sigset_t *sigmask)
+{
+  int ret;
+  int poll_count;
+
+  poll_count = fd_sets_to_pollfds (readfds, writefds, nfds);
+  ret = poll (pollfds, poll_count, timespec_to_timeout (timeout));
+  if (ret > 0)
+    pollfds_to_fd_sets(readfds, writefds, poll_count);
+  else
+    {
+      if (readfds)
+	FD_ZERO (readfds);
+      if (writefds)
+	FD_ZERO (writefds);
+    }
+  return ret;
+}
+#endif	/* USE_POLL */
 
 /* Add a file descriptor FD to be monitored for when read is possible.
    When read is possible, call FUNC with argument DATA.  */
@@ -471,7 +578,7 @@ add_read_fd (int fd, fd_callback func, void *data)
 {
   add_keyboard_wait_descriptor (fd);
 
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   fd_callback_info[fd].func = func;
   fd_callback_info[fd].data = data;
 }
@@ -486,14 +593,14 @@ add_non_keyboard_read_fd (int fd, fd_callback func, void *data)
 static void
 add_process_read_fd (int fd)
 {
-  eassert (fd >= 0 && fd < FD_SETSIZE);
+  eassert (fd >= 0 && fd < EMACS_MAX_FD);
   eassert (fd_callback_info[fd].func == NULL);
 
   fd_callback_info[fd].flags &= ~KEYBOARD_FD;
   fd_callback_info[fd].flags |= FOR_READ;
   if (fd > max_desc)
     max_desc = fd;
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   fd_callback_info[fd].flags |= PROCESS_FD;
 }
 
@@ -504,7 +611,7 @@ delete_read_fd (int fd)
 {
   delete_keyboard_wait_descriptor (fd);
 
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   if (fd_callback_info[fd].flags == 0)
     {
       fd_callback_info[fd].func = 0;
@@ -518,7 +625,7 @@ delete_read_fd (int fd)
 void
 add_write_fd (int fd, fd_callback func, void *data)
 {
-  eassert (fd >= 0 && fd < FD_SETSIZE);
+  eassert (fd >= 0 && fd < EMACS_MAX_FD);
 
   fd_callback_info[fd].func = func;
   fd_callback_info[fd].data = data;
@@ -530,7 +637,7 @@ add_write_fd (int fd, fd_callback func, void *data)
 static void
 add_non_blocking_write_fd (int fd)
 {
-  eassert (fd >= 0 && fd < FD_SETSIZE);
+  eassert (fd >= 0 && fd < EMACS_MAX_FD);
   eassert (fd_callback_info[fd].func == NULL);
 
   fd_callback_info[fd].flags |= FOR_WRITE | NON_BLOCKING_CONNECT_FD;
@@ -544,7 +651,7 @@ recompute_max_desc (void)
 {
   int fd;
 
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = max_desc; fd >= 0; --fd)
     {
       if (fd_callback_info[fd].flags != 0)
@@ -553,7 +660,7 @@ recompute_max_desc (void)
 	  break;
 	}
     }
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
 }
 
 /* Stop monitoring file descriptor FD for when write is possible.  */
@@ -561,7 +668,7 @@ recompute_max_desc (void)
 void
 delete_write_fd (int fd)
 {
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   if ((fd_callback_info[fd].flags & NON_BLOCKING_CONNECT_FD) != 0)
     {
       if (--num_pending_connects < 0)
@@ -584,7 +691,7 @@ compute_input_wait_mask (fd_set *mask)
   int fd;
 
   FD_ZERO (mask);
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; ++fd)
     {
       if (fd_callback_info[fd].thread != NULL
@@ -607,7 +714,7 @@ compute_non_process_wait_mask (fd_set *mask)
   int fd;
 
   FD_ZERO (mask);
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; ++fd)
     {
       if (fd_callback_info[fd].thread != NULL
@@ -631,7 +738,7 @@ compute_non_keyboard_wait_mask (fd_set *mask)
   int fd;
 
   FD_ZERO (mask);
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; ++fd)
     {
       if (fd_callback_info[fd].thread != NULL
@@ -655,7 +762,7 @@ compute_write_mask (fd_set *mask)
   int fd;
 
   FD_ZERO (mask);
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; ++fd)
     {
       if (fd_callback_info[fd].thread != NULL
@@ -677,7 +784,7 @@ clear_waiting_thread_info (void)
 {
   int fd;
 
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; ++fd)
     {
       if (fd_callback_info[fd].waiting_thread == current_thread)
@@ -969,10 +1076,10 @@ update_processes_for_thread_death (Lisp_Object dying_thread)
 	  struct Lisp_Process *proc = XPROCESS (process);
 
 	  pset_thread (proc, Qnil);
-	  eassert (proc->infd < FD_SETSIZE);
+	  eassert (proc->infd < EMACS_MAX_FD);
 	  if (proc->infd >= 0)
 	    fd_callback_info[proc->infd].thread = NULL;
-	  eassert (proc->outfd < FD_SETSIZE);
+	  eassert (proc->outfd < EMACS_MAX_FD);
 	  if (proc->outfd >= 0)
 	    fd_callback_info[proc->outfd].thread = NULL;
 	}
@@ -1413,10 +1520,10 @@ DEFUN ("set-process-thread", Fset_process_thread, Sset_process_thread,
 
   proc = XPROCESS (process);
   pset_thread (proc, thread);
-  eassert (proc->infd < FD_SETSIZE);
+  eassert (proc->infd < EMACS_MAX_FD);
   if (proc->infd >= 0)
     fd_callback_info[proc->infd].thread = tstate;
-  eassert (proc->outfd < FD_SETSIZE);
+  eassert (proc->outfd < EMACS_MAX_FD);
   if (proc->outfd >= 0)
     fd_callback_info[proc->outfd].thread = tstate;
 
@@ -2144,7 +2251,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
 	}
     }
 
-  if (FD_SETSIZE <= inchannel || FD_SETSIZE <= outchannel)
+  if (EMACS_MAX_FD <= inchannel || EMACS_MAX_FD <= outchannel)
     report_file_errno ("Creating pipe", Qnil, EMFILE);
 
 #ifndef WINDOWSNT
@@ -2156,7 +2263,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
   fcntl (outchannel, F_SETFL, O_NONBLOCK);
 
   /* Record this as an active process, with its channels.  */
-  eassert (0 <= inchannel && inchannel < FD_SETSIZE);
+  eassert (0 <= inchannel && inchannel < EMACS_MAX_FD);
   chan_process[inchannel] = process;
   p->infd = inchannel;
   p->outfd = outchannel;
@@ -2251,7 +2358,7 @@ create_pty (Lisp_Object process)
   if (pty_fd >= 0)
     {
       p->open_fd[SUBPROCESS_STDIN] = pty_fd;
-      if (FD_SETSIZE <= pty_fd)
+      if (EMACS_MAX_FD <= pty_fd)
 	report_file_errno ("Opening pty", Qnil, EMFILE);
 #if ! defined (USG) || defined (USG_SUBTTY_WORKS)
       /* On most USG systems it does not work to open the pty's tty here,
@@ -2274,7 +2381,7 @@ create_pty (Lisp_Object process)
 
       /* Record this as an active process, with its channels.
 	 As a result, child_setup will close Emacs's side of the pipes.  */
-      eassert (0 <= pty_fd && pty_fd < FD_SETSIZE);
+      eassert (0 <= pty_fd && pty_fd < EMACS_MAX_FD);
       chan_process[pty_fd] = process;
       p->infd = pty_fd;
       p->outfd = pty_fd;
@@ -2360,7 +2467,7 @@ DEFUN ("make-pipe-process", Fmake_pipe_process, Smake_pipe_process,
   outchannel = p->open_fd[WRITE_TO_SUBPROCESS];
   inchannel = p->open_fd[READ_FROM_SUBPROCESS];
 
-  if (FD_SETSIZE <= inchannel || FD_SETSIZE <= outchannel)
+  if (EMACS_MAX_FD <= inchannel || EMACS_MAX_FD <= outchannel)
     report_file_errno ("Creating pipe", Qnil, EMFILE);
 
   fcntl (inchannel, F_SETFL, O_NONBLOCK);
@@ -2371,7 +2478,7 @@ DEFUN ("make-pipe-process", Fmake_pipe_process, Smake_pipe_process,
 #endif
 
   /* Record this as an active process, with its channels.  */
-  eassert (0 <= inchannel && inchannel < FD_SETSIZE);
+  eassert (0 <= inchannel && inchannel < EMACS_MAX_FD);
   chan_process[inchannel] = proc;
   p->infd = inchannel;
   p->outfd = outchannel;
@@ -2702,7 +2809,7 @@ DEFUN ("process-datagram-address", Fprocess_datagram_address, Sprocess_datagram_
     return Qnil;
 
   channel = XPROCESS (process)->infd;
-  eassert (0 <= channel && channel < FD_SETSIZE);
+  eassert (0 <= channel && channel < EMACS_MAX_FD);
   return conv_sockaddr_to_lisp (datagram_address[channel].sa,
 				datagram_address[channel].len);
 }
@@ -2731,7 +2838,7 @@ DEFUN ("set-process-datagram-address", Fset_process_datagram_address, Sset_proce
   channel = XPROCESS (process)->infd;
 
   len = get_lisp_to_sockaddr_size (address, &family);
-  eassert (0 <= channel && channel < FD_SETSIZE);
+  eassert (0 <= channel && channel < EMACS_MAX_FD);
   if (len == 0 || datagram_address[channel].len != len)
     return Qnil;
   conv_lisp_to_sockaddr (family, address, datagram_address[channel].sa, len);
@@ -3105,13 +3212,13 @@ DEFUN ("make-serial-process", Fmake_serial_process, Smake_serial_process,
 
   fd = serial_open (port);
   p->open_fd[SUBPROCESS_STDIN] = fd;
-  if (FD_SETSIZE <= fd)
+  if (EMACS_MAX_FD <= fd)
     report_file_errno ("Opening serial port", port, EMFILE);
   p->infd = fd;
   p->outfd = fd;
   if (fd > max_desc)
     max_desc = fd;
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   chan_process[fd] = proc;
 
   buffer = Fplist_get (contact, QCbuffer);
@@ -3283,7 +3390,7 @@ finish_after_tls_connection (Lisp_Object proc)
 		    Fplist_get (contact, QChost),
 		    Fplist_get (contact, QCservice));
 
-  eassert (p->outfd < FD_SETSIZE);
+  eassert (p->outfd < EMACS_MAX_FD);
   if (NILP (result))
     {
       pset_status (p, list2 (Qfailed,
@@ -3329,7 +3436,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
   if (!NILP (use_external_socket_p))
     {
       socket_to_use = external_sock_fd;
-      eassert (socket_to_use < FD_SETSIZE);
+      eassert (socket_to_use < EMACS_MAX_FD);
 
       /* Ensure we don't consume the external socket twice.  */
       external_sock_fd = -1;
@@ -3372,7 +3479,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
 	      continue;
 	    }
 	  /* Reject file descriptors that would be too large.  */
-	  if (FD_SETSIZE <= s)
+	  if (EMACS_MAX_FD <= s)
 	    {
 	      emacs_close (s);
 	      s = -1;
@@ -3510,7 +3617,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
 	      if (errno == EINTR)
 		goto retry_select;
 	      else
-		report_file_error ("Failed select", Qnil);
+		report_file_error ("Failed select/poll", Qnil);
 	    }
 	  eassert (sc > 0);
 
@@ -3543,7 +3650,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
 #ifdef DATAGRAM_SOCKETS
       if (p->socktype == SOCK_DGRAM)
 	{
-	  eassert (0 <= s && s < FD_SETSIZE);
+	  eassert (0 <= s && s < EMACS_MAX_FD);
 	  if (datagram_address[s].sa)
 	    emacs_abort ();
 
@@ -3608,7 +3715,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
   inch = s;
   outch = s;
 
-  eassert (0 <= inch && inch < FD_SETSIZE);
+  eassert (0 <= inch && inch < EMACS_MAX_FD);
   chan_process[inch] = proc;
 
   fcntl (inch, F_SETFL, O_NONBLOCK);
@@ -3635,7 +3742,7 @@ connect_network_socket (Lisp_Object proc, Lisp_Object addrinfos,
       if (! (connecting_status (p->status)
 	     && EQ (XCDR (p->status), addrinfos)))
 	pset_status (p, Fcons (Qconnect, addrinfos));
-      eassert (0 <= inch && inch < FD_SETSIZE);
+      eassert (0 <= inch && inch < EMACS_MAX_FD);
       if ((fd_callback_info[inch].flags & NON_BLOCKING_CONNECT_FD) == 0)
 	add_non_blocking_write_fd (inch);
     }
@@ -4703,7 +4810,7 @@ deactivate_process (Lisp_Object proc)
     close_process_fd (&p->open_fd[i]);
 
   inchannel = p->infd;
-  eassert (inchannel < FD_SETSIZE);
+  eassert (inchannel < EMACS_MAX_FD);
   if (inchannel >= 0)
     {
       p->infd  = -1;
@@ -4839,7 +4946,7 @@ server_accept_connection (Lisp_Object server, int channel)
 
   s = accept4 (channel, &saddr.sa, &len, SOCK_CLOEXEC);
 
-  if (FD_SETSIZE <= s)
+  if (EMACS_MAX_FD <= s)
     {
       emacs_close (s);
       s = -1;
@@ -4943,7 +5050,7 @@ server_accept_connection (Lisp_Object server, int channel)
   Lisp_Object name = Fformat (nargs, args);
   Lisp_Object proc = make_process (name);
 
-  eassert (0 <= s && s < FD_SETSIZE);
+  eassert (0 <= s && s < EMACS_MAX_FD);
   chan_process[s] = proc;
 
   fcntl (s, F_SETFL, O_NONBLOCK);
@@ -5226,7 +5333,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
       if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
 	break;
 
-      eassert (max_desc < FD_SETSIZE);
+      eassert (max_desc < EMACS_MAX_FD);
 
 #if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS
       {
@@ -5359,7 +5466,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 	     because otherwise we wouldn't run into a timeout
 	     below.  */
 	  int fd = child_signal_read_fd;
-	  eassert (fd < FD_SETSIZE);
+	  eassert (fd < EMACS_MAX_FD);
 	  if (0 <= fd)
 	    FD_CLR (fd, &Atemp);
 
@@ -5453,7 +5560,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 	 an asynchronous process.  Otherwise this might deadlock if we
 	 receive a SIGCHLD during `pselect'.  */
       int child_fd = child_signal_read_fd;
-      eassert (child_fd < FD_SETSIZE);
+      eassert (child_fd < EMACS_MAX_FD);
       if (0 <= child_fd)
         FD_SET (child_fd, &Available);
 
@@ -5563,7 +5670,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 	     And if so, we need to skip the select which could block. */
 	  FD_ZERO (&tls_available);
 	  tls_nfds = 0;
-	  for (channel = 0; channel < FD_SETSIZE; ++channel)
+	  for (channel = 0; channel < EMACS_MAX_FD; ++channel)
 	    if (! NILP (chan_process[channel])
 		&& FD_ISSET (channel, &Available))
 	      {
@@ -5625,7 +5732,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 	      else if (nfds > 0)
 		/* Slow path, merge one by one.  Note: nfds does not need
 		   to be accurate, just positive is enough. */
-		for (channel = 0; channel < FD_SETSIZE; ++channel)
+		for (channel = 0; channel < EMACS_MAX_FD; ++channel)
 		  if (FD_ISSET(channel, &tls_available))
 		    FD_SET(channel, &Available);
 	    }
@@ -6019,7 +6126,7 @@ read_process_output (Lisp_Object proc, int channel)
 {
   ssize_t nbytes;
   struct Lisp_Process *p = XPROCESS (proc);
-  eassert (0 <= channel && channel < FD_SETSIZE);
+  eassert (0 <= channel && channel < EMACS_MAX_FD);
   struct coding_system *coding = proc_decode_coding_system[channel];
   int carryover = p->decoding_carryover;
   ptrdiff_t readmax = clip_to_bounds (1, read_process_output_max, PTRDIFF_MAX);
@@ -6184,7 +6291,7 @@ read_and_dispose_of_process_output (struct Lisp_Process *p, char *chars,
 	 proc_encode_coding_system[p->outfd] surely points to a
 	 valid memory because p->outfd will be changed once EOF is
 	 sent to the process.  */
-      eassert (p->outfd < FD_SETSIZE);
+      eassert (p->outfd < EMACS_MAX_FD);
       if (NILP (p->encode_coding_system) && p->outfd >= 0
 	  && proc_encode_coding_system[p->outfd])
 	{
@@ -6415,7 +6522,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
   if (p->outfd < 0)
     error ("Output file descriptor of %s is closed", SDATA (p->name));
 
-  eassert (p->outfd < FD_SETSIZE);
+  eassert (p->outfd < EMACS_MAX_FD);
   coding = proc_encode_coding_system[p->outfd];
   Vlast_coding_system_used = CODING_ID_NAME (coding->id);
 
@@ -6528,7 +6635,7 @@ send_process (Lisp_Object proc, const char *buf, ptrdiff_t len,
           if (outfd < 0)
             error ("Output file descriptor of %s is closed",
                    SDATA (p->name));
-	  eassert (0 <= outfd && outfd < FD_SETSIZE);
+	  eassert (0 <= outfd && outfd < EMACS_MAX_FD);
 #ifdef DATAGRAM_SOCKETS
 	  if (DATAGRAM_CHAN_P (outfd))
 	    {
@@ -6980,7 +7087,7 @@ DEFUN ("continue-process", Fcontinue_process, Scontinue_process, 0, 2, 0,
       struct Lisp_Process *p;
 
       p = XPROCESS (process);
-      eassert (p->infd < FD_SETSIZE);
+      eassert (p->infd < EMACS_MAX_FD);
       if (EQ (p->command, Qt)
 	  && p->infd >= 0
 	  && (!EQ (p->filter, Qt) || EQ (p->status, Qlisten)))
@@ -7124,7 +7231,7 @@ DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0,
 
 
   outfd = XPROCESS (proc)->outfd;
-  eassert (outfd < FD_SETSIZE);
+  eassert (outfd < EMACS_MAX_FD);
   if (outfd >= 0)
     coding = proc_encode_coding_system[outfd];
 
@@ -7172,13 +7279,13 @@ DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0,
       p->open_fd[WRITE_TO_SUBPROCESS] = new_outfd;
       p->outfd = new_outfd;
 
-      eassert (0 <= new_outfd && new_outfd < FD_SETSIZE);
+      eassert (0 <= new_outfd && new_outfd < EMACS_MAX_FD);
       if (!proc_encode_coding_system[new_outfd])
 	proc_encode_coding_system[new_outfd]
 	  = xmalloc (sizeof (struct coding_system));
       if (old_outfd >= 0)
 	{
-	  eassert (old_outfd < FD_SETSIZE);
+	  eassert (old_outfd < EMACS_MAX_FD);
 	  *proc_encode_coding_system[new_outfd]
 	    = *proc_encode_coding_system[old_outfd];
 	  memset (proc_encode_coding_system[old_outfd], 0,
@@ -7251,7 +7358,7 @@ child_signal_init (void)
   int fds[2];
   if (emacs_pipe (fds) < 0)
     report_file_error ("Creating pipe for child signal", Qnil);
-  if (FD_SETSIZE <= fds[0])
+  if (EMACS_MAX_FD <= fds[0])
     {
       /* Since we need to `pselect' on the read end, it has to fit
 	 into an `fd_set'.  */
@@ -7723,7 +7830,7 @@ DEFUN ("process-filter-multibyte-p", Fprocess_filter_multibyte_p,
   struct Lisp_Process *p = XPROCESS (process);
   if (p->infd < 0)
     return Qnil;
-  eassert (p->infd < FD_SETSIZE);
+  eassert (p->infd < EMACS_MAX_FD);
   struct coding_system *coding = proc_decode_coding_system[p->infd];
   return (CODING_FOR_UNIBYTE (coding) ? Qnil : Qt);
 }
@@ -7757,7 +7864,7 @@ keyboard_bit_set (fd_set *mask)
 {
   int fd;
 
-  eassert (max_desc < FD_SETSIZE);
+  eassert (max_desc < EMACS_MAX_FD);
   for (fd = 0; fd <= max_desc; fd++)
     if (FD_ISSET (fd, mask)
 	&& ((fd_callback_info[fd].flags & (FOR_READ | KEYBOARD_FD))
@@ -8005,7 +8112,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 void
 add_timer_wait_descriptor (int fd)
 {
-  eassert (0 <= fd && fd < FD_SETSIZE);
+  eassert (0 <= fd && fd < EMACS_MAX_FD);
   add_read_fd (fd, timerfd_callback, NULL);
   fd_callback_info[fd].flags &= ~KEYBOARD_FD;
 }
@@ -8031,7 +8138,7 @@ remove_slash_colon (Lisp_Object name)
 add_keyboard_wait_descriptor (int desc)
 {
 #ifdef subprocesses /* Actually means "not MSDOS".  */
-  eassert (desc >= 0 && desc < FD_SETSIZE);
+  eassert (desc >= 0 && desc < EMACS_MAX_FD);
   fd_callback_info[desc].flags &= ~PROCESS_FD;
   fd_callback_info[desc].flags |= (FOR_READ | KEYBOARD_FD);
   if (desc > max_desc)
@@ -8045,7 +8152,7 @@ add_keyboard_wait_descriptor (int desc)
 delete_keyboard_wait_descriptor (int desc)
 {
 #ifdef subprocesses
-  eassert (desc >= 0 && desc < FD_SETSIZE);
+  eassert (desc >= 0 && desc < EMACS_MAX_FD);
 
   fd_callback_info[desc].flags &= ~(FOR_READ | KEYBOARD_FD | PROCESS_FD);
 
@@ -8068,7 +8175,7 @@ setup_process_coding_systems (Lisp_Object process)
   if (inch < 0 || outch < 0)
     return;
 
-  eassert (0 <= inch && inch < FD_SETSIZE);
+  eassert (0 <= inch && inch < EMACS_MAX_FD);
   if (!proc_decode_coding_system[inch])
     proc_decode_coding_system[inch] = xmalloc (sizeof (struct coding_system));
   coding_system = p->decode_coding_system;
@@ -8080,7 +8187,7 @@ setup_process_coding_systems (Lisp_Object process)
     }
   setup_coding_system (coding_system, proc_decode_coding_system[inch]);
 
-  eassert (0 <= outch && outch < FD_SETSIZE);
+  eassert (0 <= outch && outch < EMACS_MAX_FD);
   if (!proc_encode_coding_system[outch])
     proc_encode_coding_system[outch] = xmalloc (sizeof (struct coding_system));
   setup_coding_system (p->encode_coding_system,
@@ -8322,7 +8429,7 @@ catch_child_signal (void)
 restore_nofile_limit (void)
 {
 #ifdef HAVE_SETRLIMIT
-  if (FD_SETSIZE < nofile_limit.rlim_cur)
+  if (EMACS_MAX_FD < nofile_limit.rlim_cur)
     setrlimit (RLIMIT_NOFILE, &nofile_limit);
 #endif
 }
@@ -8383,13 +8490,13 @@ init_process_emacs (int sockfd)
     }
 
 #ifdef HAVE_SETRLIMIT
-  /* Don't allocate more than FD_SETSIZE file descriptors for Emacs itself.  */
+  /* Don't allocate more than EMACS_MAX_FD file descriptors for Emacs itself.  */
   if (getrlimit (RLIMIT_NOFILE, &nofile_limit) != 0)
     nofile_limit.rlim_cur = 0;
-  else if (FD_SETSIZE < nofile_limit.rlim_cur)
+  else if (EMACS_MAX_FD < nofile_limit.rlim_cur)
     {
       struct rlimit rlim = nofile_limit;
-      rlim.rlim_cur = FD_SETSIZE;
+      rlim.rlim_cur = EMACS_MAX_FD;
       if (setrlimit (RLIMIT_NOFILE, &rlim) != 0)
 	nofile_limit.rlim_cur = 0;
     }
@@ -8425,7 +8532,7 @@ init_process_emacs (int sockfd)
 
   Vprocess_alist = Qnil;
   deleted_pid_list = Qnil;
-  for (i = 0; i < FD_SETSIZE; i++)
+  for (i = 0; i < EMACS_MAX_FD; i++)
     {
       chan_process[i] = Qnil;
       proc_buffered_char[i] = -1;
diff --git a/src/sysdep.c b/src/sysdep.c
index 95295e7e67..5988b35927 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -777,7 +777,7 @@ restore_signal_handlers (struct save_signal *saved_handlers)
 }
 \f
 #ifdef USABLE_SIGIO
-static int old_fcntl_flags[FD_SETSIZE];
+static int old_fcntl_flags[EMACS_MAX_FD];
 #endif
 
 void
@@ -1079,7 +1079,7 @@ emacs_set_tty (int fd, struct emacs_tty *settings, bool flushp)
 \f
 
 #ifdef F_SETOWN
-static int old_fcntl_owner[FD_SETSIZE];
+static int old_fcntl_owner[EMACS_MAX_FD];
 #endif /* F_SETOWN */
 
 /* Initialize the terminal mode on all tty devices that are currently
diff --git a/src/syspoll.h b/src/syspoll.h
new file mode 100644
index 0000000000..22f8a47b93
--- /dev/null
+++ b/src/syspoll.h
@@ -0,0 +1,34 @@
+/* syspoll.h - System-dependent definitions for the poll function.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#ifndef SYSPOLL_H
+#define SYSPOLL_H 1
+
+#if !defined (DOS_NT) && !defined (WINDOWSNT)
+#include <sys/poll.h>
+
+extern int fd_sets_to_pollfds (fd_set *, fd_set *, int);
+extern void pollfds_to_fd_sets (fd_set *, fd_set *, int);
+extern int timespec_to_timeout (const struct timespec *);
+extern int emacs_pselect (int, fd_set *, fd_set *,
+			  fd_set *, const struct timespec *,
+			  const sigset_t *);
+extern struct pollfd pollfds[];
+#define pselect emacs_pselect
+#endif
+#endif
diff --git a/src/sysselect.h b/src/sysselect.h
index 45cc22bc4c..5c1175a4ff 100644
--- a/src/sysselect.h
+++ b/src/sysselect.h
@@ -29,6 +29,7 @@ #define SYSSELECT_H 1
    where w32 needs it, but not where sysselect.h is included.  The w32
    definitions in w32.h are incompatible with the below.  */
 #ifndef WINDOWSNT
+#ifndef USE_POLL
 #ifdef FD_SET
 #ifndef FD_SETSIZE
 #define FD_SETSIZE 64
@@ -43,6 +44,35 @@ #define FD_CLR(n, p) (*(p) &= ~(1 << (n)))
 #define FD_ISSET(n, p) (*(p) & (1 << (n)))
 #define FD_ZERO(p) (*(p) = 0)
 #endif /* no FD_SET */
+#define EMACS_MAX_FD FD_SETSIZE
+#else /* no USE_POLL */
+#define EMACS_MAX_FD (10 * FD_SETSIZE)
+#define fd_set emacs_fd_set
+#undef FD_CLR
+#undef FD_ISSET
+#undef FD_SET
+#undef FD_ZERO
+
+typedef struct {
+  EMACS_UINT bits[EMACS_MAX_FD / EMACS_UINT_WIDTH];
+} emacs_fd_set;
+
+/* standard access macros */
+#define FD_SET(n, p) \
+  do { \
+    if ((n) < EMACS_MAX_FD) { \
+      (p)->bits[(n)/EMACS_UINT_WIDTH] |= (1 << (n)%EMACS_UINT_WIDTH); \
+    } \
+  } while (0)
+#define FD_CLR(n, p) \
+  do { \
+    if ((n) < EMACS_MAX_FD) { \
+      (p)->bits[(n)/EMACS_UINT_WIDTH] &= ~(1 << (n)%EMACS_UINT_WIDTH); \
+    } \
+  } while (0)
+#define FD_ISSET(n, p) ((n) < EMACS_MAX_FD ? ((p)->bits[(n)/EMACS_UINT_WIDTH] & (1 << (n)%EMACS_UINT_WIDTH)) : 0)
+#define FD_ZERO(p) memset((p), 0, sizeof(emacs_fd_set))
+#endif /* no USE_POLL */
 #endif /* not WINDOWSNT */
 
 #if !defined (HAVE_SELECT)
@@ -66,21 +96,21 @@ #define pselect sys_select
 INLINE void
 fd_CLR (int fd, fd_set *set)
 {
-  eassume (0 <= fd && fd < FD_SETSIZE);
+  eassume (0 <= fd && fd < EMACS_MAX_FD);
   FD_CLR (fd, set);
 }
 
 INLINE bool
 fd_ISSET (int fd, fd_set *set)
 {
-  eassume (0 <= fd && fd < FD_SETSIZE);
+  eassume (0 <= fd && fd < EMACS_MAX_FD);
   return FD_ISSET (fd, set) != 0;
 }
 
 INLINE void
 fd_SET (int fd, fd_set *set)
 {
-  eassume (0 <= fd && fd < FD_SETSIZE);
+  eassume (0 <= fd && fd < EMACS_MAX_FD);
   FD_SET (fd, set);
 }
 
diff --git a/src/thread.c b/src/thread.c
index 626d14aad0..b5c90e20a3 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -27,6 +27,9 @@ Copyright (C) 2012-2022 Free Software Foundation, Inc.
 #include "syssignal.h"
 #include "pdumper.h"
 #include "keyboard.h"
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
 
 #ifdef HAVE_NS
 #include "nsterm.h"
diff --git a/src/xgselect.c b/src/xgselect.c
index 7252210c68..cc64ae04bc 100644
--- a/src/xgselect.c
+++ b/src/xgselect.c
@@ -21,6 +21,10 @@ Copyright (C) 2009-2022 Free Software Foundation, Inc.
 
 #include "xgselect.h"
 
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
+
 #ifdef HAVE_GLIB
 
 #include <glib.h>
diff --git a/src/xmenu.c b/src/xmenu.c
index 418628d491..31bc3eaeb9 100644
--- a/src/xmenu.c
+++ b/src/xmenu.c
@@ -45,6 +45,9 @@ Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2022 Free Software
 #include "buffer.h"
 #include "coding.h"
 #include "sysselect.h"
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
 #include "pdumper.h"
 
 #ifdef MSDOS
diff --git a/src/xterm.c b/src/xterm.c
index 01832d60c9..3576267626 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -637,6 +637,9 @@ Copyright (C) 1989, 1993-2022 Free Software Foundation, Inc.
 #include "font.h"
 #include "xsettings.h"
 #include "sysselect.h"
+#ifdef USE_POLL
+#include "syspoll.h"
+#endif
 #include "menu.h"
 #include "pdumper.h"
 
-- 
2.35.1.607.gf01e51a7cf


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

end of thread, other threads:[~2022-05-25 15:30 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-05-04 15:22 1024 file descriptors should be enough for anyone Robert Pluim
2022-05-05  0:55 ` Po Lu
2022-05-05  2:51   ` Po Lu
2022-05-05  7:09   ` Robert Pluim
2022-05-06  8:28     ` Robert Pluim
2022-05-06  8:54       ` Po Lu
2022-05-06  9:02         ` Robert Pluim
2022-05-06  9:28           ` Po Lu
2022-05-06  9:31             ` Robert Pluim
2022-05-06 10:41               ` Po Lu
2022-05-06 12:21                 ` Robert Pluim
2022-05-06 13:04                   ` Po Lu
2022-05-06 13:20                     ` Po Lu
2022-05-06 16:41                       ` Robert Pluim
2022-05-05  2:11 ` Surprisingly high use of file descriptors Stefan Monnier
2022-05-05  7:13   ` Robert Pluim
2022-05-05  7:47     ` Eli Zaretskii
2022-05-05  7:51       ` Robert Pluim
2022-05-05  8:20         ` Eli Zaretskii
2022-05-05  8:25           ` Robert Pluim
2022-05-05 11:35 ` 1024 file descriptors should be enough for anyone Lars Ingebrigtsen
2022-05-05 12:30   ` Robert Pluim
2022-05-05 13:12     ` Lars Ingebrigtsen
2022-05-05 13:45       ` Robert Pluim
2022-05-24 23:50         ` Lars Ingebrigtsen
2022-05-25 15:30           ` Robert Pluim

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

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

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