unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
blob 14a8c735415e6ae2aca32f0bcc63c8803c2a6dc0 7435 bytes (raw)
name: src/xgselect.c 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
 
/* Function for handling the GLib event loop.

Copyright (C) 2009-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/>.  */

#include <config.h>

#include "xgselect.h"

#ifdef HAVE_GLIB

#include <glib.h>
#include <errno.h>
#include "lisp.h"
#include "blockinput.h"
#include "systime.h"
#include "process.h"

static ptrdiff_t threads_holding_glib_lock;
static GMainContext *glib_main_context;

void
release_select_lock (void)
{
#if GNUC_PREREQ (4, 7, 0)
  if (__atomic_sub_fetch (&threads_holding_glib_lock, 1, __ATOMIC_ACQ_REL) == 0)
    g_main_context_release (glib_main_context);
#else
  if (--threads_holding_glib_lock == 0)
    g_main_context_release (glib_main_context);
#endif
}

static void
acquire_select_lock (GMainContext *context)
{
#if GNUC_PREREQ (4, 7, 0)
  if (__atomic_fetch_add (&threads_holding_glib_lock, 1, __ATOMIC_ACQ_REL) == 0)
    {
      glib_main_context = context;
      while (!g_main_context_acquire (context))
	{
	  /* Spin. */
	}
    }
#else
  if (threads_holding_glib_lock++ == 0)
    {
      glib_main_context = context;
      while (!g_main_context_acquire (context))
	{
	  /* Spin. */
	}
    }
#endif
}

/* `xg_select' is a `pselect' replacement.  Why do we need a separate function?
   1. Timeouts.  Glib and Gtk rely on timer events.  If we did pselect
      with a greater timeout then the one scheduled by Glib, we would
      not allow Glib to process its timer events.  We want Glib to
      work smoothly, so we need to reduce our timeout to match Glib.
   2. Descriptors.  Glib may listen to more file descriptors than we do.
      So we add Glib descriptors to our pselect pool, but we don't change
      the value returned by the function.  The return value  matches only
      the descriptors passed as arguments, making it compatible with
      plain pselect.  */

int
xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
	   struct timespec *timeout, sigset_t *sigmask)
{
  fd_set all_rfds, all_wfds;
  struct timespec tmo;
  struct timespec *tmop = timeout;

  GMainContext *context;
  bool have_wfds = wfds != NULL;
  GPollFD gfds_buf[128];
  GPollFD *gfds = gfds_buf;
  int gfds_size = ARRAYELTS (gfds_buf);
  int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
  int i, nfds, tmo_in_millisec, must_free = 0;
  bool need_to_dispatch;
#ifdef HAVE_PGTK
  bool already_has_events = false;
#endif

  context = g_main_context_default ();
  acquire_select_lock (context);

#ifdef HAVE_PGTK
  {
    int errno_back = errno;
    already_has_events = g_main_context_pending (context);
    errno = errno_back;
  }
#endif

  if (rfds) all_rfds = *rfds;
  else FD_ZERO (&all_rfds);
  if (wfds) all_wfds = *wfds;
  else FD_ZERO (&all_wfds);

