From nobody Sun Feb 8 13:09:43 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 205.139.110.120 as permitted sender) client-ip=205.139.110.120; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-1.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 205.139.110.120 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) by mx.zohomail.com with SMTPS id 1580923162783106.76837712050292; Wed, 5 Feb 2020 09:19:22 -0800 (PST) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-42-kqEvH1wbO7i7W22sE6G1qg-1; Wed, 05 Feb 2020 12:19:17 -0500 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 34EF11005F77; Wed, 5 Feb 2020 17:19:12 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 0337F790E4; Wed, 5 Feb 2020 17:19:12 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id B330818089CD; Wed, 5 Feb 2020 17:19:11 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.11]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 015HJ67u015905 for ; Wed, 5 Feb 2020 12:19:06 -0500 Received: by smtp.corp.redhat.com (Postfix) id D8B4B857AD; Wed, 5 Feb 2020 17:19:06 +0000 (UTC) Received: from domokun.gsslab.fab.redhat.com (unknown [10.33.8.110]) by smtp.corp.redhat.com (Postfix) with ESMTP id 283D281213; Wed, 5 Feb 2020 17:19:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1580923161; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=UCa2tDDdocReQXht18ZvG8641cRe+zKkw+BUXBh7nxk=; b=ii47MlDCrQPUTs5NpcGTujbBDtIktxCAmi5fiL5GiXu6zvvdn4l0SUToBznNOYHDpBk5vp 7AbZneTLP6vcw6YrXy9KIeageAh/PzeKx638AzzNqTHaXlBGeLmkLmW1MDAxgOl9DWRb7N 665K9J+BeXn2PrpwBMTJoQtGG5sP6Vo= From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= To: libvir-list@redhat.com Subject: [libvirt PATCH v3 4/7] util: import an event loop impl based on GMainContext Date: Wed, 5 Feb 2020 17:18:55 +0000 Message-Id: <20200205171858.2694632-5-berrange@redhat.com> In-Reply-To: <20200205171858.2694632-1-berrange@redhat.com> References: <20200205171858.2694632-1-berrange@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.11 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-MC-Unique: kqEvH1wbO7i7W22sE6G1qg-1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" The libvirt-glib project has provided a GMainContext based event loop impl for applications. This imports it and sets it up for use by libvirt as the primary event loop. This remains a private impl detail of libvirt. IOW, applications must *NOT* assume that a call to "virEventRegisterDefaultImpl" results in a GLib based event loop. They should continue to use the libvirt-glib API gvir_event_register() if they explicitly want to guarantee a GLib event loop. This follows the general principle that the libvirt public API should not expose the fact that GLib is being used internally. Signed-off-by: Daniel P. Berrang=C3=A9 Reviewed-by: Pavel Hrdina --- build-aux/syntax-check.mk | 2 +- src/libvirt_private.syms | 5 + src/libvirt_probes.d | 14 ++ src/util/Makefile.inc.am | 2 + src/util/vireventglib.c | 499 ++++++++++++++++++++++++++++++++++++++ src/util/vireventglib.h | 28 +++ 6 files changed, 549 insertions(+), 1 deletion(-) create mode 100644 src/util/vireventglib.c create mode 100644 src/util/vireventglib.h diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index d43fa501aa..3a36c3bece 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -2176,7 +2176,7 @@ exclude_file_name_regexp--sc_avoid_write =3D \ =20 exclude_file_name_regexp--sc_bindtextdomain =3D .* =20 -exclude_file_name_regexp--sc_gettext_init =3D ^((tests|examples)/|tools/vi= rt-login-shell.c) +exclude_file_name_regexp--sc_gettext_init =3D ^((tests|examples)/|tools/vi= rt-login-shell.c|src/util/vireventglib\.c) =20 exclude_file_name_regexp--sc_copyright_format =3D \ ^build-aux/syntax-check\.mk$$ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ddf1fae71f..417ee05a24 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1933,6 +1933,11 @@ virSetErrorLogPriorityFunc; virStrerror; =20 =20 +# util/vireventglib.h +virEventGLibRegister; +virEventGLibRunOnce; + + # util/vireventpoll.h virEventPollAddHandle; virEventPollAddTimeout; diff --git a/src/libvirt_probes.d b/src/libvirt_probes.d index 2a8d600c0e..a1edb4116d 100644 --- a/src/libvirt_probes.d +++ b/src/libvirt_probes.d @@ -15,6 +15,20 @@ provider libvirt { =20 probe event_poll_run(int nfds, int timeout); =20 + # file: src/util/vireventglib.c + # prefix: event_glib + probe event_glib_add_handle(int watch, int fd, int events, void *cb, void= *opaque, void *ff); + probe event_glib_update_handle(int watch, int events); + probe event_glib_remove_handle(int watch); + probe event_glib_remove_handle_idle(int watch, void *ff, void *opaque); + probe event_glib_dispatch_handle(int watch, int events, void *cb, void *o= paque); + + probe event_glib_add_timeout(int timer, int frequency, void *cb, void *op= aque, void *ff); + probe event_glib_update_timeout(int timer, int frequency); + probe event_glib_remove_timeout(int timer); + probe event_glib_remove_timeout_idle(int timer, void *ff, void *opaque); + probe event_glib_dispatch_timeout(int timer, void *cb, void *opaque); + # file: src/util/virdbus.c # prefix: dbus probe dbus_method_call(const char *interface, const char *member, const c= har *object, const char *destination); diff --git a/src/util/Makefile.inc.am b/src/util/Makefile.inc.am index a51cba736b..3a42543a5e 100644 --- a/src/util/Makefile.inc.am +++ b/src/util/Makefile.inc.am @@ -61,6 +61,8 @@ UTIL_SOURCES =3D \ util/virerrorpriv.h \ util/virevent.c \ util/virevent.h \ + util/vireventglib.c \ + util/vireventglib.h \ util/vireventglibwatch.c \ util/vireventglibwatch.h \ util/vireventpoll.c \ diff --git a/src/util/vireventglib.c b/src/util/vireventglib.c new file mode 100644 index 0000000000..803332a6f8 --- /dev/null +++ b/src/util/vireventglib.c @@ -0,0 +1,499 @@ +/* + * vireventglib.c: GMainContext based event loop + * + * Copyright (C) 2008 Daniel P. Berrange + * Copyright (C) 2010-2019 Red Hat, Inc. + * + * 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 2.1 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 + * . + */ + +#include + +#include +#include +#include + +#include "vireventglib.h" +#include "vireventglibwatch.h" +#include "virerror.h" +#include "virlog.h" +#include "virprobe.h" + +#ifdef G_OS_WIN32 +# include +#endif + +#define VIR_FROM_THIS VIR_FROM_EVENT + +VIR_LOG_INIT("util.eventglib"); + +struct virEventGLibHandle +{ + int watch; + int fd; + int events; + int removed; + guint source; + virEventHandleCallback cb; + void *opaque; + virFreeCallback ff; +}; + +struct virEventGLibTimeout +{ + int timer; + int interval; + int removed; + guint source; + virEventTimeoutCallback cb; + void *opaque; + virFreeCallback ff; +}; + +static GMutex *eventlock; + +static int nextwatch =3D 1; +static GPtrArray *handles; + +static int nexttimer =3D 1; +static GPtrArray *timeouts; + +static GIOCondition +virEventGLibEventsToCondition(int events) +{ + GIOCondition cond =3D 0; + if (events & VIR_EVENT_HANDLE_READABLE) + cond |=3D G_IO_IN; + if (events & VIR_EVENT_HANDLE_WRITABLE) + cond |=3D G_IO_OUT; + if (events & VIR_EVENT_HANDLE_ERROR) + cond |=3D G_IO_ERR; + if (events & VIR_EVENT_HANDLE_HANGUP) + cond |=3D G_IO_HUP; + return cond; +} + +static int +virEventGLibConditionToEvents(GIOCondition cond) +{ + int events =3D 0; + if (cond & G_IO_IN) + events |=3D VIR_EVENT_HANDLE_READABLE; + if (cond & G_IO_OUT) + events |=3D VIR_EVENT_HANDLE_WRITABLE; + if (cond & G_IO_ERR) + events |=3D VIR_EVENT_HANDLE_ERROR; + if (cond & G_IO_NVAL) /* Treat NVAL as error, since libvirt doesn't di= stinguish */ + events |=3D VIR_EVENT_HANDLE_ERROR; + if (cond & G_IO_HUP) + events |=3D VIR_EVENT_HANDLE_HANGUP; + return events; +} + +static gboolean +virEventGLibHandleDispatch(int fd G_GNUC_UNUSED, + GIOCondition condition, + gpointer opaque) +{ + struct virEventGLibHandle *data =3D opaque; + int events =3D virEventGLibConditionToEvents(condition); + + VIR_DEBUG("Dispatch handler data=3D%p watch=3D%d fd=3D%d events=3D%d o= paque=3D%p", + data, data->watch, data->fd, events, data->opaque); + + PROBE(EVENT_GLIB_DISPATCH_HANDLE, + "watch=3D%d events=3D%d cb=3D%p opaque=3D%p", + data->watch, events, data->cb, data->opaque); + + (data->cb)(data->watch, data->fd, events, data->opaque); + + return TRUE; +} + + +static int +virEventGLibHandleAdd(int fd, + int events, + virEventHandleCallback cb, + void *opaque, + virFreeCallback ff) +{ + struct virEventGLibHandle *data; + GIOCondition cond =3D virEventGLibEventsToCondition(events); + int ret; + + g_mutex_lock(eventlock); + + data =3D g_new0(struct virEventGLibHandle, 1); + + data->watch =3D nextwatch++; + data->fd =3D fd; + data->events =3D events; + data->cb =3D cb; + data->opaque =3D opaque; + data->ff =3D ff; + + VIR_DEBUG("Add handle data=3D%p watch=3D%d fd=3D%d events=3D%d opaque= =3D%p", + data, data->watch, data->fd, events, data->opaque); + + if (events !=3D 0) { + data->source =3D virEventGLibAddSocketWatch( + fd, cond, NULL, virEventGLibHandleDispatch, data, NULL); + } + + g_ptr_array_add(handles, data); + + ret =3D data->watch; + + PROBE(EVENT_GLIB_ADD_HANDLE, + "watch=3D%d fd=3D%d events=3D%d cb=3D%p opaque=3D%p ff=3D%p", + ret, fd, events, cb, opaque, ff); + g_mutex_unlock(eventlock); + + return ret; +} + +static struct virEventGLibHandle * +virEventGLibHandleFind(int watch) +{ + guint i; + + for (i =3D 0; i < handles->len; i++) { + struct virEventGLibHandle *h =3D g_ptr_array_index(handles, i); + + if (h =3D=3D NULL) { + g_warn_if_reached(); + continue; + } + + if ((h->watch =3D=3D watch) && !h->removed) + return h; + } + + return NULL; +} + +static void +virEventGLibHandleUpdate(int watch, + int events) +{ + struct virEventGLibHandle *data; + + PROBE(EVENT_GLIB_UPDATE_HANDLE, + "watch=3D%d events=3D%d", + watch, events); + g_mutex_lock(eventlock); + + data =3D virEventGLibHandleFind(watch); + if (!data) { + VIR_DEBUG("Update for missing handle watch=3D%d", watch); + goto cleanup; + } + + VIR_DEBUG("Update handle data=3D%p watch=3D%d fd=3D%d events=3D%d", + data, watch, data->fd, events); + + if (events !=3D 0) { + GIOCondition cond =3D virEventGLibEventsToCondition(events); + if (events =3D=3D data->events) + goto cleanup; + + if (data->source !=3D 0) { + VIR_DEBUG("Removed old handle watch=3D%d", data->source); + g_source_remove(data->source); + } + + data->source =3D virEventGLibAddSocketWatch( + data->fd, cond, NULL, virEventGLibHandleDispatch, data, NULL); + + data->events =3D events; + VIR_DEBUG("Added new handle watch=3D%d", data->source); + } else { + if (data->source =3D=3D 0) + goto cleanup; + + VIR_DEBUG("Removed old handle watch=3D%d", data->source); + g_source_remove(data->source); + data->source =3D 0; + data->events =3D 0; + } + + cleanup: + g_mutex_unlock(eventlock); +} + +static gboolean +virEventGLibHandleRemoveIdle(gpointer data) +{ + struct virEventGLibHandle *h =3D data; + + PROBE(EVENT_GLIB_REMOVE_HANDLE_IDLE, + "watch=3D%d ff=3D%p opaque=3D%p", + h->watch, h->ff, h->opaque); + if (h->ff) + (h->ff)(h->opaque); + + g_mutex_lock(eventlock); + g_ptr_array_remove_fast(handles, h); + g_mutex_unlock(eventlock); + + return FALSE; +} + +static int +virEventGLibHandleRemove(int watch) +{ + struct virEventGLibHandle *data; + int ret =3D -1; + + PROBE(EVENT_GLIB_REMOVE_HANDLE, + "watch=3D%d", + watch); + g_mutex_lock(eventlock); + + data =3D virEventGLibHandleFind(watch); + if (!data) { + VIR_DEBUG("Remove of missing handle watch=3D%d", watch); + goto cleanup; + } + + VIR_DEBUG("Remove handle data=3D%p watch=3D%d fd=3D%d", + data, watch, data->fd); + + if (data->source !=3D 0) { + g_source_remove(data->source); + data->source =3D 0; + data->events =3D 0; + } + + /* since the actual watch deletion is done asynchronously, a handleUpd= ate call may + * reschedule the watch before it's fully deleted, that's why we need = to mark it as + * 'removed' to prevent reuse + */ + data->removed =3D TRUE; + g_idle_add(virEventGLibHandleRemoveIdle, data); + + ret =3D 0; + + cleanup: + g_mutex_unlock(eventlock); + return ret; +} + + +static gboolean +virEventGLibTimeoutDispatch(void *opaque) +{ + struct virEventGLibTimeout *data =3D opaque; + + VIR_DEBUG("Dispatch timeout data=3D%p cb=3D%p timer=3D%d opaque=3D%p", + data, data->cb, data->timer, data->opaque); + + PROBE(EVENT_GLIB_DISPATCH_TIMEOUT, + "timer=3D%d cb=3D%p opaque=3D%p", + data->timer, data->cb, data->opaque); + (data->cb)(data->timer, data->opaque); + + return TRUE; +} + +static int +virEventGLibTimeoutAdd(int interval, + virEventTimeoutCallback cb, + void *opaque, + virFreeCallback ff) +{ + struct virEventGLibTimeout *data; + int ret; + + g_mutex_lock(eventlock); + + data =3D g_new0(struct virEventGLibTimeout, 1); + data->timer =3D nexttimer++; + data->interval =3D interval; + data->cb =3D cb; + data->opaque =3D opaque; + data->ff =3D ff; + if (interval >=3D 0) + data->source =3D g_timeout_add(interval, + virEventGLibTimeoutDispatch, + data); + + g_ptr_array_add(timeouts, data); + + VIR_DEBUG("Add timeout data=3D%p interval=3D%d ms cb=3D%p opaque=3D%p = timer=3D%d", + data, interval, cb, opaque, data->timer); + + ret =3D data->timer; + + PROBE(EVENT_GLIB_ADD_TIMEOUT, + "timer=3D%d interval=3D%d cb=3D%p opaque=3D%p ff=3D%p", + ret, interval, cb, opaque, ff); + g_mutex_unlock(eventlock); + + return ret; +} + + +static struct virEventGLibTimeout * +virEventGLibTimeoutFind(int timer) +{ + guint i; + + g_return_val_if_fail(timeouts !=3D NULL, NULL); + + for (i =3D 0; i < timeouts->len; i++) { + struct virEventGLibTimeout *t =3D g_ptr_array_index(timeouts, i); + + if (t =3D=3D NULL) { + g_warn_if_reached(); + continue; + } + + if ((t->timer =3D=3D timer) && !t->removed) + return t; + } + + return NULL; +} + + +static void +virEventGLibTimeoutUpdate(int timer, + int interval) +{ + struct virEventGLibTimeout *data; + + PROBE(EVENT_GLIB_UPDATE_TIMEOUT, + "timer=3D%d interval=3D%d", + timer, interval); + g_mutex_lock(eventlock); + + data =3D virEventGLibTimeoutFind(timer); + if (!data) { + VIR_DEBUG("Update of missing timeout timer=3D%d", timer); + goto cleanup; + } + + VIR_DEBUG("Update timeout data=3D%p timer=3D%d interval=3D%d ms", data= , timer, interval); + + if (interval >=3D 0) { + if (data->source !=3D 0) + g_source_remove(data->source); + + data->interval =3D interval; + data->source =3D g_timeout_add(data->interval, + virEventGLibTimeoutDispatch, + data); + } else { + if (data->source =3D=3D 0) + goto cleanup; + + g_source_remove(data->source); + data->source =3D 0; + } + + cleanup: + g_mutex_unlock(eventlock); +} + +static gboolean +virEventGLibTimeoutRemoveIdle(gpointer data) +{ + struct virEventGLibTimeout *t =3D data; + + PROBE(EVENT_GLIB_REMOVE_TIMEOUT_IDLE, + "timer=3D%d ff=3D%p opaque=3D%p", + t->timer, t->ff, t->opaque); + + if (t->ff) + (t->ff)(t->opaque); + + g_mutex_lock(eventlock); + g_ptr_array_remove_fast(timeouts, t); + g_mutex_unlock(eventlock); + + return FALSE; +} + +static int +virEventGLibTimeoutRemove(int timer) +{ + struct virEventGLibTimeout *data; + int ret =3D -1; + + PROBE(EVENT_GLIB_REMOVE_TIMEOUT, + "timer=3D%d", + timer); + g_mutex_lock(eventlock); + + data =3D virEventGLibTimeoutFind(timer); + if (!data) { + VIR_DEBUG("Remove of missing timeout timer=3D%d", timer); + goto cleanup; + } + + VIR_DEBUG("Remove timeout data=3D%p timer=3D%d", + data, timer); + + if (data->source !=3D 0) { + g_source_remove(data->source); + data->source =3D 0; + } + + /* since the actual timeout deletion is done asynchronously, a timeout= Update call may + * reschedule the timeout before it's fully deleted, that's why we nee= d to mark it as + * 'removed' to prevent reuse + */ + data->removed =3D TRUE; + g_idle_add(virEventGLibTimeoutRemoveIdle, data); + + ret =3D 0; + + cleanup: + g_mutex_unlock(eventlock); + return ret; +} + + +static gpointer virEventGLibRegisterOnce(gpointer data G_GNUC_UNUSED) +{ + eventlock =3D g_new0(GMutex, 1); + timeouts =3D g_ptr_array_new_with_free_func(g_free); + handles =3D g_ptr_array_new_with_free_func(g_free); + virEventRegisterImpl(virEventGLibHandleAdd, + virEventGLibHandleUpdate, + virEventGLibHandleRemove, + virEventGLibTimeoutAdd, + virEventGLibTimeoutUpdate, + virEventGLibTimeoutRemove); + return NULL; +} + + +void virEventGLibRegister(void) +{ + static GOnce once =3D G_ONCE_INIT; + + g_once(&once, virEventGLibRegisterOnce, NULL); +} + + +int virEventGLibRunOnce(void) +{ + g_main_context_iteration(NULL, TRUE); + + return 0; +} diff --git a/src/util/vireventglib.h b/src/util/vireventglib.h new file mode 100644 index 0000000000..ef68abaa20 --- /dev/null +++ b/src/util/vireventglib.h @@ -0,0 +1,28 @@ +/* + * vireventglib.h: GMainContext based event loop + * + * Copyright (C) 2008 Daniel P. Berrange + * Copyright (C) 2010-2019 Red Hat, Inc. + * + * 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 2.1 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 + * . + */ + +#pragma once + +#include "internal.h" + +void virEventGLibRegister(void); + +int virEventGLibRunOnce(void); --=20 2.24.1