mirror of
https://github.com/samba-team/samba.git
synced 2025-02-02 09:47:23 +03:00
s3/lib: new tevent_glib_glue subsystem
tevent_glib_glue_create() takes glib GMainContext and adds its event sources to a tevent context. tevent will poll the sources and run handlers for pending events as detailed in the glib documentation: https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Noel Power <npower@samba.org>
This commit is contained in:
parent
2f87661c56
commit
fa39a7b2e6
883
source3/lib/tevent_glib_glue.c
Normal file
883
source3/lib/tevent_glib_glue.c
Normal file
@ -0,0 +1,883 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Integration of a glib g_main_context into a tevent_context
|
||||
Copyright (C) Stefan Metzmacher 2016
|
||||
Copyright (C) Ralph Boehme 2016
|
||||
|
||||
** NOTE! The following LGPL license applies to the tevent
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/util/debug.h"
|
||||
#include "lib/util/select.h"
|
||||
#include <tevent.h>
|
||||
|
||||
#undef DBGC_CLASS
|
||||
#define DBGC_CLASS DBGC_TEVENT
|
||||
|
||||
#ifdef HAVE_GLIB
|
||||
#include <glib.h>
|
||||
#include "tevent_glib_glue.h"
|
||||
|
||||
struct fd_map {
|
||||
struct tevent_glib_glue *glue;
|
||||
int fd;
|
||||
struct tevent_fd *fd_event;
|
||||
};
|
||||
|
||||
struct tevent_glib_glue {
|
||||
/*
|
||||
* The tevent context we're feeding.
|
||||
*/
|
||||
struct tevent_context *ev;
|
||||
|
||||
/*
|
||||
* The glib gmain context we're polling and whether we're currently
|
||||
* owning it by virtue of g_main_context_acquire().
|
||||
*/
|
||||
GMainContext *gmain_ctx;
|
||||
bool gmain_owner;
|
||||
|
||||
/*
|
||||
* Set by samba_tevent_glib_glue_quit().
|
||||
*/
|
||||
bool quit;
|
||||
|
||||
/*
|
||||
* tevent trace callback and data we got from tevent_get_trace_callback()
|
||||
* before installing our own trace callback.
|
||||
*/
|
||||
tevent_trace_callback_t prev_tevent_trace_cb;
|
||||
void *prev_tevent_trace_data;
|
||||
|
||||
/*
|
||||
* Don't call tevent_glib_prepare() in the tevent tracepoint handler if
|
||||
* explicity told so. This is an optimisation for the case that glib
|
||||
* event sources are created from glib event callbacks.
|
||||
*/
|
||||
bool skip_glib_refresh;
|
||||
|
||||
/*
|
||||
* Used when acquiring the glib gmain context failed.
|
||||
*/
|
||||
struct tevent_timer *acquire_retry_timer;
|
||||
|
||||
/*
|
||||
* glib gmain context timeout and priority for the current event look
|
||||
* iteration. gtimeout is translated to a tevent timer event, unless it
|
||||
* is 0 which signals some event source is pending. In that case we
|
||||
* dispatch an immediate event. gpriority is ignored by us, just passed
|
||||
* to the glib relevant functions.
|
||||
*/
|
||||
gint gtimeout;
|
||||
gint gpriority;
|
||||
struct tevent_timer *timer;
|
||||
struct tevent_immediate *im;
|
||||
bool scheduled_im;
|
||||
|
||||
/*
|
||||
* glib gmain context fds returned from g_main_context_query(). These
|
||||
* get translated to tevent fd events.
|
||||
*/
|
||||
GPollFD *gpollfds;
|
||||
gint num_gpollfds;
|
||||
|
||||
/*
|
||||
* A copy of gpollfds and num_gpollfds from the previous event loop
|
||||
* iteration, used to detect changes in the set of fds.
|
||||
*/
|
||||
GPollFD *prev_gpollfds;
|
||||
gint num_prev_gpollfds;
|
||||
|
||||
/*
|
||||
* An array of pointers to fd_map's. The fd_map'd contain the tevent
|
||||
* event fd as well as a pointer to the corresponding glib GPollFD.
|
||||
*/
|
||||
struct fd_map **fd_map;
|
||||
size_t num_maps;
|
||||
};
|
||||
|
||||
static bool tevent_glib_prepare(struct tevent_glib_glue *glue);
|
||||
static bool tevent_glib_process(struct tevent_glib_glue *glue);
|
||||
static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue);
|
||||
static void tevent_glib_fd_handler(struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags,
|
||||
void *private_data);
|
||||
|
||||
typedef int (*gfds_cmp_cb)(const void *fd1, const void *fd2);
|
||||
typedef bool (*gfds_found_cb)(struct tevent_glib_glue *glue,
|
||||
const GPollFD *new,
|
||||
const GPollFD *old);
|
||||
typedef bool (*gfds_new_cb)(struct tevent_glib_glue *glue,
|
||||
const GPollFD *fd);
|
||||
typedef bool (*gfds_removed_cb)(struct tevent_glib_glue *glue,
|
||||
const GPollFD *fd);
|
||||
|
||||
/**
|
||||
* Compare two sorted GPollFD arrays
|
||||
*
|
||||
* For every element that exists in gfds and prev_gfds found_fn() is called.
|
||||
* For every element in gfds but not in prev_gfds, new_fn() is called.
|
||||
* For every element in prev_gfds but not in gfds removed_fn() is called.
|
||||
**/
|
||||
static bool cmp_gfds(struct tevent_glib_glue *glue,
|
||||
GPollFD *gfds,
|
||||
GPollFD *prev_gfds,
|
||||
size_t num_gfds,
|
||||
size_t num_prev_gfds,
|
||||
gfds_cmp_cb cmp_cb,
|
||||
gfds_found_cb found_cb,
|
||||
gfds_new_cb new_cb,
|
||||
gfds_removed_cb removed_cb)
|
||||
{
|
||||
bool ok;
|
||||
size_t i = 0, j = 0;
|
||||
int cmp;
|
||||
|
||||
while (i < num_gfds && j < num_prev_gfds) {
|
||||
cmp = cmp_cb(&gfds[i], &prev_gfds[j]);
|
||||
if (cmp == 0) {
|
||||
ok = found_cb(glue, &gfds[i], &prev_gfds[j]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
} else if (cmp < 0) {
|
||||
ok = new_cb(glue, &gfds[i]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
} else {
|
||||
ok = removed_cb(glue, &prev_gfds[j]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < num_gfds) {
|
||||
ok = new_cb(glue, &gfds[i++]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
while (j < num_prev_gfds) {
|
||||
ok = removed_cb(glue, &prev_gfds[j++]);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int glib_fd_cmp_func(const void *p1, const void *p2)
|
||||
{
|
||||
const GPollFD *lhs = p1;
|
||||
const GPollFD *rhs = p2;
|
||||
|
||||
if (lhs->fd < rhs->fd) {
|
||||
return -1;
|
||||
} else if (lhs->fd > rhs->fd) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We already have a tevent fd event for the glib GPollFD, but we may have to
|
||||
* update flags.
|
||||
*/
|
||||
static bool match_gfd_cb(struct tevent_glib_glue *glue,
|
||||
const GPollFD *new_gfd,
|
||||
const GPollFD *old_gfd)
|
||||
{
|
||||
size_t i;
|
||||
struct fd_map *fd_map = NULL;
|
||||
struct tevent_fd *fd_event = NULL;
|
||||
|
||||
if (new_gfd->events == old_gfd->events) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < glue->num_maps; i++) {
|
||||
if (glue->fd_map[i]->fd == new_gfd->fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == glue->num_maps) {
|
||||
DBG_ERR("match_gfd_cb: glib fd %d not in map\n", new_gfd->fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_map = glue->fd_map[i];
|
||||
if (fd_map == NULL) {
|
||||
DBG_ERR("fd_map for fd %d is NULL\n", new_gfd->fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_event = fd_map->fd_event;
|
||||
if (fd_event == NULL) {
|
||||
DBG_ERR("fd_event for fd %d is NULL\n", new_gfd->fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
tevent_fd_set_flags(fd_event, 0);
|
||||
|
||||
if (new_gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR)) {
|
||||
TEVENT_FD_READABLE(fd_event);
|
||||
}
|
||||
if (new_gfd->events & G_IO_OUT) {
|
||||
TEVENT_FD_WRITEABLE(fd_event);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool new_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
|
||||
{
|
||||
struct tevent_fd *fd_event = NULL;
|
||||
struct fd_map *fd_map = NULL;
|
||||
uint16_t events = 0;
|
||||
bool revent;
|
||||
bool wevent;
|
||||
|
||||
revent = (gfd->events & (G_IO_IN | G_IO_HUP | G_IO_ERR));
|
||||
wevent = (gfd->events & G_IO_OUT);
|
||||
if (revent) {
|
||||
events |= TEVENT_FD_READ;
|
||||
}
|
||||
if (wevent) {
|
||||
events |= TEVENT_FD_WRITE;
|
||||
}
|
||||
|
||||
glue->fd_map = talloc_realloc(glue,
|
||||
glue->fd_map,
|
||||
struct fd_map *,
|
||||
glue->num_maps + 1);
|
||||
if (glue->fd_map == NULL) {
|
||||
DBG_ERR("talloc_realloc failed\n");
|
||||
return false;
|
||||
}
|
||||
fd_map = talloc_zero(glue->fd_map, struct fd_map);
|
||||
if (fd_map == NULL) {
|
||||
DBG_ERR("talloc_realloc failed\n");
|
||||
return false;
|
||||
}
|
||||
glue->fd_map[glue->num_maps] = fd_map;
|
||||
glue->num_maps++;
|
||||
|
||||
fd_event = tevent_add_fd(glue->ev,
|
||||
glue->fd_map,
|
||||
gfd->fd,
|
||||
events,
|
||||
tevent_glib_fd_handler,
|
||||
fd_map);
|
||||
if (fd_event == NULL) {
|
||||
DBG_ERR("tevent_add_fd failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
*fd_map = (struct fd_map) {
|
||||
.glue = glue,
|
||||
.fd = gfd->fd,
|
||||
.fd_event = fd_event,
|
||||
};
|
||||
|
||||
DBG_DEBUG("added tevent_fd for glib fd %d\n", gfd->fd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove_gfd_cb(struct tevent_glib_glue *glue, const GPollFD *gfd)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < glue->num_maps; i++) {
|
||||
if (glue->fd_map[i]->fd == gfd->fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == glue->num_maps) {
|
||||
DBG_ERR("remove_gfd_cb: glib fd %d not in map\n", gfd->fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
TALLOC_FREE(glue->fd_map[i]->fd_event);
|
||||
TALLOC_FREE(glue->fd_map[i]);
|
||||
|
||||
if (i + 1 < glue->num_maps) {
|
||||
memmove(&glue->fd_map[i],
|
||||
&glue->fd_map[i+1],
|
||||
(glue->num_maps - (i + 1)) * sizeof(struct fd_map *));
|
||||
}
|
||||
|
||||
glue->fd_map = talloc_realloc(glue,
|
||||
glue->fd_map,
|
||||
struct fd_map *,
|
||||
glue->num_maps - 1);
|
||||
if (glue->num_maps > 0 && glue->fd_map == NULL) {
|
||||
DBG_ERR("talloc_realloc failed\n");
|
||||
return false;
|
||||
}
|
||||
glue->num_maps--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static short gpoll_to_poll_event(gushort gevent)
|
||||
{
|
||||
short pevent = 0;
|
||||
|
||||
if (gevent & G_IO_IN) {
|
||||
pevent |= POLLIN;
|
||||
}
|
||||
if (gevent & G_IO_OUT) {
|
||||
pevent |= POLLOUT;
|
||||
}
|
||||
if (gevent & G_IO_HUP) {
|
||||
pevent |= POLLHUP;
|
||||
}
|
||||
if (gevent & G_IO_ERR) {
|
||||
pevent |= POLLERR;
|
||||
}
|
||||
|
||||
return pevent;
|
||||
}
|
||||
|
||||
static gushort poll_to_gpoll_event(short pevent)
|
||||
{
|
||||
gushort gevent = 0;
|
||||
|
||||
if (pevent & POLLIN) {
|
||||
gevent |= G_IO_IN;
|
||||
}
|
||||
if (pevent & POLLOUT) {
|
||||
gevent |= G_IO_OUT;
|
||||
}
|
||||
if (pevent & POLLHUP) {
|
||||
gevent |= G_IO_HUP;
|
||||
}
|
||||
if (pevent & POLLERR) {
|
||||
gevent |= G_IO_ERR;
|
||||
}
|
||||
|
||||
return gevent;
|
||||
}
|
||||
|
||||
static void tevent_glib_fd_handler(struct tevent_context *ev,
|
||||
struct tevent_fd *fde,
|
||||
uint16_t flags,
|
||||
void *private_data)
|
||||
{
|
||||
struct fd_map *fd_map = talloc_get_type_abort(
|
||||
private_data, struct fd_map);
|
||||
struct tevent_glib_glue *glue = NULL;
|
||||
GPollFD *gpollfd = NULL;
|
||||
struct pollfd fd;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
glue = fd_map->glue;
|
||||
|
||||
for (i = 0; i < glue->num_gpollfds; i++) {
|
||||
if (glue->gpollfds[i].fd != fd_map->fd) {
|
||||
continue;
|
||||
}
|
||||
gpollfd = &glue->gpollfds[i];
|
||||
break;
|
||||
}
|
||||
if (gpollfd == NULL) {
|
||||
DBG_ERR("No gpollfd for fd_map [%p] fd [%d]\n",
|
||||
fd_map, fd_map->fd);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* We have to poll() the fd to get the correct fd event for glib. tevent
|
||||
* only tells us about readable/writable in flags, but we need the full
|
||||
* glory for glib.
|
||||
*/
|
||||
|
||||
fd = (struct pollfd) {
|
||||
.fd = gpollfd->fd,
|
||||
.events = gpoll_to_poll_event(gpollfd->events),
|
||||
};
|
||||
|
||||
ret = sys_poll_intr(&fd, 1, 0);
|
||||
if (ret == -1) {
|
||||
DBG_ERR("poll: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gpollfd->revents = poll_to_gpoll_event(fd.revents);
|
||||
|
||||
tevent_glib_process(glue);
|
||||
return;
|
||||
}
|
||||
|
||||
static void tevent_glib_timer_handler(struct tevent_context *ev,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *private_data)
|
||||
{
|
||||
struct tevent_glib_glue *glue = talloc_get_type_abort(
|
||||
private_data, struct tevent_glib_glue);
|
||||
|
||||
glue->timer = NULL;
|
||||
tevent_glib_process(glue);
|
||||
return;
|
||||
}
|
||||
|
||||
static void tevent_glib_im_handler(struct tevent_context *ev,
|
||||
struct tevent_immediate *im,
|
||||
void *private_data)
|
||||
{
|
||||
struct tevent_glib_glue *glue = talloc_get_type_abort(
|
||||
private_data, struct tevent_glib_glue);
|
||||
|
||||
glue->scheduled_im = false;
|
||||
tevent_glib_process(glue);
|
||||
return;
|
||||
}
|
||||
|
||||
static bool save_current_fdset(struct tevent_glib_glue *glue)
|
||||
{
|
||||
/*
|
||||
* Save old glib fds. We only grow the prev array.
|
||||
*/
|
||||
|
||||
if (glue->num_prev_gpollfds < glue->num_gpollfds) {
|
||||
glue->prev_gpollfds = talloc_realloc(glue,
|
||||
glue->prev_gpollfds,
|
||||
GPollFD,
|
||||
glue->num_gpollfds);
|
||||
if (glue->prev_gpollfds == NULL) {
|
||||
DBG_ERR("talloc_realloc failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
glue->num_prev_gpollfds = glue->num_gpollfds;
|
||||
if (glue->num_gpollfds > 0) {
|
||||
memcpy(glue->prev_gpollfds, glue->gpollfds,
|
||||
sizeof(GPollFD) * glue->num_gpollfds);
|
||||
memset(glue->gpollfds, 0, sizeof(GPollFD) * glue->num_gpollfds);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_glib_fds_and_timeout(struct tevent_glib_glue *glue)
|
||||
{
|
||||
bool ok;
|
||||
gint num_fds;
|
||||
|
||||
ok = save_current_fdset(glue);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
num_fds = g_main_context_query(glue->gmain_ctx,
|
||||
glue->gpriority,
|
||||
&glue->gtimeout,
|
||||
glue->gpollfds,
|
||||
glue->num_gpollfds);
|
||||
if (num_fds == glue->num_gpollfds) {
|
||||
break;
|
||||
}
|
||||
glue->gpollfds = talloc_realloc(glue,
|
||||
glue->gpollfds,
|
||||
GPollFD,
|
||||
num_fds);
|
||||
if (num_fds > 0 && glue->gpollfds == NULL) {
|
||||
DBG_ERR("talloc_realloc failed\n");
|
||||
return false;
|
||||
}
|
||||
glue->num_gpollfds = num_fds;
|
||||
};
|
||||
|
||||
if (glue->num_gpollfds > 0) {
|
||||
qsort(glue->gpollfds,
|
||||
num_fds,
|
||||
sizeof(GPollFD),
|
||||
glib_fd_cmp_func);
|
||||
}
|
||||
|
||||
DBG_DEBUG("num fds: %d, timeout: %d ms\n",
|
||||
num_fds, glue->gtimeout);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tevent_glib_update_events(struct tevent_glib_glue *glue)
|
||||
{
|
||||
uint64_t microsec;
|
||||
struct timeval tv;
|
||||
bool ok;
|
||||
|
||||
ok = cmp_gfds(glue,
|
||||
glue->gpollfds,
|
||||
glue->prev_gpollfds,
|
||||
glue->num_gpollfds,
|
||||
glue->num_prev_gpollfds,
|
||||
glib_fd_cmp_func,
|
||||
match_gfd_cb,
|
||||
new_gfd_cb,
|
||||
remove_gfd_cb);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TALLOC_FREE(glue->timer);
|
||||
|
||||
if (glue->gtimeout == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (glue->gtimeout == 0) {
|
||||
/*
|
||||
* glue->gtimeout is 0 if g_main_context_query() returned
|
||||
* timeout=0. That means there are pending events ready to be
|
||||
* dispatched. We only want to run one event handler per loop
|
||||
* iteration, so we schedule an immediate event to run it in the
|
||||
* next iteration.
|
||||
*/
|
||||
if (glue->scheduled_im) {
|
||||
return true;
|
||||
}
|
||||
tevent_schedule_immediate(glue->im,
|
||||
glue->ev,
|
||||
tevent_glib_im_handler,
|
||||
glue);
|
||||
glue->scheduled_im = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
microsec = glue->gtimeout * 1000;
|
||||
tv = tevent_timeval_current_ofs(microsec / 1000000,
|
||||
microsec % 1000000);
|
||||
|
||||
glue->timer = tevent_add_timer(glue->ev,
|
||||
glue,
|
||||
tv,
|
||||
tevent_glib_timer_handler,
|
||||
glue);
|
||||
if (glue->timer == NULL) {
|
||||
DBG_ERR("tevent_add_timer failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tevent_glib_retry_timer(struct tevent_context *ev,
|
||||
struct tevent_timer *te,
|
||||
struct timeval current_time,
|
||||
void *private_data)
|
||||
{
|
||||
struct tevent_glib_glue *glue = talloc_get_type_abort(
|
||||
private_data, struct tevent_glib_glue);
|
||||
|
||||
glue->acquire_retry_timer = NULL;
|
||||
(void)tevent_glib_prepare(glue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch glib event sources and add them to tevent
|
||||
*
|
||||
* Fetch glib event sources and attach corresponding tevent events to our tevent
|
||||
* context. get_glib_fds_and_timeout() gets the relevant glib event sources: the
|
||||
* set of active fds and the next timer. tevent_glib_update_events() then
|
||||
* translates those to tevent and creates tevent events.
|
||||
*
|
||||
* When called, the thread must NOT be the owner to the glib main
|
||||
* context. tevent_glib_prepare() is either the first function when the
|
||||
* tevent_glib_glue is created, or after tevent_glib_process() has been called
|
||||
* to process pending event, which will have ceased ownership.
|
||||
**/
|
||||
static bool tevent_glib_prepare(struct tevent_glib_glue *glue)
|
||||
{
|
||||
bool ok;
|
||||
gboolean gok;
|
||||
|
||||
if (glue->quit) {
|
||||
/* Set via samba_tevent_glib_glue_quit() */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (glue->acquire_retry_timer != NULL) {
|
||||
/*
|
||||
* We're still waiting on the below g_main_context_acquire() to
|
||||
* succeed, just return.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
if (glue->gmain_owner) {
|
||||
g_main_context_release(glue->gmain_ctx);
|
||||
glue->gmain_owner = false;
|
||||
}
|
||||
|
||||
gok = g_main_context_acquire(glue->gmain_ctx);
|
||||
if (!gok) {
|
||||
DBG_ERR("couldn't acquire g_main_context\n");
|
||||
|
||||
/*
|
||||
* Ensure no tevent event fires while we're not the gmain
|
||||
* context owner. The event handler would call
|
||||
* tevent_glib_process() and that expects being the owner of the
|
||||
* context.
|
||||
*/
|
||||
ok = tevent_glib_glue_reinit(glue);
|
||||
if (!ok) {
|
||||
DBG_ERR("tevent_glib_glue_reinit failed\n");
|
||||
samba_tevent_glib_glue_quit(glue);
|
||||
return false;
|
||||
}
|
||||
|
||||
glue->acquire_retry_timer = tevent_add_timer(
|
||||
glue->ev,
|
||||
glue,
|
||||
tevent_timeval_current_ofs(0, 1000),
|
||||
tevent_glib_retry_timer,
|
||||
glue);
|
||||
if (glue->acquire_retry_timer == NULL) {
|
||||
DBG_ERR("tevent_add_timer failed\n");
|
||||
samba_tevent_glib_glue_quit(glue);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
glue->gmain_owner = true;
|
||||
|
||||
/*
|
||||
* Discard "ready" return value from g_main_context_prepare(). We don't
|
||||
* want to dispatch events here, thats only done in from the tevent loop.
|
||||
*/
|
||||
(void)g_main_context_prepare(glue->gmain_ctx, &glue->gpriority);
|
||||
|
||||
ok = get_glib_fds_and_timeout(glue);
|
||||
if (!ok) {
|
||||
DBG_ERR("get_glib_fds_and_timeout failed\n");
|
||||
samba_tevent_glib_glue_quit(glue);
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = tevent_glib_update_events(glue);
|
||||
if (!ok) {
|
||||
DBG_ERR("tevent_glib_update_events failed\n");
|
||||
samba_tevent_glib_glue_quit(glue);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process pending glib events
|
||||
*
|
||||
* tevent_glib_process() gets called to process pending glib events via
|
||||
* g_main_context_check() and then g_main_context_dispatch().
|
||||
*
|
||||
* After pending event handlers are dispatched, we rearm the glib glue event
|
||||
* handlers in tevent by calling tevent_glib_prepare().
|
||||
*
|
||||
* When tevent_glib_process() is called the thread must own the glib
|
||||
* gmain_ctx. That is achieved by tevent_glib_prepare() being the only function
|
||||
* that acuires context ownership.
|
||||
*
|
||||
* To give other threads that are blocked on g_main_context_acquire(gmain_ctx) a
|
||||
* chance to acquire context ownership (eg needed to attach event sources), we
|
||||
* release context ownership before calling tevent_glib_prepare() which will
|
||||
* acquire it again.
|
||||
*/
|
||||
static bool tevent_glib_process(struct tevent_glib_glue *glue)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
DBG_DEBUG("tevent_glib_process\n");
|
||||
|
||||
/*
|
||||
* Ignore the "sources_ready" return from g_main_context_check(). glib
|
||||
* itself also ignores it in g_main_context_iterate(). In theory only
|
||||
* calling g_main_context_dispatch() if g_main_context_check() returns
|
||||
* true should work, but older glib versions had a bug where
|
||||
* g_main_context_check() returns false even though events are pending.
|
||||
*
|
||||
* https://bugzilla.gnome.org/show_bug.cgi?id=11059
|
||||
*/
|
||||
(void)g_main_context_check(glue->gmain_ctx,
|
||||
glue->gpriority,
|
||||
glue->gpollfds,
|
||||
glue->num_gpollfds);
|
||||
|
||||
g_main_context_dispatch(glue->gmain_ctx);
|
||||
|
||||
ok = tevent_glib_prepare(glue);
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
glue->skip_glib_refresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tevent_glib_glue_trace_callback(enum tevent_trace_point point,
|
||||
void *private_data)
|
||||
{
|
||||
struct tevent_glib_glue *glue = talloc_get_type_abort(
|
||||
private_data, struct tevent_glib_glue);
|
||||
|
||||
if (point == TEVENT_TRACE_AFTER_LOOP_ONCE) {
|
||||
if (!glue->skip_glib_refresh) {
|
||||
tevent_glib_prepare(glue);
|
||||
}
|
||||
glue->skip_glib_refresh = false;
|
||||
}
|
||||
|
||||
/* chain previous handler */
|
||||
if (glue->prev_tevent_trace_cb != NULL) {
|
||||
glue->prev_tevent_trace_cb(point, glue->prev_tevent_trace_data);
|
||||
}
|
||||
}
|
||||
|
||||
static void tevent_glib_glue_cleanup(struct tevent_glib_glue *glue)
|
||||
{
|
||||
size_t n = talloc_array_length(glue->fd_map);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
TALLOC_FREE(glue->fd_map[i]->fd_event);
|
||||
TALLOC_FREE(glue->fd_map[i]);
|
||||
}
|
||||
|
||||
tevent_set_trace_callback(glue->ev,
|
||||
glue->prev_tevent_trace_cb,
|
||||
glue->prev_tevent_trace_data);
|
||||
glue->prev_tevent_trace_cb = NULL;
|
||||
glue->prev_tevent_trace_data = NULL;
|
||||
|
||||
TALLOC_FREE(glue->fd_map);
|
||||
glue->num_maps = 0;
|
||||
|
||||
TALLOC_FREE(glue->gpollfds);
|
||||
glue->num_gpollfds = 0;
|
||||
|
||||
TALLOC_FREE(glue->prev_gpollfds);
|
||||
glue->num_prev_gpollfds = 0;
|
||||
|
||||
TALLOC_FREE(glue->timer);
|
||||
TALLOC_FREE(glue->acquire_retry_timer);
|
||||
TALLOC_FREE(glue->im);
|
||||
|
||||
/*
|
||||
* These are not really needed, but let's wipe the slate clean.
|
||||
*/
|
||||
glue->skip_glib_refresh = false;
|
||||
glue->gtimeout = 0;
|
||||
glue->gpriority = 0;
|
||||
}
|
||||
|
||||
static bool tevent_glib_glue_reinit(struct tevent_glib_glue *glue)
|
||||
{
|
||||
tevent_glib_glue_cleanup(glue);
|
||||
|
||||
glue->im = tevent_create_immediate(glue);
|
||||
if (glue->im == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tevent_get_trace_callback(glue->ev,
|
||||
&glue->prev_tevent_trace_cb,
|
||||
&glue->prev_tevent_trace_data);
|
||||
tevent_set_trace_callback(glue->ev,
|
||||
tevent_glib_glue_trace_callback,
|
||||
glue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
|
||||
{
|
||||
tevent_glib_glue_cleanup(glue);
|
||||
glue->quit = true;
|
||||
return;
|
||||
}
|
||||
|
||||
struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
GMainContext *gmain_ctx)
|
||||
{
|
||||
bool ok;
|
||||
struct tevent_glib_glue *glue = NULL;
|
||||
|
||||
glue = talloc_zero(mem_ctx, struct tevent_glib_glue);
|
||||
if (glue == NULL) {
|
||||
DBG_ERR("talloc_zero failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*glue = (struct tevent_glib_glue) {
|
||||
.ev = ev,
|
||||
.gmain_ctx = gmain_ctx,
|
||||
};
|
||||
|
||||
glue->im = tevent_create_immediate(glue);
|
||||
|
||||
tevent_get_trace_callback(glue->ev,
|
||||
&glue->prev_tevent_trace_cb,
|
||||
&glue->prev_tevent_trace_data);
|
||||
tevent_set_trace_callback(glue->ev,
|
||||
tevent_glib_glue_trace_callback,
|
||||
glue);
|
||||
|
||||
ok = tevent_glib_prepare(glue);
|
||||
if (!ok) {
|
||||
TALLOC_FREE(glue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return glue;
|
||||
}
|
||||
|
||||
#else /* HAVE_GLIB */
|
||||
|
||||
struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
GMainContext *gmain_ctx)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* HAVE_GLIB */
|
68
source3/lib/tevent_glib_glue.h
Normal file
68
source3/lib/tevent_glib_glue.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
Poll glib event loop from tevent
|
||||
|
||||
Copyright (C) Ralph Boehme 2016
|
||||
|
||||
** NOTE! The following LGPL license applies to the tevent
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _TEVENT_GLIB_GLUE_H
|
||||
#define _TEVENT_GLIB_GLUE_H
|
||||
|
||||
#include <talloc.h>
|
||||
#include <tevent.h>
|
||||
|
||||
/**
|
||||
* @brief Add a glib GmainContext to a tevent context
|
||||
*
|
||||
* tevent will poll the glib event sources and run handlers for
|
||||
* pending events as detailed in the glib documentation:
|
||||
*
|
||||
* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
|
||||
*
|
||||
* If tevent was built without glib support, this function will always return
|
||||
* NULL with an error number ENOSYS.
|
||||
*
|
||||
* @param[in] mem_ctx Memory context to use
|
||||
*
|
||||
* @param[in] ev Event context to use
|
||||
*
|
||||
* @param[in] gmain_ctx GMainContext that will be added to tevent
|
||||
*
|
||||
* @return A handle on the glue context that binds the
|
||||
* the GMainContext to tevent. Pass the glue handle to
|
||||
* tevent_glib_glue_quit() in a callback when you want
|
||||
* stop processing glib events.
|
||||
* You must not call talloc_free() on the handle while
|
||||
* the loop is still in use and attached to tevent.
|
||||
*/
|
||||
struct tevent_glib_glue *samba_tevent_glib_glue_create(TALLOC_CTX *mem_ctx,
|
||||
struct tevent_context *ev,
|
||||
GMainContext *gmain_ctx);
|
||||
|
||||
/**
|
||||
* @brief Stop polling a GMainContext
|
||||
*
|
||||
* Used in a callback when you want to stop processing glib events.
|
||||
*
|
||||
* @param[in] glue And tevent_glib_glue handle
|
||||
*/
|
||||
void samba_tevent_glib_glue_quit(struct tevent_glib_glue *glue);
|
||||
|
||||
#endif
|
@ -1637,6 +1637,16 @@ main() {
|
||||
else:
|
||||
conf.fatal('AFS headers not available, but --with-fake-kaserver was specified')
|
||||
|
||||
if conf.CHECK_CFG(package='glib-2.0',
|
||||
args='--cflags --libs',
|
||||
msg='Checking for glib-2.0',
|
||||
uselib_store="GLIB-2.0"):
|
||||
if (conf.CHECK_HEADERS('glib.h', lib='glib-2.0') and conf.CHECK_LIB('glib-2.0', shlib=True)):
|
||||
conf.DEFINE('HAVE_GLIB', 1)
|
||||
|
||||
if conf.CONFIG_SET('HAVE_GLIB'):
|
||||
conf.DEFINE('WITH_TEVENT_GLIB_GLUE', '1')
|
||||
|
||||
conf.env['libtracker']=''
|
||||
tracker_versions = ['2.0', '1.0', '0.16', '0.14']
|
||||
|
||||
@ -1677,6 +1687,9 @@ main() {
|
||||
if not conf.CONFIG_SET('HAVE_TRACKER'):
|
||||
conf.fatal('Missing Gnome Tracker development files')
|
||||
|
||||
if not conf.CONFIG_SET('HAVE_GLIB'):
|
||||
conf.fatal('Missing glib-2.0 development files')
|
||||
|
||||
Logs.info("building with Spotlight support")
|
||||
default_static_modules.extend(TO_LIST('rpc_mdssvc_module'))
|
||||
conf.DEFINE('WITH_SPOTLIGHT', '1')
|
||||
|
@ -1074,6 +1074,12 @@ bld.SAMBA3_SUBSYSTEM('SPOOLSSD',
|
||||
RPC_SOCK_HELPER
|
||||
''')
|
||||
|
||||
bld.SAMBA3_SUBSYSTEM('tevent-glib-glue',
|
||||
source='lib/tevent_glib_glue.c',
|
||||
deps='glib-2.0',
|
||||
enabled=bld.CONFIG_SET('WITH_TEVENT_GLIB_GLUE'),
|
||||
)
|
||||
|
||||
########################## BINARIES #################################
|
||||
|
||||
bld.SAMBA3_BINARY('smbd/smbd',
|
||||
|
Loading…
x
Reference in New Issue
Block a user