master
c 243 lines 5.67 KB
Raw
1 /*
2 * QEMU I/O task
3 *
4 * Copyright (c) 2015 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 <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include "qemu/osdep.h"
22 #include "io/task.h"
23 #include "qapi/error.h"
24 #include "qemu/thread.h"
25 #include "qom/object.h"
26 #include "trace.h"
27
28 struct QIOTaskThreadData {
29 QIOTaskWorker worker;
30 gpointer opaque;
31 GDestroyNotify destroy;
32 GMainContext *context;
33 GSource *completion;
34 };
35
36
37 struct QIOTask {
38 Object *source;
39 QIOTaskFunc func;
40 gpointer opaque;
41 GDestroyNotify destroy;
42 Error *err;
43 gpointer result;
44 GDestroyNotify destroyResult;
45 QemuMutex thread_lock;
46 QemuCond thread_cond;
47 struct QIOTaskThreadData *thread;
48 };
49
50
51 QIOTask *qio_task_new(Object *source,
52 QIOTaskFunc func,
53 gpointer opaque,
54 GDestroyNotify destroy)
55 {
56 QIOTask *task;
57
58 task = g_new0(QIOTask, 1);
59
60 task->source = source;
61 object_ref(source);
62 task->func = func;
63 task->opaque = opaque;
64 task->destroy = destroy;
65 qemu_mutex_init(&task->thread_lock);
66 qemu_cond_init(&task->thread_cond);
67
68 trace_qio_task_new(task, source, func, opaque);
69
70 return task;
71 }
72
73 void qio_task_free(QIOTask *task)
74 {
75 if (!task) {
76 return;
77 }
78
79 qemu_mutex_lock(&task->thread_lock);
80 if (task->thread) {
81 if (task->thread->destroy) {
82 task->thread->destroy(task->thread->opaque);
83 }
84
85 if (task->thread->context) {
86 g_main_context_unref(task->thread->context);
87 }
88
89 g_free(task->thread);
90 }
91
92 if (task->destroy) {
93 task->destroy(task->opaque);
94 }
95 if (task->destroyResult) {
96 task->destroyResult(task->result);
97 }
98 error_free(task->err);
99 object_unref(task->source);
100
101 qemu_mutex_unlock(&task->thread_lock);
102 qemu_mutex_destroy(&task->thread_lock);
103 qemu_cond_destroy(&task->thread_cond);
104
105 g_free(task);
106 }
107
108
109 static gboolean qio_task_thread_result(gpointer opaque)
110 {
111 QIOTask *task = opaque;
112
113 trace_qio_task_thread_result(task);
114 qio_task_complete(task);
115 qio_task_free(task);
116
117 return FALSE;
118 }
119
120
121 static gpointer qio_task_thread_worker(gpointer opaque)
122 {
123 QIOTask *task = opaque;
124
125 trace_qio_task_thread_run(task);
126
127 task->thread->worker(task, task->thread->opaque);
128
129 /* We're running in the background thread, and must only
130 * ever report the task results in the main event loop
131 * thread. So we schedule an idle callback to report
132 * the worker results
133 */
134 trace_qio_task_thread_exit(task);
135
136 qemu_mutex_lock(&task->thread_lock);
137
138 task->thread->completion = g_idle_source_new();
139 g_source_set_callback(task->thread->completion,
140 qio_task_thread_result, task, NULL);
141 g_source_attach(task->thread->completion,
142 task->thread->context);
143 g_source_unref(task->thread->completion);
144 trace_qio_task_thread_source_attach(task, task->thread->completion);
145
146 qemu_cond_signal(&task->thread_cond);
147 qemu_mutex_unlock(&task->thread_lock);
148
149 return NULL;
150 }
151
152
153 void qio_task_run_in_thread(QIOTask *task,
154 QIOTaskWorker worker,
155 gpointer opaque,
156 GDestroyNotify destroy,
157 GMainContext *context)
158 {
159 struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
160 QemuThread thread;
161
162 if (context) {
163 g_main_context_ref(context);
164 }
165
166 data->worker = worker;
167 data->opaque = opaque;
168 data->destroy = destroy;
169 data->context = context;
170
171 task->thread = data;
172
173 trace_qio_task_thread_start(task, worker, opaque);
174 qemu_thread_create(&thread,
175 "io-task-worker",
176 qio_task_thread_worker,
177 task,
178 QEMU_THREAD_DETACHED);
179 }
180
181
182 void qio_task_wait_thread(QIOTask *task)
183 {
184 qemu_mutex_lock(&task->thread_lock);
185 g_assert(task->thread != NULL);
186 while (task->thread->completion == NULL) {
187 qemu_cond_wait(&task->thread_cond, &task->thread_lock);
188 }
189
190 trace_qio_task_thread_source_cancel(task, task->thread->completion);
191 g_source_destroy(task->thread->completion);
192 qemu_mutex_unlock(&task->thread_lock);
193
194 qio_task_thread_result(task);
195 }
196
197
198 void qio_task_complete(QIOTask *task)
199 {
200 task->func(task, task->opaque);
201 trace_qio_task_complete(task);
202 }
203
204
205 void qio_task_set_error(QIOTask *task,
206 Error *err)
207 {
208 error_propagate(&task->err, err);
209 }
210
211
212 bool qio_task_propagate_error(QIOTask *task,
213 Error **errp)
214 {
215 if (task->err) {
216 error_propagate(errp, task->err);
217 task->err = NULL;
218 return true;
219 }
220
221 return false;
222 }
223
224
225 void qio_task_set_result_pointer(QIOTask *task,
226 gpointer result,
227 GDestroyNotify destroy)
228 {
229 task->result = result;
230 task->destroyResult = destroy;
231 }
232
233
234 gpointer qio_task_get_result_pointer(QIOTask *task)
235 {
236 return task->result;
237 }
238
239
240 Object *qio_task_get_source(QIOTask *task)
241 {
242 return task->source;
243 }