Coverage Report

Created: 2025-07-01 07:09

/src/glib/glib/gwakeup.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright © 2011 Canonical Limited
3
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6
 * License as published by the Free Software Foundation; either
7
 * version 2.1 of the License, or (at your option) any later version.
8
 *
9
 * This library is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16
 *
17
 * Author: Ryan Lortie <desrt@desrt.ca>
18
 */
19
20
#include "config.h"
21
22
23
/* gwakeup.c is special -- GIO and some test cases include it.  As such,
24
 * it cannot include other glib headers without triggering the single
25
 * includes warnings.  We have to manually include its dependencies here
26
 * (and at all other use sites).
27
 */
28
#ifdef GLIB_COMPILATION
29
#include "gtypes.h"
30
#include "gpoll.h"
31
#else
32
#include <glib.h>
33
#endif
34
35
#include "gwakeup.h"
36
37
/*< private >
38
 * SECTION:gwakeup
39
 * @title: GWakeup
40
 * @short_description: portable cross-thread event signal mechanism
41
 *
42
 * #GWakeup is a simple and portable way of signaling events between
43
 * different threads in a way that integrates nicely with g_poll().
44
 * GLib uses it internally for cross-thread signalling in the
45
 * implementation of #GMainContext and #GCancellable.
46
 *
47
 * You first create a #GWakeup with g_wakeup_new() and initialise a
48
 * #GPollFD from it using g_wakeup_get_pollfd().  Polling on the created
49
 * #GPollFD will block until g_wakeup_signal() is called, at which point
50
 * it will immediately return.  Future attempts to poll will continue to
51
 * return until g_wakeup_acknowledge() is called.  g_wakeup_free() is
52
 * used to free a #GWakeup.
53
 *
54
 * On sufficiently modern Linux, this is implemented using eventfd.  On
55
 * Windows it is implemented using an event handle.  On other systems it
56
 * is implemented with a pair of pipes.
57
 *
58
 * Since: 2.30
59
 **/
60
#ifdef _WIN32
61
62
#include <windows.h>
63
64
#ifdef GLIB_COMPILATION
65
#include "gmessages.h"
66
#include "giochannel.h"
67
#include "gwin32.h"
68
#endif
69
70
GWakeup *
71
g_wakeup_new (void)
72
{
73
  HANDLE wakeup;
74
75
  wakeup = CreateEvent (NULL, TRUE, FALSE, NULL);
76
77
  if (wakeup == NULL)
78
    g_error ("Cannot create event for GWakeup: %s",
79
             g_win32_error_message (GetLastError ()));
80
81
  return (GWakeup *) wakeup;
82
}
83
84
void
85
g_wakeup_get_pollfd (GWakeup *wakeup,
86
                     GPollFD *poll_fd)
87
{
88
  poll_fd->fd = (gintptr) wakeup;
89
  poll_fd->events = G_IO_IN;
90
}
91
92
void
93
g_wakeup_acknowledge (GWakeup *wakeup)
94
{
95
  ResetEvent ((HANDLE) wakeup);
96
}
97
98
void
99
g_wakeup_signal (GWakeup *wakeup)
100
{
101
  SetEvent ((HANDLE) wakeup);
102
}
103
104
void
105
g_wakeup_free (GWakeup *wakeup)
106
{
107
  CloseHandle ((HANDLE) wakeup);
108
}
109
110
#else
111
112
#include "glib-unix.h"
113
#include <fcntl.h>
114
115
#if defined (HAVE_EVENTFD)
116
#include <sys/eventfd.h>
117
#endif
118
119
struct _GWakeup
120
{
121
  gint fds[2];
122
};
123
124
/**
125
 * g_wakeup_new:
126
 *
127
 * Creates a new #GWakeup.
128
 *
129
 * You should use g_wakeup_free() to free it when you are done.
130
 *
131
 * Returns: a new #GWakeup
132
 *
133
 * Since: 2.30
134
 **/
135
GWakeup *
136
g_wakeup_new (void)
137
0
{
138
0
  GError *error = NULL;
139
0
  GWakeup *wakeup;
140
141
0
  wakeup = g_slice_new (GWakeup);
142
143
  /* try eventfd first, if we think we can */
144
0
#if defined (HAVE_EVENTFD)
145
0
#ifndef TEST_EVENTFD_FALLBACK
146
0
  wakeup->fds[0] = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK);
