LCOV - code coverage report
Current view: top level - source3/lib - tevent_glib_glue.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 222 313 70.9 %
Date: 2024-04-21 15:09:00 Functions: 16 19 84.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             :          * explicitly 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      393363 :         bool ok;
     151      393363 :         size_t i = 0, j = 0;
     152      393363 :         int cmp;
     153             : 
     154     1573285 :         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             :                                 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             :                                 return false;
     167             :                         }
     168           1 :                         i++;
     169             :                 } else {
     170           0 :                         ok = removed_cb(glue, &prev_gfds[j]);
     171           0 :                         if (!ok) {
     172             :                                 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             :                         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             :                         return false;
     189             :                 }
     190             :         }
     191             : 
     192             :         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             :                 return -1;
     202     1179921 :         } else if (lhs->fd > rhs->fd) {
     203           0 :                 return 1;
     204             :         }
     205             : 
     206             :         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     1179921 :         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             :                 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             :                         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             :         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           6 :         bool revent;
     266           6 :         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             :         return true;
     313             : }
     314             : 
     315           1 : static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
     316             : {
     317           1 :         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             :                         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           2 :                 memmove(&glue->fd_map[i],
     335           1 :                         &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      393342 : 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      393342 :         return pevent;
     370             : }
     371             : 
     372      393342 : 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      393342 :         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      393342 :         struct pollfd fd;
     402      393342 :         int ret;
     403      393342 :         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             :                 gpollfd = &glue->gpollfds[i];
     412             :                 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      786684 :         fd = (struct pollfd) {
     426      393342 :                 .fd = gpollfd->fd,
     427      393342 :                 .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             :                 return;
     437             :         }
     438             : 
     439      393342 :         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      393359 :                 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             :         return true;
     494             : }
     495             : 
     496      393363 : static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
     497             : {
     498      393363 :         bool ok;
     499      393363 :         gint num_fds;
     500             : 
     501      393363 :         ok = save_current_fdset(glue);
     502      393363 :         if (!ok) {
     503             :                 return false;
     504             :         }
     505             : 
     506      393377 :         while (true) {
     507      393370 :                 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             :                         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      393363 :         };
     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             :         return true;
     537             : }
     538             : 
     539      393363 : static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
     540             : {
     541      393363 :         struct timeval tv;
     542      393363 :         bool ok;
     543             : 
     544      786726 :         ok = cmp_gfds(glue,
     545             :                       glue->gpollfds,
     546             :                       glue->prev_gpollfds,
     547      393363 :                       glue->num_gpollfds,
     548      393363 :                       glue->num_prev_gpollfds,
     549             :                       glib_fd_cmp_func,
     550             :                       match_gfd_cb,
     551             :                       new_gfd_cb,
     552             :                       remove_gfd_cb);
     553      393363 :         if (!ok) {
     554             :                 return false;
     555             :         }
     556             : 
     557      393363 :         TALLOC_FREE(glue->timer);
     558             : 
     559      393363 :         if (glue->gtimeout == -1) {
     560             :                 return true;
     561             :         }
     562             : 
     563          19 :         if (glue->gtimeout == 0) {
     564             :                 /*
     565             :                  * glue->gtimeout is 0 if g_main_context_query() returned
     566             :                  * timeout=0. That means there are pending events ready to be
     567             :                  * dispatched. We only want to run one event handler per loop
     568             :                  * iteration, so we schedule an immediate event to run it in the
     569             :                  * next iteration.
     570             :                  */
     571           0 :                 if (glue->scheduled_im) {
     572             :                         return true;
     573             :                 }
     574           0 :                 tevent_schedule_immediate(glue->im,
     575             :                                           glue->ev,
     576             :                                           tevent_glib_im_handler,
     577           0 :                                           glue);
     578           0 :                 glue->scheduled_im = true;
     579           0 :                 return true;
     580             :         }
     581             : 
     582          19 :         tv = tevent_timeval_current_ofs(glue->gtimeout / 1000,
     583          19 :                                         (glue->gtimeout % 1000) * 1000);
     584             : 
     585          19 :         glue->timer = tevent_add_timer(glue->ev,
     586             :                                        glue,
     587             :                                        tv,
     588             :                                        tevent_glib_timer_handler,
     589             :                                        glue);
     590          19 :         if (glue->timer == NULL) {
     591           0 :                 DBG_ERR("tevent_add_timer failed\n");
     592           0 :                 return false;
     593             :         }
     594             : 
     595             :         return true;
     596             : }
     597             : 
     598           0 : static void tevent_glib_retry_timer(struct tevent_context *ev,
     599             :                                     struct tevent_timer *te,
     600             :                                     struct timeval current_time,
     601             :                                     void *private_data)
     602             : {
     603           0 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     604             :                 private_data, struct tevent_glib_glue);
     605             : 
     606           0 :         glue->acquire_retry_timer = NULL;
     607           0 :         (void)tevent_glib_prepare(glue);
     608           0 : }
     609             : 
     610             : /**
     611             :  * Fetch glib event sources and add them to tevent
     612             :  *
     613             :  * Fetch glib event sources and attach corresponding tevent events to our tevent
     614             :  * context. get_glib_fds_and_timeout() gets the relevant glib event sources: the
     615             :  * set of active fds and the next timer. tevent_glib_update_events() then
     616             :  * translates those to tevent and creates tevent events.
     617             :  *
     618             :  * When called, the thread must NOT be the owner to the glib main
     619             :  * context. tevent_glib_prepare() is either the first function when the
     620             :  * tevent_glib_glue is created, or after tevent_glib_process() has been called
     621             :  * to process pending event, which will have ceased ownership.
     622             :  **/
     623      393366 : static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
     624             : {
     625      393366 :         bool ok;
     626      393366 :         gboolean gok;
     627             : 
     628      393366 :         if (glue->quit) {
     629             :                 /* Set via samba_tevent_glib_glue_quit() */
     630             :                 return true;
     631             :         }
     632             : 
     633      393363 :         if (glue->acquire_retry_timer != NULL) {
     634             :                 /*
     635             :                  * We're still waiting on the below g_main_context_acquire() to
     636             :                  * succeed, just return.
     637             :                  */
     638             :                 return true;
     639             :         }
     640             : 
     641      393363 :         if (glue->gmain_owner) {
     642      393359 :                 g_main_context_release(glue->gmain_ctx);
     643      393359 :                 glue->gmain_owner = false;
     644             :         }
     645             : 
     646      393363 :         gok = g_main_context_acquire(glue->gmain_ctx);
     647      393363 :         if (!gok) {
     648           0 :                 DBG_ERR("couldn't acquire g_main_context\n");
     649             : 
     650             :                 /*
     651             :                  * Ensure no tevent event fires while we're not the gmain
     652             :                  * context owner. The event handler would call
     653             :                  * tevent_glib_process() and that expects being the owner of the
     654             :                  * context.
     655             :                  */
     656           0 :                 ok = tevent_glib_glue_reinit(glue);
     657           0 :                 if (!ok) {
     658           0 :                         DBG_ERR("tevent_glib_glue_reinit failed\n");
     659           0 :                         samba_tevent_glib_glue_quit(glue);
     660           0 :                         return false;
     661             :                 }
     662             : 
     663           0 :                 glue->acquire_retry_timer = tevent_add_timer(
     664             :                         glue->ev,
     665             :                         glue,
     666             :                         tevent_timeval_current_ofs(0, 1000),
     667             :                         tevent_glib_retry_timer,
     668             :                         glue);
     669           0 :                 if (glue->acquire_retry_timer == NULL) {
     670           0 :                         DBG_ERR("tevent_add_timer failed\n");
     671           0 :                         samba_tevent_glib_glue_quit(glue);
     672           0 :                         return false;
     673             :                 }
     674             :                 return true;
     675             :         }
     676      393363 :         glue->gmain_owner = true;
     677             : 
     678             :         /*
     679             :          * Discard "ready" return value from g_main_context_prepare(). We don't
     680             :          * want to dispatch events here, that's only done in from the tevent loop.
     681             :          */
     682      393363 :         (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
     683             : 
     684      393363 :         ok = get_glib_fds_and_timeout(glue);
     685      393363 :         if (!ok) {
     686           0 :                 DBG_ERR("get_glib_fds_and_timeout failed\n");
     687           0 :                 samba_tevent_glib_glue_quit(glue);
     688           0 :                 return false;
     689             :         }
     690             : 
     691      393363 :         ok = tevent_glib_update_events(glue);
     692      393363 :         if (!ok) {
     693           0 :                 DBG_ERR("tevent_glib_update_events failed\n");
     694           0 :                 samba_tevent_glib_glue_quit(glue);
     695           0 :                 return false;
     696             :         }
     697             : 
     698             :         return true;
     699             : }
     700             : 
     701             : /**
     702             :  * Process pending glib events
     703             :  *
     704             :  * tevent_glib_process() gets called to process pending glib events via
     705             :  * g_main_context_check() and then g_main_context_dispatch().
     706             :  *
     707             :  * After pending event handlers are dispatched, we rearm the glib glue event
     708             :  * handlers in tevent by calling tevent_glib_prepare().
     709             :  *
     710             :  * When tevent_glib_process() is called the thread must own the glib
     711             :  * gmain_ctx. That is achieved by tevent_glib_prepare() being the only function
     712             :  * that acquires context ownership.
     713             :  *
     714             :  * To give other threads that are blocked on g_main_context_acquire(gmain_ctx) a
     715             :  * chance to acquire context ownership (eg needed to attach event sources), we
     716             :  * release context ownership before calling tevent_glib_prepare() which will
     717             :  * acquire it again.
     718             :  */
     719      393361 : static bool tevent_glib_process(struct tevent_glib_glue *glue)
     720             : {
     721      393361 :         bool ok;
     722             : 
     723      393361 :         DBG_DEBUG("tevent_glib_process\n");
     724             : 
     725             :         /*
     726             :          * Ignore the "sources_ready" return from g_main_context_check(). glib
     727             :          * itself also ignores it in g_main_context_iterate(). In theory only
     728             :          * calling g_main_context_dispatch() if g_main_context_check() returns
     729             :          * true should work, but older glib versions had a bug where
     730             :          * g_main_context_check() returns false even though events are pending.
     731             :          *
     732             :          * https://bugzilla.gnome.org/show_bug.cgi?id=11059
     733             :          */
     734      393361 :         (void)g_main_context_check(glue->gmain_ctx,
     735             :                                    glue->gpriority,
     736             :                                    glue->gpollfds,
     737             :                                    glue->num_gpollfds);
     738             : 
     739      393361 :         g_main_context_dispatch(glue->gmain_ctx);
     740             : 
     741      393361 :         ok = tevent_glib_prepare(glue);
     742      393361 :         if (!ok) {
     743             :                 return false;
     744             :         }
     745      393361 :         glue->skip_glib_refresh = true;
     746      393361 :         return true;
     747             : }
     748             : 
     749     1573445 : static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
     750             :                                             void *private_data)
     751             : {
     752     1573445 :         struct tevent_glib_glue *glue = talloc_get_type_abort(
     753             :                 private_data, struct tevent_glib_glue);
     754             : 
     755     1573445 :         if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
     756      393359 :                 if (!glue->skip_glib_refresh) {
     757           1 :                         tevent_glib_prepare(glue);
     758             :                 }
     759      393359 :                 glue->skip_glib_refresh = false;
     760             :         }
     761             : 
     762             :         /* chain previous handler */
     763     1573445 :         if (glue->prev_tevent_trace_cb != NULL) {
     764           0 :                 glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
     765             :         }
     766     1573445 : }
     767             : 
     768           5 : static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
     769             : {
     770           5 :         size_t n = talloc_array_length(glue->fd_map);
     771           5 :         size_t i;
     772             : 
     773          10 :         for (i = 0; i < n; i++) {
     774           5 :                 TALLOC_FREE(glue->fd_map[i]->fd_event);
     775           5 :                 TALLOC_FREE(glue->fd_map[i]);
     776             :         }
     777             : 
     778           5 :         tevent_set_trace_callback(glue->ev,
     779             :                                   glue->prev_tevent_trace_cb,
     780             :                                   glue->prev_tevent_trace_data);
     781           5 :         glue->prev_tevent_trace_cb = NULL;
     782           5 :         glue->prev_tevent_trace_data = NULL;
     783             : 
     784           5 :         TALLOC_FREE(glue->fd_map);
     785           5 :         glue->num_maps = 0;
     786             : 
     787           5 :         TALLOC_FREE(glue->gpollfds);
     788           5 :         glue->num_gpollfds = 0;
     789             : 
     790           5 :         TALLOC_FREE(glue->prev_gpollfds);
     791           5 :         glue->num_prev_gpollfds = 0;
     792             : 
     793           5 :         TALLOC_FREE(glue->timer);
     794           5 :         TALLOC_FREE(glue->acquire_retry_timer);
     795           5 :         TALLOC_FREE(glue->im);
     796             : 
     797             :         /*
     798             :          * These are not really needed, but let's wipe the slate clean.
     799             :          */
     800           5 :         glue->skip_glib_refresh = false;
     801           5 :         glue->gtimeout = 0;
     802           5 :         glue->gpriority = 0;
     803           5 : }
     804             : 
     805           0 : static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
     806             : {
     807           0 :         tevent_glib_glue_cleanup(glue);
     808             : 
     809           0 :         glue->im = tevent_create_immediate(glue);
     810           0 :         if (glue->im == NULL) {
     811             :                 return false;
     812             :         }
     813             : 
     814           0 :         tevent_get_trace_callback(glue->ev,
     815             :                                   &glue->prev_tevent_trace_cb,
     816           0 :                                   &glue->prev_tevent_trace_data);
     817           0 :         tevent_set_trace_callback(glue->ev,
     818             :                                   tevent_glib_glue_trace_callback,
     819             :                                   glue);
     820             : 
     821           0 :         return true;
     822             : }
     823             : 
     824           5 : void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
     825             : {
     826           5 :         tevent_glib_glue_cleanup(glue);
     827           5 :         glue->quit = true;
     828           5 :         return;
     829             : }
     830             : 
     831           4 : struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
     832             :                                                        struct tevent_context *ev,
     833             :                                                        GMainContext *gmain_ctx)
     834             : {
     835           4 :         bool ok;
     836           4 :         struct tevent_glib_glue *glue = NULL;
     837             : 
     838           4 :         glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
     839           4 :         if (glue == NULL) {
     840           0 :                 DBG_ERR("talloc_zero failed\n");
     841           0 :                 return NULL;
     842             :         }
     843             : 
     844           4 :         *glue = (struct tevent_glib_glue) {
     845             :                 .ev = ev,
     846             :                 .gmain_ctx = gmain_ctx,
     847             :         };
     848             : 
     849           4 :         glue->im = tevent_create_immediate(glue);
     850             : 
     851           4 :         tevent_get_trace_callback(glue->ev,
     852             :                                   &glue->prev_tevent_trace_cb,
     853           4 :                                   &glue->prev_tevent_trace_data);
     854           4 :         tevent_set_trace_callback(glue->ev,
     855             :                                   tevent_glib_glue_trace_callback,
     856             :                                   glue);
     857             : 
     858           4 :         ok = tevent_glib_prepare(glue);
     859           4 :         if (!ok) {
     860           0 :                 TALLOC_FREE(glue);
     861           0 :                 return NULL;
     862             :         }
     863             : 
     864             :         return glue;
     865             : }
     866             : 
     867             : #else /* HAVE_GLIB */
     868             : 
     869             : struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
     870             :                                                        struct tevent_context *ev,
     871             :                                                        GMainContext *gmain_ctx)
     872             : {
     873             :         errno = ENOSYS;
     874             :         return NULL;
     875             : }
     876             : 
     877             : void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
     878             : {
     879             :         return;
     880             : }
     881             : #endif /* HAVE_GLIB */

Generated by: LCOV version 1.14