1 /*
2  * vireventthread.c: thread running a dedicated GMainLoop
3  *
4  * Copyright (C) 2020 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include "vireventthread.h"
24 #include "virthread.h"
25 #include "virerror.h"
26 
27 struct _virEventThread {
28     GObject parent;
29 
30     GThread *thread;
31     GMainContext *context;
32     GMainLoop *loop;
33 };
34 
G_DEFINE_TYPE(virEventThread,vir_event_thread,G_TYPE_OBJECT)35 G_DEFINE_TYPE(virEventThread, vir_event_thread, G_TYPE_OBJECT)
36 
37 #define VIR_FROM_THIS VIR_FROM_EVENT
38 
39 static void
40 vir_event_thread_finalize(GObject *object)
41 {
42     virEventThread *evt = VIR_EVENT_THREAD(object);
43 
44     if (evt->thread) {
45         g_main_loop_quit(evt->loop);
46         g_thread_join(evt->thread);
47     }
48 
49     g_main_loop_unref(evt->loop);
50     g_main_context_unref(evt->context);
51 
52     G_OBJECT_CLASS(vir_event_thread_parent_class)->finalize(object);
53 }
54 
55 
56 static void
vir_event_thread_init(virEventThread * evt)57 vir_event_thread_init(virEventThread *evt)
58 {
59     evt->context = g_main_context_new();
60     evt->loop = g_main_loop_new(evt->context, FALSE);
61 }
62 
63 
64 static void
vir_event_thread_class_init(virEventThreadClass * klass)65 vir_event_thread_class_init(virEventThreadClass *klass)
66 {
67     GObjectClass *obj = G_OBJECT_CLASS(klass);
68 
69     obj->finalize = vir_event_thread_finalize;
70 }
71 
72 
73 typedef struct {
74     GCond cond;
75     GMutex lock;
76     bool running;
77 
78     GMainContext *context;
79     GMainLoop *loop;
80 } virEventThreadData;
81 
82 
83 static void
virEventThreadDataFree(virEventThreadData * data)84 virEventThreadDataFree(virEventThreadData *data)
85 {
86     g_main_loop_unref(data->loop);
87     g_main_context_unref(data->context);
88 
89     g_mutex_clear(&data->lock);
90     g_cond_clear(&data->cond);
91 
92     g_free(data);
93 }
94 
95 
96 static gboolean
virEventThreadNotify(void * opaque)97 virEventThreadNotify(void *opaque)
98 {
99     virEventThreadData *data = opaque;
100 
101     g_mutex_lock(&data->lock);
102     data->running = TRUE;
103     g_mutex_unlock(&data->lock);
104     g_cond_signal(&data->cond);
105 
106     return G_SOURCE_REMOVE;
107 }
108 
109 
110 static void *
virEventThreadWorker(void * opaque)111 virEventThreadWorker(void *opaque)
112 {
113     virEventThreadData *data = opaque;
114     /*
115      * Do NOT use g_autoptr on this. We need to unref it
116      * before the GMainContext is unrefed
117      */
118     GSource *running = g_idle_source_new();
119 
120     g_source_set_callback(running, virEventThreadNotify, data, NULL);
121 
122     g_source_attach(running, data->context);
123 
124     g_main_loop_run(data->loop);
125 
126     g_source_unref(running);
127     virEventThreadDataFree(data);
128 
129     return NULL;
130 }
131 
132 
133 static int
virEventThreadStart(virEventThread * evt,const char * name)134 virEventThreadStart(virEventThread *evt, const char *name)
135 {
136     g_autoptr(GError) gerr = NULL;
137     g_autofree char *thname = NULL;
138     size_t maxname = virThreadMaxName();
139     virEventThreadData *data;
140 
141     if (maxname)
142         thname = g_strndup(name, maxname);
143     else
144         thname = g_strdup(name);
145 
146     if (evt->thread) {
147         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
148                        _("Event thread is already running"));
149         return -1;
150     }
151 
152     data = g_new0(virEventThreadData, 1);
153     data->loop = g_main_loop_ref(evt->loop);
154     data->context = g_main_context_ref(evt->context);
155     g_mutex_init(&data->lock);
156     g_cond_init(&data->cond);
157 
158     evt->thread = g_thread_try_new(thname,
159                                    virEventThreadWorker,
160                                    data,
161                                    &gerr);
162     if (!evt->thread) {
163         virEventThreadDataFree(data);
164         virReportError(VIR_ERR_INTERNAL_ERROR,
165                        _("Unable to start event thread: %s"),
166                        gerr->message);
167         return -1;
168     }
169 
170     g_mutex_lock(&data->lock);
171     while (!data->running)
172         g_cond_wait(&data->cond, &data->lock);
173     g_mutex_unlock(&data->lock);
174 
175     return 0;
176 }
177 
178 
179 virEventThread *
virEventThreadNew(const char * name)180 virEventThreadNew(const char *name)
181 {
182     g_autoptr(virEventThread) evt = VIR_EVENT_THREAD(g_object_new(VIR_TYPE_EVENT_THREAD, NULL));
183 
184     if (virEventThreadStart(evt, name) < 0)
185         return NULL;
186 
187     return g_steal_pointer(&evt);
188 }
189 
190 
191 GMainContext *
virEventThreadGetContext(virEventThread * evt)192 virEventThreadGetContext(virEventThread *evt)
193 {
194     return evt->context;
195 }
196