LCOV - code coverage report
Current view: top level - glib/glib - gwakeup.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 27 39 69.2 %
Date: 2024-04-16 05:15:53 Functions: 5 5 100.0 %
Branches: 6 32 18.8 %

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

Generated by: LCOV version 1.14