  n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				 gfds, gfds_size);

  if (gfds_size < n_gfds)
    {
      /* Avoid using SAFE_NALLOCA, as that implicitly refers to the
	 current thread.  Using xnmalloc avoids thread-switching
	 problems here.  */
      gfds = xnmalloc (n_gfds, sizeof *gfds);
      must_free = 1;
      gfds_size = n_gfds;
      n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
				     gfds, gfds_size);
    }

  for (i = 0; i < n_gfds; ++i)
    {
      if (gfds[i].events & G_IO_IN)
        {
          FD_SET (gfds[i].fd, &all_rfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
        }
      if (gfds[i].events & G_IO_OUT)
        {
          FD_SET (gfds[i].fd, &all_wfds);
          if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
          have_wfds = true;
        }
    }

  if (must_free)
    xfree (gfds);

  if (n_gfds >= 0 && tmo_in_millisec >= 0)
    {
      tmo = make_timespec (tmo_in_millisec / 1000,
			   1000 * 1000 * (tmo_in_millisec % 1000));
      if (!timeout || timespec_cmp (tmo, *timeout) < 0)
	tmop = &tmo;
    }

#ifndef HAVE_PGTK
  fds_lim = max_fds + 1;
  nfds = thread_select (pselect, fds_lim,
			&all_rfds, have_wfds ? &all_wfds : NULL, efds,
			tmop, sigmask);
#else
  /*
    On PGTK, when you type a key, the key press event are received,
    and one more key press event seems to be received internally.
    The second event is not via a socket, so there are weird status:
      - socket read buffer is empty
      - a key press event is pending
    In that case, we should not sleep, and dispatch the event immediately.
    Bug#52761
   */
  if (!already_has_events)
    {
      fds_lim = max_fds + 1;
      nfds = thread_select (pselect, fds_lim,
			    &all_rfds, have_wfds ? &all_wfds : NULL, efds,
			    tmop, sigmask);
    }
  else
    {
      /* Emulate return values */
      nfds = 1;
      FD_ZERO(&all_rfds);
      if (have_wfds)
	FD_ZERO(&all_wfds);
      if (efds)
	FD_ZERO(efds);
      our_fds++;
    }
#endif

  if (nfds < 0)
    retval = nfds;
  else if (nfds > 0)
    {
      for (i = 0; i < fds_lim; ++i)
        {
          if (FD_ISSET (i, &all_rfds))
            {
              if (rfds && FD_ISSET (i, rfds)) ++retval;
              else ++our_fds;
            }
          else if (rfds)
            FD_CLR (i, rfds);

          if (have_wfds && FD_ISSET (i, &all_wfds))
            {
              if (wfds && FD_ISSET (i, wfds)) ++retval;
              else ++our_fds;
            }
          else if (wfds)
            FD_CLR (i, wfds);

          if (efds && FD_ISSET (i, efds))
            ++retval;
        }
    }

  /* If Gtk+ is in use eventually gtk_main_iteration will be called,
     unless retval is zero.  */
#ifdef USE_GTK
  need_to_dispatch = retval == 0;
#else
  need_to_dispatch = true;
#endif

  /* xwidgets make heavy use of GLib subprocesses, which add their own
     SIGCHLD handler at arbitrary locations.  That doesn't play well
     with Emacs's own handler, so once GLib does its thing with its
     subprocesses we restore our own SIGCHLD handler (which chains the
     GLib handler) here.

     There is an obvious race condition, but we can't really do
     anything about that, except hope a SIGCHLD arrives soon to clear
     up the situation.  */

#ifdef HAVE_XWIDGETS
  catch_child_signal ();
#endif

  if (need_to_dispatch)
    {
      acquire_select_lock (context);

      int pselect_errno = errno;
      /* Prevent g_main_dispatch recursion, that would occur without
         block_input wrapper, because event handlers call
         unblock_input.  Event loop recursion was causing Bug#15801.  */
      block_input ();
      while (g_main_context_pending (context))
        g_main_context_dispatch (context);
      unblock_input ();
      errno = pselect_errno;
      release_select_lock ();
    }

  /* To not have to recalculate timeout, return like this.  */
  if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
    {
      retval = -1;
      errno = EINTR;
    }

  return retval;
}
#endif /* HAVE_GLIB */

debug log:

solving 14a8c73541 ...
found 14a8c73541 in https://yhetil.org/emacs-bugs/20220106.235617.1013151202943040802.masm@luna.pink.masm11.me/
found 674c259db7 in https://git.savannah.gnu.org/cgit/emacs.git
preparing index
index prepared:
100644 674c259db762e13e3beec32cefbb47c77488c0a9	src/xgselect.c

applying [1/1] https://yhetil.org/emacs-bugs/20220106.235617.1013151202943040802.masm@luna.pink.masm11.me/
diff --git a/src/xgselect.c b/src/xgselect.c
index 674c259db7..14a8c73541 100644

Checking patch src/xgselect.c...
Applied patch src/xgselect.c cleanly.

index at:
100644 14a8c735415e6ae2aca32f0bcc63c8803c2a6dc0	src/xgselect.c

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).