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 */