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
7 ** NOTE! The following LGPL license applies to the tevent
8 ** library. This does NOT imply that all of Samba is released
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.
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.
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/>.
26 #include "system/filesys.h"
27 #include "lib/util/debug.h"
28 #include "lib/util/select.h"
32 #define DBGC_CLASS DBGC_TEVENT
36 #include "tevent_glib_glue.h"
39 struct tevent_glib_glue *glue;
41 struct tevent_fd *fd_event;
44 struct tevent_glib_glue {
46 * The tevent context we're feeding.
48 struct tevent_context *ev;
51 * The glib gmain context we're polling and whether we're currently
52 * owning it by virtue of g_main_context_acquire().
54 GMainContext *gmain_ctx;
58 * Set by samba_tevent_glib_glue_quit().
63 * tevent trace callback and data we got from tevent_get_trace_callback()
64 * before installing our own trace callback.
66 tevent_trace_callback_t prev_tevent_trace_cb;
67 void *prev_tevent_trace_data;
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.
74 bool skip_glib_refresh;
77 * Used when acquiring the glib gmain context failed.
79 struct tevent_timer *acquire_retry_timer;
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.
90 struct tevent_timer *timer;
91 struct tevent_immediate *im;
95 * glib gmain context fds returned from g_main_context_query(). These
96 * get translated to tevent fd events.
102 * A copy of gpollfds and num_gpollfds from the previous event loop
103 * iteration, used to detect changes in the set of fds.
105 GPollFD *prev_gpollfds;
106 gint num_prev_gpollfds;
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.
112 struct fd_map **fd_map;
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,
124 typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2);
125 typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue,
128 typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue,
130 typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue,
134 * Compare two sorted GPollFD arrays
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.
140 static bool cmp_gfds(struct tevent_glib_glue *glue,
144 size_t num_prev_gfds,
146 gfds_found_cb found_cb,
148 gfds_removed_cb removed_cb)
154 while (i < num_gfds && j < num_prev_gfds) {
155 cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
157 ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
163 } else if (cmp < 0) {
164 ok = new_cb(glue, &gfds[i]);
170 ok = removed_cb(glue, &prev_gfds[j]);
178 while (i < num_gfds) {
179 ok = new_cb(glue, &gfds[i++]);
185 while (j < num_prev_gfds) {
186 ok = removed_cb(glue, &prev_gfds[j++]);
195 static int glib_fd_cmp_func(const void *p1, const void *p2)
197 const GPollFD *lhs = p1;
198 const GPollFD *rhs = p2;
200 if (lhs->fd < rhs->fd) {
202 } else if (lhs->fd > rhs->fd) {
210 * We already have a tevent fd event for the glib GPollFD, but we may have to
213 static bool match_gfd_cb(struct tevent_glib_glue *glue,
214 const GPollFD *new_gfd,
215 const GPollFD *old_gfd)
218 struct fd_map *fd_map = NULL;
219 struct tevent_fd *fd_event = NULL;
221 if (new_gfd->events == old_gfd->events) {
225 for (i = 0; i < glue->num_maps; i++) {
226 if (glue->fd_map[i]->fd == new_gfd->fd) {
231 if (i == glue->num_maps) {
232 DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
236 fd_map = glue->fd_map[i];
237 if (fd_map == NULL) {
238 DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd);
242 fd_event = fd_map->fd_event;
243 if (fd_event == NULL) {
244 DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd);
248 tevent_fd_set_flags(fd_event, 0);
250 if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
251 TEVENT_FD_READABLE(fd_event);
253 if (new_gfd->events & G_IO_OUT) {
254 TEVENT_FD_WRITEABLE(fd_event);
260 static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
262 struct tevent_fd *fd_event = NULL;
263 struct fd_map *fd_map = NULL;
268 revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR));
269 wevent = (gfd->events & G_IO_OUT);
271 events |= TEVENT_FD_READ;
274 events |= TEVENT_FD_WRITE;
277 glue->fd_map = talloc_realloc(glue,
281 if (glue->fd_map == NULL) {
282 DBG_ERR("talloc_realloc failed\n");
285 fd_map = talloc_zero(glue->fd_map, struct fd_map);
286 if (fd_map == NULL) {
287 DBG_ERR("talloc_realloc failed\n");
290 glue->fd_map[glue->num_maps] = fd_map;
293 fd_event = tevent_add_fd(glue->ev,
297 tevent_glib_fd_handler,
299 if (fd_event == NULL) {
300 DBG_ERR("tevent_add_fd failed\n");
304 *fd_map = (struct fd_map) {
307 .fd_event = fd_event,
310 DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
315 static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
319 for (i = 0; i < glue->num_maps; i++) {
320 if (glue->fd_map[i]->fd == gfd->fd) {
325 if (i == glue->num_maps) {
326 DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
330 TALLOC_FREE(glue->fd_map[i]->fd_event);
331 TALLOC_FREE(glue->fd_map[i]);
333 if (i + 1 < glue->num_maps) {
334 memmove(&glue->fd_map[i],
336 (glue->num_maps - (i + 1)) * sizeof(struct fd_map *));
339 glue->fd_map = talloc_realloc(glue,
343 if (glue->num_maps > 0 && glue->fd_map == NULL) {
344 DBG_ERR("talloc_realloc failed\n");
352 static short gpoll_to_poll_event(gushort gevent)
356 if (gevent & G_IO_IN) {
359 if (gevent & G_IO_OUT) {
362 if (gevent & G_IO_HUP) {
365 if (gevent & G_IO_ERR) {
372 static gushort poll_to_gpoll_event(short pevent)
376 if (pevent & POLLIN) {
379 if (pevent & POLLOUT) {
382 if (pevent & POLLHUP) {
385 if (pevent & POLLERR) {
392 static void tevent_glib_fd_handler(struct tevent_context *ev,
393 struct tevent_fd *fde,
397 struct fd_map *fd_map = talloc_get_type_abort(
398 private_data, struct fd_map);
399 struct tevent_glib_glue *glue = NULL;
400 GPollFD *gpollfd = NULL;
407 for (i = 0; i < glue->num_gpollfds; i++) {
408 if (glue->gpollfds[i].fd != fd_map->fd) {
411 gpollfd = &glue->gpollfds[i];
414 if (gpollfd == NULL) {
415 DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
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
425 fd = (struct pollfd) {
427 .events = gpoll_to_poll_event(gpollfd->events),
430 ret = sys_poll_intr(&fd, 1, 0);
432 DBG_ERR("poll: %s\n", strerror(errno));
439 gpollfd->revents = poll_to_gpoll_event(fd.revents);
441 tevent_glib_process(glue);
445 static void tevent_glib_timer_handler(struct tevent_context *ev,
446 struct tevent_timer *te,
447 struct timeval current_time,
450 struct tevent_glib_glue *glue = talloc_get_type_abort(
451 private_data, struct tevent_glib_glue);
454 tevent_glib_process(glue);
458 static void tevent_glib_im_handler(struct tevent_context *ev,
459 struct tevent_immediate *im,
462 struct tevent_glib_glue *glue = talloc_get_type_abort(
463 private_data, struct tevent_glib_glue);
465 glue->scheduled_im = false;
466 tevent_glib_process(glue);
470 static bool save_current_fdset(struct tevent_glib_glue *glue)
473 * Save old glib fds. We only grow the prev array.
476 if (glue->num_prev_gpollfds < glue->num_gpollfds) {
477 glue->prev_gpollfds = talloc_realloc(glue,
481 if (glue->prev_gpollfds == NULL) {
482 DBG_ERR("talloc_realloc failed\n");
486 glue->num_prev_gpollfds = glue->num_gpollfds;
487 if (glue->num_gpollfds > 0) {
488 memcpy(glue->prev_gpollfds, glue->gpollfds,
489 sizeof(GPollFD) * glue->num_gpollfds);
490 memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
496 static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
501 ok = save_current_fdset(glue);
507 num_fds = g_main_context_query(glue->gmain_ctx,
512 if (num_fds == glue->num_gpollfds) {
515 glue->gpollfds = talloc_realloc(glue,
519 if (num_fds > 0 && glue->gpollfds == NULL) {
520 DBG_ERR("talloc_realloc failed\n");
523 glue->num_gpollfds = num_fds;
526 if (glue->num_gpollfds > 0) {
527 qsort(glue->gpollfds,
533 DBG_DEBUG("num fds: %d, timeout: %d ms\n",
534 num_fds, glue->gtimeout);
539 static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
548 glue->num_prev_gpollfds,
557 TALLOC_FREE(glue->timer);
559 if (glue->gtimeout == -1) {
563 if (glue->gtimeout == 0) {
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
571 if (glue->scheduled_im) {
574 tevent_schedule_immediate(glue->im,
576 tevent_glib_im_handler,
578 glue->scheduled_im = true;
582 tv = tevent_timeval_current_ofs(glue->gtimeout / 1000,
583 (glue->gtimeout % 1000) * 1000);
585 glue->timer = tevent_add_timer(glue->ev,
588 tevent_glib_timer_handler,
590 if (glue->timer == NULL) {
591 DBG_ERR("tevent_add_timer failed\n");
598 static void tevent_glib_retry_timer(struct tevent_context *ev,
599 struct tevent_timer *te,
600 struct timeval current_time,
603 struct tevent_glib_glue *glue = talloc_get_type_abort(
604 private_data, struct tevent_glib_glue);
606 glue->acquire_retry_timer = NULL;
607 (void)tevent_glib_prepare(glue);
611 * Fetch glib event sources and add them to tevent
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.
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.
623 static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
629 /* Set via samba_tevent_glib_glue_quit() */
633 if (glue->acquire_retry_timer != NULL) {
635 * We're still waiting on the below g_main_context_acquire() to
636 * succeed, just return.
641 if (glue->gmain_owner) {
642 g_main_context_release(glue->gmain_ctx);
643 glue->gmain_owner = false;
646 gok = g_main_context_acquire(glue->gmain_ctx);
648 DBG_ERR("couldn't acquire g_main_context\n");
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
656 ok = tevent_glib_glue_reinit(glue);
658 DBG_ERR("tevent_glib_glue_reinit failed\n");
659 samba_tevent_glib_glue_quit(glue);
663 glue->acquire_retry_timer = tevent_add_timer(
666 tevent_timeval_current_ofs(0, 1000),
667 tevent_glib_retry_timer,
669 if (glue->acquire_retry_timer == NULL) {
670 DBG_ERR("tevent_add_timer failed\n");
671 samba_tevent_glib_glue_quit(glue);
676 glue->gmain_owner = true;
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.
682 (void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
684 ok = get_glib_fds_and_timeout(glue);
686 DBG_ERR("get_glib_fds_and_timeout failed\n");
687 samba_tevent_glib_glue_quit(glue);
691 ok = tevent_glib_update_events(glue);
693 DBG_ERR("tevent_glib_update_events failed\n");
694 samba_tevent_glib_glue_quit(glue);
702 * Process pending glib events
704 * tevent_glib_process() gets called to process pending glib events via
705 * g_main_context_check() and then g_main_context_dispatch().
707 * After pending event handlers are dispatched, we rearm the glib glue event
708 * handlers in tevent by calling tevent_glib_prepare().
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.
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
719 static bool tevent_glib_process(struct tevent_glib_glue *glue)
723 DBG_DEBUG("tevent_glib_process\n");
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.
732 * https://bugzilla.gnome.org/show_bug.cgi?id=11059
734 (void)g_main_context_check(glue->gmain_ctx,
739 g_main_context_dispatch(glue->gmain_ctx);
741 ok = tevent_glib_prepare(glue);
745 glue->skip_glib_refresh = true;
749 static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
752 struct tevent_glib_glue *glue = talloc_get_type_abort(
753 private_data, struct tevent_glib_glue);
755 if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
756 if (!glue->skip_glib_refresh) {
757 tevent_glib_prepare(glue);
759 glue->skip_glib_refresh = false;
762 /* chain previous handler */
763 if (glue->prev_tevent_trace_cb != NULL) {
764 glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
768 static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
770 size_t n = talloc_array_length(glue->fd_map);
773 for (i = 0; i < n; i++) {
774 TALLOC_FREE(glue->fd_map[i]->fd_event);
775 TALLOC_FREE(glue->fd_map[i]);
778 tevent_set_trace_callback(glue->ev,
779 glue->prev_tevent_trace_cb,
780 glue->prev_tevent_trace_data);
781 glue->prev_tevent_trace_cb = NULL;
782 glue->prev_tevent_trace_data = NULL;
784 TALLOC_FREE(glue->fd_map);
787 TALLOC_FREE(glue->gpollfds);
788 glue->num_gpollfds = 0;
790 TALLOC_FREE(glue->prev_gpollfds);
791 glue->num_prev_gpollfds = 0;
793 TALLOC_FREE(glue->timer);
794 TALLOC_FREE(glue->acquire_retry_timer);
795 TALLOC_FREE(glue->im);
798 * These are not really needed, but let's wipe the slate clean.
800 glue->skip_glib_refresh = false;
805 static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
807 tevent_glib_glue_cleanup(glue);
809 glue->im = tevent_create_immediate(glue);
810 if (glue->im == NULL) {
814 tevent_get_trace_callback(glue->ev,
815 &glue->prev_tevent_trace_cb,
816 &glue->prev_tevent_trace_data);
817 tevent_set_trace_callback(glue->ev,
818 tevent_glib_glue_trace_callback,
824 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
826 tevent_glib_glue_cleanup(glue);
831 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
832 struct tevent_context *ev,
833 GMainContext *gmain_ctx)
836 struct tevent_glib_glue *glue = NULL;
838 glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
840 DBG_ERR("talloc_zero failed\n");
844 *glue = (struct tevent_glib_glue) {
846 .gmain_ctx = gmain_ctx,
849 glue->im = tevent_create_immediate(glue);
851 tevent_get_trace_callback(glue->ev,
852 &glue->prev_tevent_trace_cb,
853 &glue->prev_tevent_trace_data);
854 tevent_set_trace_callback(glue->ev,
855 tevent_glib_glue_trace_callback,
858 ok = tevent_glib_prepare(glue);
867 #else /* HAVE_GLIB */
869 struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
870 struct tevent_context *ev,
871 GMainContext *gmain_ctx)
877 void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
881 #endif /* HAVE_GLIB */