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