147
#else
148
  wakeup->fds[0] = -1;
149
#endif
150
151
0
  if (wakeup->fds[0] != -1)
152
0
    {
153
0
      wakeup->fds[1] = -1;
154
0
      return wakeup;
155
0
    }
156
157
  /* for any failure, try a pipe instead */
158
0
#endif
159
160
0
  if (!g_unix_open_pipe (wakeup->fds, FD_CLOEXEC, &error))
161
0
    g_error ("Creating pipes for GWakeup: %s", error->message);
162
163
0
  if (!g_unix_set_fd_nonblocking (wakeup->fds[0], TRUE, &error) ||
164
0
      !g_unix_set_fd_nonblocking (wakeup->fds[1], TRUE, &error))
165
0
    g_error ("Set pipes non-blocking for GWakeup: %s", error->message);
166
167
0
  return wakeup;
168
0
}
169
170
/**
171
 * g_wakeup_get_pollfd:
172
 * @wakeup: a #GWakeup
173
 * @poll_fd: a #GPollFD
174
 *
175
 * Prepares a @poll_fd such that polling on it will succeed when
176
 * g_wakeup_signal() has been called on @wakeup.
177
 *
178
 * @poll_fd is valid until @wakeup is freed.
179
 *
180
 * Since: 2.30
181
 **/
182
void
183
g_wakeup_get_pollfd (GWakeup *wakeup,
184
                     GPollFD *poll_fd)
185
0
{
186
0
  poll_fd->fd = wakeup->fds[0];
187
0
  poll_fd->events = G_IO_IN;
188
0
}
189
190
/**
191
 * g_wakeup_acknowledge:
192
 * @wakeup: a #GWakeup
193
 *
194
 * Acknowledges receipt of a wakeup signal on @wakeup.
195
 *
196
 * You must call this after @wakeup polls as ready.  If not, it will
197
 * continue to poll as ready until you do so.
198
 *
199
 * If you call this function and @wakeup is not signaled, nothing
200
 * happens.
201
 *
202
 * Since: 2.30
203
 **/
204
void
205
g_wakeup_acknowledge (GWakeup *wakeup)
206
0
{
207
0
  char buffer[16];
208
209
  /* read until it is empty */
210
0
  while (read (wakeup->fds[0], buffer, sizeof buffer) == sizeof buffer);
211
0
}
212
213
/**
214
 * g_wakeup_signal:
215
 * @wakeup: a #GWakeup
216
 *
217
 * Signals @wakeup.
218
 *
219
 * Any future (or present) polling on the #GPollFD returned by
220
 * g_wakeup_get_pollfd() will immediately succeed until such a time as
221
 * g_wakeup_acknowledge() is called.
222
 *
223
 * This function is safe to call from a UNIX signal handler.
224
 *
225
 * Since: 2.30
226
 **/
227
void
228
g_wakeup_signal (GWakeup *wakeup)
229
0
{
230
0
  int res;
231
232
0
  if (wakeup->fds[1] == -1)
233
0
    {
234
0
      guint64 one = 1;
235
236
      /* eventfd() case. It requires a 64-bit counter increment value to be
237
       * written. */
238
0
      do
239
0
        res = write (wakeup->fds[0], &one, sizeof one);
240
0
      while (G_UNLIKELY (res == -1 && errno == EINTR));
241
0
    }
242
0
  else
243
0
    {
244
0
      guint8 one = 1;
245
246
      /* Non-eventfd() case. Only a single byte needs to be written, and it can
247
       * have an arbitrary value. */
248
0
      do
249
0
        res = write (wakeup->fds[1], &one, sizeof one);
250
0
      while (G_UNLIKELY (res == -1 && errno == EINTR));
251
0
    }
252
0
}
253
254
/**
255
 * g_wakeup_free:
256
 * @wakeup: a #GWakeup
257
 *
258
 * Frees @wakeup.
259
 *
260
 * You must not currently be polling on the #GPollFD returned by
261
 * g_wakeup_get_pollfd(), or the result is undefined.
262
 **/
263
void
264
g_wakeup_free (GWakeup *wakeup)
265
0
{
266
0
  close (wakeup->fds[0]);
267
268
0
  if (wakeup->fds[1] != -1)
269
0
    close (wakeup->fds[1]);
270
271
0
  g_slice_free (GWakeup, wakeup);
272
0
}
273
274
#endif /* !_WIN32 */