Minor copyedits in Emacs manual in macos.texi
[emacs.git] / src / xgselect.c
blobfedd3127ef3f9590541b1967dc92cb6e4a7e8b06
1 /* Function for handling the GLib event loop.
3 Copyright (C) 2009-2018 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
20 #include <config.h>
22 #include "xgselect.h"
24 #ifdef HAVE_GLIB
26 #include <glib.h>
27 #include <errno.h>
28 #include "lisp.h"
29 #include "blockinput.h"
30 #include "systime.h"
32 /* `xg_select' is a `pselect' replacement. Why do we need a separate function?
33 1. Timeouts. Glib and Gtk rely on timer events. If we did pselect
34 with a greater timeout then the one scheduled by Glib, we would
35 not allow Glib to process its timer events. We want Glib to
36 work smoothly, so we need to reduce our timeout to match Glib.
37 2. Descriptors. Glib may listen to more file descriptors than we do.
38 So we add Glib descriptors to our pselect pool, but we don't change
39 the value returned by the function. The return value matches only
40 the descriptors passed as arguments, making it compatible with
41 plain pselect. */
43 int
44 xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
45 struct timespec *timeout, sigset_t *sigmask)
47 fd_set all_rfds, all_wfds;
48 struct timespec tmo;
49 struct timespec *tmop = timeout;
51 GMainContext *context;
52 bool have_wfds = wfds != NULL;
53 GPollFD gfds_buf[128];
54 GPollFD *gfds = gfds_buf;
55 int gfds_size = ARRAYELTS (gfds_buf);
56 int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
57 bool context_acquired = false;
58 int i, nfds, tmo_in_millisec, must_free = 0;
59 bool need_to_dispatch;
61 context = g_main_context_default ();
62 context_acquired = g_main_context_acquire (context);
63 /* FIXME: If we couldn't acquire the context, we just silently proceed
64 because this function handles more than just glib file descriptors.
65 Note that, as implemented, this failure is completely silent: there is
66 no feedback to the caller. */
68 if (rfds) all_rfds = *rfds;
69 else FD_ZERO (&all_rfds);
70 if (wfds) all_wfds = *wfds;
71 else FD_ZERO (&all_wfds);
73 n_gfds = (context_acquired
74 ? g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
75 gfds, gfds_size)
76 : -1);
78 if (gfds_size < n_gfds)
80 /* Avoid using SAFE_NALLOCA, as that implicitly refers to the
81 current thread. Using xnmalloc avoids thread-switching
82 problems here. */
83 gfds = xnmalloc (n_gfds, sizeof *gfds);
84 must_free = 1;
85 gfds_size = n_gfds;
86 n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
87 gfds, gfds_size);
90 for (i = 0; i < n_gfds; ++i)
92 if (gfds[i].events & G_IO_IN)
94 FD_SET (gfds[i].fd, &all_rfds);
95 if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
97 if (gfds[i].events & G_IO_OUT)
99 FD_SET (gfds[i].fd, &all_wfds);
100 if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
101 have_wfds = true;
105 if (must_free)
106 xfree (gfds);
108 if (n_gfds >= 0 && tmo_in_millisec >= 0)
110 tmo = make_timespec (tmo_in_millisec / 1000,
111 1000 * 1000 * (tmo_in_millisec % 1000));
112 if (!timeout || timespec_cmp (tmo, *timeout) < 0)
113 tmop = &tmo;
116 fds_lim = max_fds + 1;
117 nfds = thread_select (pselect, fds_lim,
118 &all_rfds, have_wfds ? &all_wfds : NULL, efds,
119 tmop, sigmask);
120 if (nfds < 0)
121 retval = nfds;
122 else if (nfds > 0)
124 for (i = 0; i < fds_lim; ++i)
126 if (FD_ISSET (i, &all_rfds))
128 if (rfds && FD_ISSET (i, rfds)) ++retval;
129 else ++our_fds;
131 else if (rfds)
132 FD_CLR (i, rfds);
134 if (have_wfds && FD_ISSET (i, &all_wfds))
136 if (wfds && FD_ISSET (i, wfds)) ++retval;
137 else ++our_fds;
139 else if (wfds)
140 FD_CLR (i, wfds);
142 if (efds && FD_ISSET (i, efds))
143 ++retval;
147 /* If Gtk+ is in use eventually gtk_main_iteration will be called,
148 unless retval is zero. */
149 #ifdef USE_GTK
150 need_to_dispatch = retval == 0;
151 #else
152 need_to_dispatch = true;
153 #endif
154 if (need_to_dispatch && context_acquired)
156 int pselect_errno = errno;
157 /* Prevent g_main_dispatch recursion, that would occur without
158 block_input wrapper, because event handlers call
159 unblock_input. Event loop recursion was causing Bug#15801. */
160 block_input ();
161 while (g_main_context_pending (context))
162 g_main_context_dispatch (context);
163 unblock_input ();
164 errno = pselect_errno;
167 if (context_acquired)
168 g_main_context_release (context);
170 /* To not have to recalculate timeout, return like this. */
171 if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
173 retval = -1;
174 errno = EINTR;
177 return retval;
179 #endif /* HAVE_GLIB */