PageRenderTime 27ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/xgselect.c

https://github.com/jwiegley/emacs-release
C | 164 lines | 110 code | 20 blank | 34 comment | 43 complexity | 6726f8c6ff206ae5e3adc166e1437396 MD5 | raw file
  1. /* Function for handling the GLib event loop.
  2. Copyright (C) 2009-2014 Free Software Foundation, Inc.
  3. This file is part of GNU Emacs.
  4. GNU Emacs is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. GNU Emacs is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
  14. #include <config.h>
  15. #include "xgselect.h"
  16. #ifdef HAVE_GLIB
  17. #include <glib.h>
  18. #include <errno.h>
  19. #include <stdbool.h>
  20. #include <timespec.h>
  21. #include "frame.h"
  22. #include "blockinput.h"
  23. /* `xg_select' is a `pselect' replacement. Why do we need a separate function?
  24. 1. Timeouts. Glib and Gtk rely on timer events. If we did pselect
  25. with a greater timeout then the one scheduled by Glib, we would
  26. not allow Glib to process its timer events. We want Glib to
  27. work smoothly, so we need to reduce our timeout to match Glib.
  28. 2. Descriptors. Glib may listen to more file descriptors than we do.
  29. So we add Glib descriptors to our pselect pool, but we don't change
  30. the value returned by the function. The return value matches only
  31. the descriptors passed as arguments, making it compatible with
  32. plain pselect. */
  33. int
  34. xg_select (int fds_lim, fd_set *rfds, fd_set *wfds, fd_set *efds,
  35. struct timespec const *timeout, sigset_t const *sigmask)
  36. {
  37. fd_set all_rfds, all_wfds;
  38. struct timespec tmo;
  39. struct timespec const *tmop = timeout;
  40. GMainContext *context;
  41. int have_wfds = wfds != NULL;
  42. GPollFD gfds_buf[128];
  43. GPollFD *gfds = gfds_buf;
  44. int gfds_size = sizeof gfds_buf / sizeof *gfds_buf;
  45. int n_gfds, retval = 0, our_fds = 0, max_fds = fds_lim - 1;
  46. int i, nfds, tmo_in_millisec;
  47. bool need_to_dispatch;
  48. USE_SAFE_ALLOCA;
  49. context = g_main_context_default ();
  50. if (rfds) all_rfds = *rfds;
  51. else FD_ZERO (&all_rfds);
  52. if (wfds) all_wfds = *wfds;
  53. else FD_ZERO (&all_wfds);
  54. n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
  55. gfds, gfds_size);
  56. if (gfds_size < n_gfds)
  57. {
  58. SAFE_NALLOCA (gfds, sizeof *gfds, n_gfds);
  59. gfds_size = n_gfds;
  60. n_gfds = g_main_context_query (context, G_PRIORITY_LOW, &tmo_in_millisec,
  61. gfds, gfds_size);
  62. }
  63. for (i = 0; i < n_gfds; ++i)
  64. {
  65. if (gfds[i].events & G_IO_IN)
  66. {
  67. FD_SET (gfds[i].fd, &all_rfds);
  68. if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
  69. }
  70. if (gfds[i].events & G_IO_OUT)
  71. {
  72. FD_SET (gfds[i].fd, &all_wfds);
  73. if (gfds[i].fd > max_fds) max_fds = gfds[i].fd;
  74. have_wfds = 1;
  75. }
  76. }
  77. SAFE_FREE ();
  78. if (tmo_in_millisec >= 0)
  79. {
  80. tmo = make_timespec (tmo_in_millisec / 1000,
  81. 1000 * 1000 * (tmo_in_millisec % 1000));
  82. if (!timeout || timespec_cmp (tmo, *timeout) < 0)
  83. tmop = &tmo;
  84. }
  85. fds_lim = max_fds + 1;
  86. nfds = pselect (fds_lim, &all_rfds, have_wfds ? &all_wfds : NULL,
  87. efds, tmop, sigmask);
  88. if (nfds < 0)
  89. retval = nfds;
  90. else if (nfds > 0)
  91. {
  92. for (i = 0; i < fds_lim; ++i)
  93. {
  94. if (FD_ISSET (i, &all_rfds))
  95. {
  96. if (rfds && FD_ISSET (i, rfds)) ++retval;
  97. else ++our_fds;
  98. }
  99. else if (rfds)
  100. FD_CLR (i, rfds);
  101. if (have_wfds && FD_ISSET (i, &all_wfds))
  102. {
  103. if (wfds && FD_ISSET (i, wfds)) ++retval;
  104. else ++our_fds;
  105. }
  106. else if (wfds)
  107. FD_CLR (i, wfds);
  108. if (efds && FD_ISSET (i, efds))
  109. ++retval;
  110. }
  111. }
  112. /* If Gtk+ is in use eventually gtk_main_iteration will be called,
  113. unless retval is zero. */
  114. #ifdef USE_GTK
  115. need_to_dispatch = retval == 0;
  116. #else
  117. need_to_dispatch = true;
  118. #endif
  119. if (need_to_dispatch)
  120. {
  121. int pselect_errno = errno;
  122. /* Prevent g_main_dispatch recursion, that would occur without
  123. block_input wrapper, because event handlers call
  124. unblock_input. Event loop recursion was causing Bug#15801. */
  125. block_input ();
  126. while (g_main_context_pending (context))
  127. g_main_context_dispatch (context);
  128. unblock_input ();
  129. errno = pselect_errno;
  130. }
  131. /* To not have to recalculate timeout, return like this. */
  132. if ((our_fds > 0 || (nfds == 0 && tmop == &tmo)) && (retval == 0))
  133. {
  134. retval = -1;
  135. errno = EINTR;
  136. }
  137. return retval;
  138. }
  139. #endif /* HAVE_GLIB */