xref: /qemu/io/task.c (revision 18375b5c)
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 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 "trace.h"
26 
27 struct QIOTask {
28     Object *source;
29     QIOTaskFunc func;
30     gpointer opaque;
31     GDestroyNotify destroy;
32     Error *err;
33     gpointer result;
34     GDestroyNotify destroyResult;
35 };
36 
37 
38 QIOTask *qio_task_new(Object *source,
39                       QIOTaskFunc func,
40                       gpointer opaque,
41                       GDestroyNotify destroy)
42 {
43     QIOTask *task;
44 
45     task = g_new0(QIOTask, 1);
46 
47     task->source = source;
48     object_ref(source);
49     task->func = func;
50     task->opaque = opaque;
51     task->destroy = destroy;
52 
53     trace_qio_task_new(task, source, func, opaque);
54 
55     return task;
56 }
57 
58 static void qio_task_free(QIOTask *task)
59 {
60     if (task->destroy) {
61         task->destroy(task->opaque);
62     }
63     if (task->destroyResult) {
64         task->destroyResult(task->result);
65     }
66     if (task->err) {
67         error_free(task->err);
68     }
69     object_unref(task->source);
70 
71     g_free(task);
72 }
73 
74 
75 struct QIOTaskThreadData {
76     QIOTask *task;
77     QIOTaskWorker worker;
78     gpointer opaque;
79     GDestroyNotify destroy;
80 };
81 
82 
83 static gboolean gio_task_thread_result(gpointer opaque)
84 {
85     struct QIOTaskThreadData *data = opaque;
86 
87     trace_qio_task_thread_result(data->task);
88     qio_task_complete(data->task);
89 
90     if (data->destroy) {
91         data->destroy(data->opaque);
92     }
93 
94     g_free(data);
95 
96     return FALSE;
97 }
98 
99 
100 static gpointer qio_task_thread_worker(gpointer opaque)
101 {
102     struct QIOTaskThreadData *data = opaque;
103 
104     trace_qio_task_thread_run(data->task);
105     data->worker(data->task, data->opaque);
106 
107     /* We're running in the background thread, and must only
108      * ever report the task results in the main event loop
109      * thread. So we schedule an idle callback to report
110      * the worker results
111      */
112     trace_qio_task_thread_exit(data->task);
113     g_idle_add(gio_task_thread_result, data);
114     return NULL;
115 }
116 
117 
118 void qio_task_run_in_thread(QIOTask *task,
119                             QIOTaskWorker worker,
120                             gpointer opaque,
121                             GDestroyNotify destroy)
122 {
123     struct QIOTaskThreadData *data = g_new0(struct QIOTaskThreadData, 1);
124     QemuThread thread;
125 
126     data->task = task;
127     data->worker = worker;
128     data->opaque = opaque;
129     data->destroy = destroy;
130 
131     trace_qio_task_thread_start(task, worker, opaque);
132     qemu_thread_create(&thread,
133                        "io-task-worker",
134                        qio_task_thread_worker,
135                        data,
136                        QEMU_THREAD_DETACHED);
137 }
138 
139 
140 void qio_task_complete(QIOTask *task)
141 {
142     task->func(task, task->opaque);
143     trace_qio_task_complete(task);
144     qio_task_free(task);
145 }
146 
147 
148 void qio_task_set_error(QIOTask *task,
149                         Error *err)
150 {
151     error_propagate(&task->err, err);
152 }
153 
154 
155 bool qio_task_propagate_error(QIOTask *task,
156                               Error **errp)
157 {
158     if (task->err) {
159         error_propagate(errp, task->err);
160         task->err = NULL;
161         return true;
162     }
163 
164     return false;
165 }
166 
167 
168 void qio_task_set_result_pointer(QIOTask *task,
169                                  gpointer result,
170                                  GDestroyNotify destroy)
171 {
172     task->result = result;
173     task->destroyResult = destroy;
174 }
175 
176 
177 gpointer qio_task_get_result_pointer(QIOTask *task)
178 {
179     return task->result;
180 }
181 
182 
183 Object *qio_task_get_source(QIOTask *task)
184 {
185     return task->source;
186 }
187