LCOV - code coverage report
Current view: top level - source3/lib - tevent_glib_glue.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 200 324 61.7 %
Date: 2021-09-23 10:06:22 Functions: 16 21 76.2 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Integration of a glib g_main_context into a tevent_context
       4             :    Copyright (C) Stefan Metzmacher 2016
       5             :    Copyright (C) Ralph Boehme 2016
       6             : 
       7             :      ** NOTE! The following LGPL license applies to the tevent
       8             :      ** library. This does NOT imply that all of Samba is released
       9             :      ** under the LGPL
      10             : 
      11             :    This library is free software; you can redistribute it and/or
      12             :    modify it under the terms of the GNU Lesser General Public
      13             :    License as published by the Free Software Foundation; either
      14             :    version 3 of the License, or (at your option) any later version.
      15             : 
      16             :    This library is distributed in the hope that it will be useful,
      17             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      19             :    Lesser General Public License for more details.
      20             : 
      21             :    You should have received a copy of the GNU Lesser General Public
      22             :    License along with this library; if not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : #include "replace.h"
      26             : #include "system/filesys.h"
      27             : #include "lib/util/debug.h"
      28             : #include "lib/util/select.h"
      29             : #include <tevent.h>
      30             : 
      31             : #undef DBGC_CLASS
      32             : #define DBGC_CLASS DBGC_TEVENT
      33             : 
      34             : #ifdef HAVE_GLIB
      35             : #include <glib.h>
      36             : #include "tevent_glib_glue.h"
      37             : 
      38             : struct fd_map {
      39             :         struct tevent_glib_glue *glue;
      40             :         int fd;
      41             :         struct tevent_fd *fd_event;
      42             : };
      43             : 
      44             : struct tevent_glib_glue {
      45             :         /*
      46             :          * The tevent context we're feeding.
      47             :          */
      48             :         struct tevent_context *ev;
      49             : 
      50             :         /*
      51             :          * The glib gmain context we're polling and whether we're currently
      52             :          * owning it by virtue of g_main_context_acquire().
      53             :          */
      54             :         GMainContext *gmain_ctx;
      55             :         bool gmain_owner;
      56             : 
      57             :         /*
      58             :          * Set by samba_tevent_glib_glue_quit().
      59             :          */
      60             :         bool quit;
      61             : 
      62             :         /*
      63             :          * tevent trace callback and data we got from tevent_get_trace_callback()
      64             :          * before installing our own trace callback.
      65             :          */
      66             :         tevent_trace_callback_t prev_tevent_trace_cb;
      67             :         void *prev_tevent_trace_data;
      68             : 
      69             :         /*
      70             :          * Don't call tevent_glib_prepare() in the tevent tracepoint handler if
      71             :          * explicity told so. This is an optimisation for the case that glib
      72             :          * event sources are created from glib event callbacks.
      73             :          */
      74             :         bool skip_glib_refresh;
      75             : 
      76             :         /*
      77             :          * Used when acquiring the glib gmain context failed.
      78             :          */
      79             :         struct tevent_timer *acquire_retry_timer;
      80             : 
      81             :         /*
      82             :          * glib gmain context timeout and priority for the current event look
      83             :          * iteration. gtimeout is translated to a tevent timer event, unless it
      84             :          * is 0 which signals some event source is pending. In that case we
      85             :          * dispatch an immediate event. gpriority is ignored by us, just passed
      86             :          * to the glib relevant functions.
      87             :          */
      88             :         gint gtimeout;
      89             :         gint gpriority;
      90             :         struct tevent_timer *timer;
      91             :         struct tevent_immediate *im;
      92             :         bool scheduled_im;
      93             : 
      94             :         /*
      95             :          * glib gmain context fds returned from g_main_context_query(). These
      96             :          * get translated to tevent fd events.
      97             :          */
      98             :         GPollFD *gpollfds;
      99             :         gint num_gpollfds;
     100             : 
     101             :         /*
     102             :          * A copy of gpollfds and num_gpollfds from the previous event loop
     103             :          * iteration, used to detect changes in the set of fds.
     104             :          */
     105             :         GPollFD *prev_gpollfds;
     106             :         gint num_prev_gpollfds;
     107             : 
     108             :         /*
     109             :          * An array of pointers to fd_map's. The fd_map'd contain the tevent
     110             :          * event fd as well as a pointer to the corresponding glib GPollFD.
     111             :          */
     112             :         struct fd_map **fd_map;
     113             :         size_t num_maps;
     114             : };
     115             : 
     116             : static bool tevent_glib_prepare(struct tevent_glib_glue *glue);
     117             : static bool tevent_glib_process(struct tevent_glib_glue *glue);
     118             : static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue);
     119             : static void tevent_glib_fd_handler(struct tevent_context *ev,
     120             :                                    struct tevent_fd *fde,
     121             :                                    uint16_t flags,
     122             :                                    void *private_data);
     123             : 
     124             : typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2);
     125             : typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue,
     126             :                               const GPollFD *new,
     127             :                               const GPollFD *old);
     128             : typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue,
     129             :                             const GPollFD *fd);
     130             : typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue,
     131             :                                 const GPollFD *fd);
     132             : 
     133             : /**
     134             :  * Compare two sorted GPollFD arrays
     135             :  *
     136             :  * For every element that exists in gfds and prev_gfds found_fn() is called.
     137             :  * For every element in gfds but not in prev_gfds, new_fn() is called.
     138             :  * For every element in prev_gfds but not in gfds removed_fn() is called.
     139             :  **/
     140      393363 : static bool cmp_gfds(struct tevent_glib_glue *glue,
     141             :                      GPollFD *gfds,
     142             :                      GPollFD *prev_gfds,
     143             :                      size_t num_gfds,
     144             :                      size_t num_prev_gfds,
     145             :                      gfds_cmp_cb cmp_cb,
     146             :                      gfds_found_cb found_cb,
     147             :                      gfds_new_cb new_cb,
     148             :                      gfds_removed_cb removed_cb)
     149             : {
     150             :         bool ok;
     151      393363 :         size_t i = 0, j = 0;
     152             :         int cmp;
     153             : 
     154     1966648 :         while (i < num_gfds && j < num_prev_gfds) {
     155     1179922 :                 cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
     156     1179922 :                 if (cmp == 0) {
     157     1179921 :                         ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
     158     1179921 :                         if (!ok) {
     159           0 :                                 return false;
     160             :                         }
     161     1179921 :                         i++;
     162     1179921 :                         j++;
     163           1 :                 } else if (cmp < 0) {
     164           1 :                         ok = new_cb(glue, &gfds[i]);
     165           1 :                         if (!ok) {
     166           0 :                                 return false;
     167             :                         }
     168           1 :                         i++;
     169             :                 } else {
     170           0 :                         ok = removed_cb(glue, &prev_gfds[j]);
     171           0 :                         if (!ok) {
     172           0 :                                 return false;
     173             :                         }
     174           0 :                         j++;
     175             :                 }
     176             :         }
     177             : 
     178      393368 :         while (i < num_gfds) {
     179           5 :                 ok = new_cb(glue, &gfds[i++]);
     180           5 :                 if (!ok) {
     181           0 :                         return false;
     182             :                 }
     183             :         }
     184             : 
     185      393364 :         while (j < num_prev_gfds) {
     186           1 :                 ok = removed_cb(glue, &prev_gfds[j++]);
     187           1 :                 if (!ok) {
     188           0 :                         return false;
     189             :                 }
     190             :         }
     191             : 
     192           0 :         return true;
     193             : }
     194             : 
     195     1966486 : static int glib_fd_cmp_func(const void *p1, const void *p2)
     196             : {
     197     1966486 :         const GPollFD *lhs = p1;
     198     1966486 :         const GPollFD *rhs = p2;
     199             : 
     200     1966486 :         if (lhs->fd < rhs->fd) {
     201           0 :                 return -1;
     202     1179921 :         } else if (lhs->fd > rhs->fd) {
     203           0 :                 return 1;
     204             :         }
     205             : 
     206     1179921 :         return 0;
     207             : }
     208             : 
     209             : /*
     210             :  * We already have a tevent fd event for the glib GPollFD, but we may have to
     211             :  * update flags.
     212             :  */
     213     1179921 : static bool match_gfd_cb(struct tevent_glib_glue *glue,
     214             :                          const GPollFD *new_gfd,
     215             :                          const GPollFD *old_gfd)
     216             : {
     217             :         size_t i;
     218     1179921 :         struct fd_map *fd_map = NULL;
     219     1179921 :         struct tevent_fd *fd_event = NULL;
     220             : 
     221     1179921 :         if (new_gfd->events == old_gfd->events) {
     222           0 :                 return true;
     223             :         }
     224             : 
     225           0 :         for (i = 0; i < glue->num_maps; i++) {
     226           0 :                 if (glue->fd_map[i]->fd == new_gfd->fd) {
     227           0 :                         break;
     228             :                 }
     229             :         }
     230             : 
     231           0 :         if (i == glue->num_maps) {
     232           0 :                 DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
     233           0 :                 return false;
     234             :         }
     235             : 
     236           0 :         fd_map = glue->fd_map[i];
     237           0 :         if (fd_map == NULL) {
     238           0 :                 DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd);
     239           0 :                 return false;
     240             :         }
     241             : 
     242           0 :         fd_event = fd_map->fd_event;
     243           0 :         if (fd_event == NULL) {
     244           0 :                 DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd);
     245           0 :                 return false;
     246             :         }
     247             : 
     248           0 :         tevent_fd_set_flags(fd_event, 0);
     249             : 
     250           0 :         if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
     251           0 :                 TEVENT_FD_READABLE(fd_event);
     252             :         }
     253           0 :         if (new_gfd->events & G_IO_OUT) {
     254           0 :                 TEVENT_FD_WRITEABLE(fd_event);
     255             :         }
     256             : 
     257           0 :         return true;
     258             : }
     259             : 
     260           6 : static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
     261             : {
     262           6 :         struct tevent_fd *fd_event = NULL;
     263           6 :         struct fd_map *fd_map = NULL;
     264           6 :         uint16_t events = 0;
     265             :         bool revent;
     266             :         bool wevent;
     267             : 
     268           6 :         revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR));
     269           6 :         wevent = (gfd->events & G_IO_OUT);
     270           6 :         if (revent) {
     271           5 :                 events |= TEVENT_FD_READ;
     272             :         }
     273           6 :         if (wevent) {
     274           1 :                 events |= TEVENT_FD_WRITE;
     275             :         }
     276             : 
     277           6 :         glue->fd_map = talloc_realloc(glue,
     278             :                                       glue->fd_map,
     279             :                                       struct fd_map *,
     280             :                                       glue->num_maps + 1);
     281           6 :         if (glue->fd_map == NULL) {
     282           0 :                 DBG_ERR("talloc_realloc failed\n");
     283           0 :                 return false;
     284             :         }
     285           6 :         fd_map = talloc_zero(glue->fd_map, struct fd_map);
     286           6 :         if (fd_map == NULL) {
     287           0 :                 DBG_ERR("talloc_realloc failed\n");
     288           0 :                 return false;
     289             :         }
     290           6 :         glue->fd_map[glue->num_maps] = fd_map;
     291           6 :         glue->num_maps++;
     292             : 
     293           6 :         fd_event = tevent_add_fd(glue->ev,
     294             :                                  glue->fd_map,
     295             :                                  gfd->fd,
     296             :                                  events,
     297             :                                  tevent_glib_fd_handler,
     298             :                                  fd_map);
     299           6 :         if (fd_event == NULL) {
     300           0 :                 DBG_ERR("tevent_add_fd failed\n");
     301           0 :                 return false;
     302             :         }
     303             : 
     304           6 :         *fd_map = (struct fd_map) {
     305             :                 .glue = glue,
     306           6 :                 .fd = gfd->fd,
     307             :                 .fd_event = fd_event,
     308             :         };
     309             : 
     310           6 :         DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
     311             : 
     312           0 :         return true;
     313             : }
     314             : 
     315           1 : static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
     316             : {
     317             :         size_t i;
     318             : 
     319           2 :         for (i = 0; i < glue->num_maps; i++) {
     320           2 :                 if (glue->fd_map[i]->fd == gfd->fd) {
     321           0 :                         break;
     322             :                 }
     323             :         }
     324             : 
     325           1 :         if (i == glue->num_maps) {
     326           0 :                 DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
     327           0 :                 return false;
     328             :         }
     329             : 
     330           1 :         TALLOC_FREE(glue->fd_map[i]->fd_event);
     331           1 :         TALLOC_FREE(glue->fd_map[i]);
     332             : 
     333           1 :         if (i + 1 < glue->num_maps) {
     334           3 :                 memmove(&glue->fd_map[i],
     335           2 :                         &glue->fd_map[i+1],
     336           1 :                         (glue->num_maps - (i + 1)) * sizeof(struct fd_map *));
     337             :         }
     338             : 
     339           1 :         glue->fd_map = talloc_realloc(glue,
     340             :                                       glue->fd_map,
     341             :                                       struct fd_map *,
     342             :                                       glue->num_maps - 1);
     343           1 :         if (glue->num_maps > 0 && glue->fd_map == NULL) {
     344           0 :                 DBG_ERR("talloc_realloc failed\n");
     345           0 :                 return false;
     346             :         }
     347           1 :         glue->num_maps--;
     348             : 
     349           1 :         return true;
     350             : }
     351             : 
     352           0 : static short gpoll_to_poll_event(gushort gevent)
     353             : {
     354      393342 :         short pevent = 0;
     355             : 
     356      393342 :         if (gevent & G_IO_IN) {
     357      262269 :                 pevent |= POLLIN;
     358             :         }
     359      393342 :         if (gevent & G_IO_OUT) {
     360      131073 :                 pevent |= POLLOUT;
     361             :         }
     362      393342 :         if (gevent & G_IO_HUP) {
     363           0 :                 pevent |= POLLHUP;
     364             :         }
     365      393342 :         if (gevent & G_IO_ERR) {
     366           0 :                 pevent |= POLLERR;
     367             :         }
     368             : 
     369           0 :         return pevent;
     370             : }
     371             : 
     372           0 : static gushort poll_to_gpoll_event(short pevent)
     373             : {
     374      393342 :         gushort gevent = 0;
     375             : 
     376      393342 :         if (pevent & POLLIN) {
     377      262269 :                 gevent |= G_IO_IN;
     378             :         }
     379      393342 :         if (pevent & POLLOUT) {
     380      131073 :                 gevent |= G_IO_OUT;
     381             :         }
     382      393342 :         if (pevent & POLLHUP) {
     383           0 :                 gevent |= G_IO_HUP;
     384             :         }
     385      393342 :         if (pevent & POLLERR) {
     386           0 :                 gevent |= G_IO_ERR;
     387             :         }
     388             : 
     389           0 :         return gevent;
     390             : }
     391             : 
     392      393342 : static void tevent_glib_fd_handler(struct tevent_context *ev,
     393             :                                    struct tevent_fd *fde,
     394             :                                    uint16_t flags,
     395             :                                    void *private_data)
     396             : {
     397      393342 :         struct fd_map *fd_map = talloc_get_type_abort(
     398             :                 private_data, struct fd_map);
     399      393342 :         struct tevent_glib_glue *glue = NULL;
     400      393342 :         GPollFD *gpollfd = NULL;
     401             :         struct pollfd fd;
     402             :         int ret;
     403             :         int i;
     404             : 
     405      393342 :         glue = fd_map->glue;
     406             : 
     407      786621 :         for (i = 0; i < glue->num_gpollfds; i++) {
     408      786621 :                 if (glue->gpollfds[i].fd != fd_map->fd) {
     409      393279 :                         continue;
     410             :                 }
     411           0 :                 gpollfd = &glue->gpollfds[i];
     412           0 :                 break;
     413             :         }
     414      393342 :         if (gpollfd == NULL) {
     415           0 :                 DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
     416             :                         fd_map, fd_map->fd);
     417           0 :                 return;
     418             :         }
     419             :         /*
     420             :          * We have to poll() the fd to get the correct fd event for glib. tevent
     421             :          * only tells us about readable/writable in flags, but we need the full
     422             :          * glory for glib.
     423             :          */
     424             : 
     425      393342 :         fd = (struct pollfd) {
     426      393342 :                 .fd = gpollfd->fd,
     427      786684 :                 .events = gpoll_to_poll_event(gpollfd->events),
     428             :         };
     429             : 
     430      393342 :         ret = sys_poll_intr(&fd, 1, 0);
     431      393342 :         if (ret == -1) {
     432           0 :                 DBG_ERR("poll: %s\n", strerror(errno));
     433           0 :                 return;
     434             :         }
     435      393342 :         if (ret == 0) {
     436           0 :                 return;
     437             :         }
     438             : 
     439      786684 :         gpollfd->revents = poll_to_gpoll_event(fd.revents);
     440             : 
     441      393342 :         tevent_glib_process(glue);
     442      393342 :         return;
     443             : }
     444             : 
     445          19 : static void tevent_glib_timer_handler(struct tevent_context *ev,
     446             :                                       struct tevent_timer *te,
     447             :                                       struct timeval current_time,
     448             :                                       void *private_data)
     449             : {
     450          19 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     451             :                 private_data, struct tevent_glib_glue);
     452             : 
     453          19 :         glue->timer = NULL;
     454          19 :         tevent_glib_process(glue);
     455          19 :         return;
     456             : }
     457             : 
     458           0 : static void tevent_glib_im_handler(struct tevent_context *ev,
     459             :                                    struct tevent_immediate *im,
     460             :                                    void *private_data)
     461             : {
     462           0 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     463             :                 private_data, struct tevent_glib_glue);
     464             : 
     465           0 :         glue->scheduled_im = false;
     466           0 :         tevent_glib_process(glue);
     467           0 :         return;
     468             : }
     469             : 
     470      393363 : static bool save_current_fdset(struct tevent_glib_glue *glue)
     471             : {
     472             :         /*
     473             :          * Save old glib fds. We only grow the prev array.
     474             :          */
     475             : 
     476      393363 :         if (glue->num_prev_gpollfds < glue->num_gpollfds) {
     477           6 :                 glue->prev_gpollfds = talloc_realloc(glue,
     478             :                                                      glue->prev_gpollfds,
     479             :                                                      GPollFD,
     480             :                                                      glue->num_gpollfds);
     481           6 :                 if (glue->prev_gpollfds == NULL) {
     482           0 :                         DBG_ERR("talloc_realloc failed\n");
     483           0 :                         return false;
     484             :                 }
     485             :         }
     486      393363 :         glue->num_prev_gpollfds = glue->num_gpollfds;
     487      393363 :         if (glue->num_gpollfds > 0) {
     488      786718 :                 memcpy(glue->prev_gpollfds, glue->gpollfds,
     489      393359 :                        sizeof(GPollFD) * glue->num_gpollfds);
     490      393359 :                 memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
     491             :         }
     492             : 
     493           0 :         return true;
     494             : }
     495             : 
     496      393363 : static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
     497             : {
     498             :         bool ok;
     499             :         gint num_fds;
     500             : 
     501      393363 :         ok = save_current_fdset(glue);
     502      393363 :         if (!ok) {
     503           0 :                 return false;
     504             :         }
     505             : 
     506             :         while (true) {
     507      393377 :                 num_fds = g_main_context_query(glue->gmain_ctx,
     508             :                                                glue->gpriority,
     509             :                                                &glue->gtimeout,
     510             :                                                glue->gpollfds,
     511             :                                                glue->num_gpollfds);
     512      393370 :                 if (num_fds == glue->num_gpollfds) {
     513           0 :                         break;
     514             :                 }
     515           7 :                 glue->gpollfds = talloc_realloc(glue,
     516             :                                                 glue->gpollfds,
     517             :                                                 GPollFD,
     518             :                                                 num_fds);
     519           7 :                 if (num_fds > 0 && glue->gpollfds == NULL) {
     520           0 :                         DBG_ERR("talloc_realloc failed\n");
     521           0 :                         return false;
     522             :                 }
     523           7 :                 glue->num_gpollfds = num_fds;
     524             :         };
     525             : 
     526      393363 :         if (glue->num_gpollfds > 0) {
     527      393363 :                 qsort(glue->gpollfds,
     528             :                       num_fds,
     529             :                       sizeof(GPollFD),
     530             :                       glib_fd_cmp_func);
     531             :         }
     532             : 
     533      393363 :         DBG_DEBUG("num fds: %d, timeout: %d ms\n",
     534             :                   num_fds, glue->gtimeout);
     535             : 
     536           0 :         return true;
     537             : }
     538             : 
     539      393363 : static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
     540             : {
     541             :         uint64_t microsec;
     542             :         struct timeval tv;
     543             :         bool ok;
     544             : 
     545      786726 :         ok = cmp_gfds(glue,
     546             :                       glue->gpollfds,
     547             :                       glue->prev_gpollfds,
     548      393363 :                       glue->num_gpollfds,
     549      393363 :                       glue->num_prev_gpollfds,
     550             :                       glib_fd_cmp_func,
     551             :                       match_gfd_cb,
     552             :                       new_gfd_cb,
     553             :                       remove_gfd_cb);
     554      393363 :         if (!ok) {
     555           0 :                 return false;
     556             :         }
     557             : 
     558      393363 :         TALLOC_FREE(glue->timer);
     559             : 
     560      393363 :         if (glue->gtimeout == -1) {
     561           0 :                 return true;
     562             :         }
     563             : 
     564          19 :         if (glue->gtimeout == 0) {
     565             :                 /*
     566             :                  * glue->gtimeout is 0 if g_main_context_query() returned
     567             :                  * timeout=0. That means there are pending events ready to be
     568             :                  * dispatched. We only want to run one event handler per loop
     569             :                  * iteration, so we schedule an immediate event to run it in the
     570             :                  * next iteration.
     571             :                  */
     572           0 :                 if (glue->scheduled_im) {
     573           0 :                         return true;
     574             :                 }
     575           0 :                 tevent_schedule_immediate(glue->im,
     576             :                                           glue->ev,
     577             :                                           tevent_glib_im_handler,
     578             :                                           glue);
     579           0 :                 glue->scheduled_im = true;
     580           0 :                 return true;
     581             :         }
     582             : 
     583          19 :         microsec = glue->gtimeout * 1000;
     584          19 :         tv = tevent_timeval_current_ofs(microsec / 1000000,
     585          19 :                                         microsec % 1000000);
     586             : 
     587          19 :         glue->timer = tevent_add_timer(glue->ev,
     588             :                                        glue,
     589             :                                        tv,
     590             :                                        tevent_glib_timer_handler,
     591             :                                        glue);
     592          19 :         if (glue->timer == NULL) {
     593           0 :                 DBG_ERR("tevent_add_timer failed\n");
     594           0 :                 return false;
     595             :         }
     596             : 
     597           0 :         return true;
     598             : }
     599             : 
     600           0 : static void tevent_glib_retry_timer(struct tevent_context *ev,
     601             :                                     struct tevent_timer *te,
     602             :                                     struct timeval current_time,
     603             :                                     void *private_data)
     604             : {
     605           0 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     606             :                 private_data, struct tevent_glib_glue);
     607             : 
     608           0 :         glue->acquire_retry_timer = NULL;
     609           0 :         (void)tevent_glib_prepare(glue);
     610           0 : }
     611             : 
     612             : /**
     613             :  * Fetch glib event sources and add them to tevent
     614             :  *
     615             :  * Fetch glib event sources and attach corresponding tevent events to our tevent
     616             :  * context. get_glib_fds_and_timeout() gets the relevant glib event sources: the
     617             :  * set of active fds and the next timer. tevent_glib_update_events() then
     618             :  * translates those to tevent and creates tevent events.
     619             :  *
     620             :  * When called, the thread must NOT be the owner to the glib main
     621             :  * context. tevent_glib_prepare() is either the first function when the
     622             :  * tevent_glib_glue is created, or after tevent_glib_process() has been called
     623             :  * to process pending event, which will have ceased ownership.
     624             :  **/
     625      393366 : static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
     626             : {
     627             :         bool ok;
     628             :         gboolean gok;
     629             : 
     630      393366 :         if (glue->quit) {
     631             :                 /* Set via samba_tevent_glib_glue_quit() */
     632           0 :                 return true;
     633             :         }
     634             : 
     635      393363 :         if (glue->acquire_retry_timer != NULL) {
     636             :                 /*
     637             :                  * We're still waiting on the below g_main_context_acquire() to
     638             :                  * succeed, just return.
     639             :                  */
     640           0 :                 return true;
     641             :         }
     642             : 
     643      393363 :         if (glue->gmain_owner) {
     644      393359 :                 g_main_context_release(glue->gmain_ctx);
     645      393359 :                 glue->gmain_owner = false;
     646             :         }
     647             : 
     648      393363 :         gok = g_main_context_acquire(glue->gmain_ctx);
     649      393363 :         if (!gok) {
     650           0 :                 DBG_ERR("couldn't acquire g_main_context\n");
     651             : 
     652             :                 /*
     653             :                  * Ensure no tevent event fires while we're not the gmain
     654             :                  * context owner. The event handler would call
     655             :                  * tevent_glib_process() and that expects being the owner of the
     656             :                  * context.
     657             :                  */
     658           0 :                 ok = tevent_glib_glue_reinit(glue);
     659           0 :                 if (!ok) {
     660           0 :                         DBG_ERR("tevent_glib_glue_reinit failed\n");
     661           0 :                         samba_tevent_glib_glue_quit(glue);
     662           0 :                         return false;
     663             :                 }
     664             : 
     665           0 :                 glue->acquire_retry_timer = tevent_add_timer(
     666             :                         glue->ev,
     667             :                         glue,
     668             :                         tevent_timeval_current_ofs(0, 1000),
     669             :                         tevent_glib_retry_timer,
     670             :                         glue);
     671           0 :                 if (glue->acquire_retry_timer == NULL) {
     672           0 :                         DBG_ERR("tevent_add_timer failed\n");
     673           0 :                         samba_tevent_glib_glue_quit(glue);
     674           0 :                         return false;
     675             :                 }
     676           0 :                 return true;
     677             :         }
     678      393363 :         glue->gmain_owner = true;
     679             : 
     680             :         /*
     681             :          * Discard "ready" return value from g_main_context_prepare(). We don't
     682             :          * want to dispatch events here, thats only done in from the tevent loop.
     683             :          */
     684      393363 :         (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
     685             : 
     686      393363 :         ok = get_glib_fds_and_timeout(glue);
     687      393363 :         if (!ok) {
     688           0 :                 DBG_ERR("get_glib_fds_and_timeout failed\n");
     689           0 :                 samba_tevent_glib_glue_quit(glue);
     690           0 :                 return false;
     691             :         }
     692             : 
     693      393363 :         ok = tevent_glib_update_events(glue);
     694      393363 :         if (!ok) {
     695           0 :                 DBG_ERR("tevent_glib_update_events failed\n");
     696           0 :                 samba_tevent_glib_glue_quit(glue);
     697           0 :                 return false;
     698             :         }
     699             : 
     700           0 :         return true;
     701             : }
     702             : 
     703             : /**
     704             :  * Process pending glib events
     705             :  *
     706             :  * tevent_glib_process() gets called to process pending glib events via
     707             :  * g_main_context_check() and then g_main_context_dispatch().
     708             :  *
     709             :  * After pending event handlers are dispatched, we rearm the glib glue event
     710             :  * handlers in tevent by calling tevent_glib_prepare().
     711             :  *
     712             :  * When tevent_glib_process() is called the thread must own the glib
     713             :  * gmain_ctx. That is achieved by tevent_glib_prepare() being the only function
     714             :  * that acuires context ownership.
     715             :  *
     716             :  * To give other threads that are blocked on g_main_context_acquire(gmain_ctx) a
     717             :  * chance to acquire context ownership (eg needed to attach event sources), we
     718             :  * release context ownership before calling tevent_glib_prepare() which will
     719             :  * acquire it again.
     720             :  */
     721      393361 : static bool tevent_glib_process(struct tevent_glib_glue *glue)
     722             : {
     723             :         bool ok;
     724             : 
     725      393361 :         DBG_DEBUG("tevent_glib_process\n");
     726             : 
     727             :         /*
     728             :          * Ignore the "sources_ready" return from g_main_context_check(). glib
     729             :          * itself also ignores it in g_main_context_iterate(). In theory only
     730             :          * calling g_main_context_dispatch() if g_main_context_check() returns
     731             :          * true should work, but older glib versions had a bug where
     732             :          * g_main_context_check() returns false even though events are pending.
     733             :          *
     734             :          * https://bugzilla.gnome.org/show_bug.cgi?id=11059
     735             :          */
     736      393361 :         (void)g_main_context_check(glue->gmain_ctx,
     737             :                                    glue->gpriority,
     738             :                                    glue->gpollfds,
     739             :                                    glue->num_gpollfds);
     740             : 
     741      393361 :         g_main_context_dispatch(glue->gmain_ctx);
     742             : 
     743      393361 :         ok = tevent_glib_prepare(glue);
     744      393361 :         if (!ok) {
     745           0 :                 return false;
     746             :         }
     747      393361 :         glue->skip_glib_refresh = true;
     748      393361 :         return true;
     749             : }
     750             : 
     751     1573445 : static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
     752             :                                             void *private_data)
     753             : {
     754     1573445 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     755             :                 private_data, struct tevent_glib_glue);
     756             : 
     757     1573445 :         if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
     758      393359 :                 if (!glue->skip_glib_refresh) {
     759           1 :                         tevent_glib_prepare(glue);
     760             :                 }
     761      393359 :                 glue->skip_glib_refresh = false;
     762             :         }
     763             : 
     764             :         /* chain previous handler */
     765     1573445 :         if (glue->prev_tevent_trace_cb != NULL) {
     766           0 :                 glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
     767             :         }
     768     1573445 : }
     769             : 
     770           5 : static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
     771             : {
     772           5 :         size_t n = talloc_array_length(glue->fd_map);
     773             :         size_t i;
     774             : 
     775          10 :         for (i = 0; i < n; i++) {
     776           5 :                 TALLOC_FREE(glue->fd_map[i]->fd_event);
     777           5 :                 TALLOC_FREE(glue->fd_map[i]);
     778             :         }
     779             : 
     780           5 :         tevent_set_trace_callback(glue->ev,
     781             :                                   glue->prev_tevent_trace_cb,
     782             :                                   glue->prev_tevent_trace_data);
     783           5 :         glue->prev_tevent_trace_cb = NULL;
     784           5 :         glue->prev_tevent_trace_data = NULL;
     785             : 
     786           5 :         TALLOC_FREE(glue->fd_map);
     787           5 :         glue->num_maps = 0;
     788             : 
     789           5 :         TALLOC_FREE(glue->gpollfds);
     790           5 :         glue->num_gpollfds = 0;
     791             : 
     792           5 :         TALLOC_FREE(glue->prev_gpollfds);
     793           5 :         glue->num_prev_gpollfds = 0;
     794             : 
     795           5 :         TALLOC_FREE(glue->timer);
     796           5 :         TALLOC_FREE(glue->acquire_retry_timer);
     797           5 :         TALLOC_FREE(glue->im);
     798             : 
     799             :         /*
     800             :          * These are not really needed, but let's wipe the slate clean.
     801             :          */
     802           5 :         glue->skip_glib_refresh = false;
     803           5 :         glue->gtimeout = 0;
     804           5 :         glue->gpriority = 0;
     805           5 : }
     806             : 
     807           0 : static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
     808             : {
     809           0 :         tevent_glib_glue_cleanup(glue);
     810             : 
     811           0 :         glue->im = tevent_create_immediate(glue);
     812           0 :         if (glue->im == NULL) {
     813           0 :                 return false;
     814             :         }
     815             : 
     816           0 :         tevent_get_trace_callback(glue->ev,
     817             :                                   &glue->prev_tevent_trace_cb,
     818           0 :                                   &glue->prev_tevent_trace_data);
     819           0 :         tevent_set_trace_callback(glue->ev,
     820             :                                   tevent_glib_glue_trace_callback,
     821             :                                   glue);
     822             : 
     823           0 :         return true;
     824             : }
     825             : 
     826           5 : void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
     827             : {
     828           5 :         tevent_glib_glue_cleanup(glue);
     829           5 :         glue->quit = true;
     830           5 :         return;
     831             : }
     832             : 
     833           4 : struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
     834             :                                                        struct tevent_context *ev,
     835             :                                                        GMainContext *gmain_ctx)
     836             : {
     837             :         bool ok;
     838           4 :         struct tevent_glib_glue *glue = NULL;
     839             : 
     840           4 :         glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
     841           4 :         if (glue == NULL) {
     842           0 :                 DBG_ERR("talloc_zero failed\n");
     843           0 :                 return NULL;
     844             :         }
     845             : 
     846           4 :         *glue = (struct tevent_glib_glue) {
     847             :                 .ev = ev,
     848             :                 .gmain_ctx = gmain_ctx,
     849             :         };
     850             : 
     851           4 :         glue->im = tevent_create_immediate(glue);
     852             : 
     853           4 :         tevent_get_trace_callback(glue->ev,
     854             :                                   &glue->prev_tevent_trace_cb,
     855           4 :                                   &glue->prev_tevent_trace_data);
     856           4 :         tevent_set_trace_callback(glue->ev,
     857             :                                   tevent_glib_glue_trace_callback,
     858             :                                   glue);
     859             : 
     860           4 :         ok = tevent_glib_prepare(glue);
     861           4 :         if (!ok) {
     862           0 :                 TALLOC_FREE(glue);
     863           0 :                 return NULL;
     864             :         }
     865             : 
     866           0 :         return glue;
     867             : }
     868             : 
     869             : #else /* HAVE_GLIB */
     870             : 
     871             : struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
     872             :                                                        struct tevent_context *ev,
     873             :                                                        GMainContext *gmain_ctx)
     874             : {
     875             :         errno = ENOSYS;
     876             :         return NULL;
     877             : }
     878             : 
     879             : void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
     880             : {
     881             :         return;
     882             : }
     883             : #endif /* HAVE_GLIB */

Generated by: LCOV version 1.13