Coverage Report

Created: 2025-07-01 06:23

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