From: "Daniel P. Berrangé" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: "Markus Armbruster" <armbru@redhat.com>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
devel@lists.libvirt.org, qemu-rust@nongnu.org,
"Dr. David Alan Gilbert" <dave@treblig.org>,
"Gerd Hoffmann" <kraxel@redhat.com>,
"Christian Schoenebeck" <qemu_oss@crudebyte.com>,
"Daniel P. Berrangé" <berrange@redhat.com>,
"Marc-André Lureau" <marcandre.lureau@redhat.com>,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Eduardo Habkost" <eduardo@habkost.net>,
"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
"Paolo Bonzini" <pbonzini@redhat.com>,
"Stefan Weil" <sw@weilnetz.de>
Subject: [PULL 02/27] io: separate freeing of tasks from marking them as complete
Date: Thu, 5 Mar 2026 17:47:18 +0000 [thread overview]
Message-ID: <20260305174743.3084606-3-berrange@redhat.com> (raw)
In-Reply-To: <20260305174743.3084606-1-berrange@redhat.com>
The original design of QIOTask was intended to simplify lifecycle
management by automatically freeing it when the task was marked as
complete. This overlooked the fact that when a QIOTask is used in
combination with a GSource, there may be times when the source
callback is never invoked. This is typically when a GSource is
released before any I/O event arrives. In such cases it is not
desirable to mark a QIOTask as complete, but it still needs to be
freed. To satisfy this, the task must be released manually.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
include/io/task.h | 29 +++++++++++++++++++++--------
io/channel-tls.c | 4 ++++
io/channel-websock.c | 3 +++
io/task.c | 8 ++++++--
tests/unit/test-io-task.c | 26 ++++++++++++++++++++++++++
5 files changed, 60 insertions(+), 10 deletions(-)
diff --git a/include/io/task.h b/include/io/task.h
index 0b5342ee84..98847f5994 100644
--- a/include/io/task.h
+++ b/include/io/task.h
@@ -96,7 +96,7 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
* 1000,
* myobject_operation_timer,
* task,
- * NULL);
+ * qio_task_free);
* }
* </programlisting>
* </example>
@@ -138,9 +138,8 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
* the callback func 'myobject_operation_notify' shown
* earlier to deal with the results.
*
- * Once this function returns false, object_unref will be called
- * automatically on the task causing it to be released and the
- * ref on QMyObject dropped too.
+ * Once this function returns FALSE, the task will be freed,
+ * causing it release the ref on QMyObject too.
*
* The QIOTask module can also be used to perform operations
* in a background thread context, while still reporting the
@@ -208,8 +207,8 @@ typedef void (*QIOTaskWorker)(QIOTask *task,
* 'err' attribute in the task object to determine if
* the operation was successful or not.
*
- * The returned task will be released when qio_task_complete()
- * is invoked.
+ * The returned task must be released by calling
+ * qio_task_free() when no longer required.
*
* Returns: the task struct
*/
@@ -218,6 +217,19 @@ QIOTask *qio_task_new(Object *source,
gpointer opaque,
GDestroyNotify destroy);
+/**
+ * qio_task_free:
+ * task: the task object to free
+ *
+ * Free the resources associated with the task. Typically
+ * the qio_task_complete() method will be called immediately
+ * before this to trigger the task callback, however, it is
+ * permissible to free the task in the case of cancellation.
+ * The destroy callback will be used to release the opaque
+ * data provided to qio_task_new().
+ */
+void qio_task_free(QIOTask *task);
+
/**
* qio_task_run_in_thread:
* @task: the task struct
@@ -268,8 +280,9 @@ void qio_task_wait_thread(QIOTask *task);
* qio_task_complete:
* @task: the task struct
*
- * Invoke the completion callback for @task and
- * then free its memory.
+ * Invoke the completion callback for @task. This should typically
+ * only be invoked once on a task, and then qio_task_free() used
+ * to free it.
*/
void qio_task_complete(QIOTask *task);
diff --git a/io/channel-tls.c b/io/channel-tls.c
index b0cec27cb9..07274c12df 100644
--- a/io/channel-tls.c
+++ b/io/channel-tls.c
@@ -170,6 +170,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
trace_qio_channel_tls_handshake_fail(ioc);
qio_task_set_error(task, err);
qio_task_complete(task);
+ qio_task_free(task);
return;
}
@@ -183,6 +184,7 @@ static void qio_channel_tls_handshake_task(QIOChannelTLS *ioc,
trace_qio_channel_tls_credentials_allow(ioc);
}
qio_task_complete(task);
+ qio_task_free(task);
} else {
GIOCondition condition;
QIOChannelTLSData *data = g_new0(typeof(*data), 1);
@@ -270,11 +272,13 @@ static void qio_channel_tls_bye_task(QIOChannelTLS *ioc, QIOTask *task,
trace_qio_channel_tls_bye_fail(ioc);
qio_task_set_error(task, err);
qio_task_complete(task);
+ qio_task_free(task);
return;
}
if (status == QCRYPTO_TLS_BYE_COMPLETE) {
qio_task_complete(task);
+ qio_task_free(task);
return;
}
diff --git a/io/channel-websock.c b/io/channel-websock.c
index d0929ba232..100faba6b3 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -545,6 +545,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
qio_task_set_error(task, err);
qio_task_complete(task);
+ qio_task_free(task);
wioc->hs_io_tag = 0;
return FALSE;
}
@@ -561,6 +562,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
trace_qio_channel_websock_handshake_complete(ioc);
qio_task_complete(task);
}
+ qio_task_free(task);
wioc->hs_io_tag = 0;
return FALSE;
}
@@ -588,6 +590,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
qio_task_set_error(task, err);
qio_task_complete(task);
+ qio_task_free(task);
wioc->hs_io_tag = 0;
return FALSE;
}
diff --git a/io/task.c b/io/task.c
index da79d31782..c3615d2d74 100644
--- a/io/task.c
+++ b/io/task.c
@@ -70,8 +70,12 @@ QIOTask *qio_task_new(Object *source,
return task;
}
-static void qio_task_free(QIOTask *task)
+void qio_task_free(QIOTask *task)
{
+ if (!task) {
+ return;
+ }
+
qemu_mutex_lock(&task->thread_lock);
if (task->thread) {
if (task->thread->destroy) {
@@ -108,6 +112,7 @@ static gboolean qio_task_thread_result(gpointer opaque)
trace_qio_task_thread_result(task);
qio_task_complete(task);
+ qio_task_free(task);
return FALSE;
}
@@ -194,7 +199,6 @@ void qio_task_complete(QIOTask *task)
{
task->func(task, task->opaque);
trace_qio_task_complete(task);
- qio_task_free(task);
}
diff --git a/tests/unit/test-io-task.c b/tests/unit/test-io-task.c
index 115dba8970..b1c8ecb7ab 100644
--- a/tests/unit/test-io-task.c
+++ b/tests/unit/test-io-task.c
@@ -73,6 +73,7 @@ static void test_task_complete(void)
src = qio_task_get_source(task);
qio_task_complete(task);
+ qio_task_free(task);
g_assert(obj == src);
@@ -84,6 +85,28 @@ static void test_task_complete(void)
}
+static void test_task_cancel(void)
+{
+ QIOTask *task;
+ Object *obj = object_new(TYPE_DUMMY);
+ Object *src;
+ struct TestTaskData data = { NULL, NULL, false };
+
+ task = qio_task_new(obj, task_callback, &data, NULL);
+ src = qio_task_get_source(task);
+
+ qio_task_free(task);
+
+ g_assert(obj == src);
+
+ object_unref(obj);
+
+ g_assert(data.source == NULL);
+ g_assert(data.err == NULL);
+ g_assert(data.freed == false);
+}
+
+
static void task_data_free(gpointer opaque)
{
struct TestTaskData *data = opaque;
@@ -101,6 +124,7 @@ static void test_task_data_free(void)
task = qio_task_new(obj, task_callback, &data, task_data_free);
qio_task_complete(task);
+ qio_task_free(task);
object_unref(obj);
@@ -123,6 +147,7 @@ static void test_task_failure(void)
qio_task_set_error(task, err);
qio_task_complete(task);
+ qio_task_free(task);
object_unref(obj);
@@ -260,6 +285,7 @@ int main(int argc, char **argv)
module_call_init(MODULE_INIT_QOM);
type_register_static(&dummy_info);
g_test_add_func("/crypto/task/complete", test_task_complete);
+ g_test_add_func("/crypto/task/cancel", test_task_cancel);
g_test_add_func("/crypto/task/datafree", test_task_data_free);
g_test_add_func("/crypto/task/failure", test_task_failure);
g_test_add_func("/crypto/task/thread_complete", test_task_thread_complete);
--
2.53.0
next prev parent reply other threads:[~2026-03-05 17:49 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-05 17:47 [PULL v2 00/27] Misc patches queue Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 01/27] scripts: detect another GPL license boilerplate variant Daniel P. Berrangé
2026-03-05 17:47 ` Daniel P. Berrangé [this message]
2026-03-05 17:47 ` [PULL 03/27] io: fix cleanup for TLS I/O source data on cancellation Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 04/27] io: fix cleanup for websock " Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 05/27] docs: simplify DiamondRapids CPU docs Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 06/27] qemu-options: remove extraneous [] around arg values Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 07/27] include: define constant for early constructor priority Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 08/27] monitor: initialize global data from a constructor Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 09/27] system: unconditionally enable thread naming Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 10/27] util: fix race setting thread name on Win32 Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 11/27] util: expose qemu_thread_set_name Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 12/27] audio: make jackaudio use qemu_thread_set_name Daniel P. Berrangé
2026-03-07 11:37 ` Philippe Mathieu-Daudé
2026-03-05 17:47 ` [PULL 13/27] util: set the name for the 'main' thread on Windows Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 14/27] util: add API to fetch the current thread name Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 15/27] util: introduce some API docs for logging APIs Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 16/27] util: avoid repeated prefix on incremental qemu_log calls Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 17/27] util/log: add missing error reporting in qemu_log_trylock_with_err Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 18/27] ui: add proper error reporting for password changes Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 19/27] ui: remove redundant use of error_printf_unless_qmp() Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 20/27] monitor: remove redundant error_[v]printf_unless_qmp Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 21/27] monitor: refactor error_vprintf() Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 22/27] monitor: move error_vprintf back to error-report.c Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 23/27] util: fix interleaving of error & trace output Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 24/27] util: don't skip error prefixes when QMP is active Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 25/27] util: fix interleaving of error prefixes Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 26/27] scripts/checkpatch: Fix MAINTAINERS update warning with --terse Daniel P. Berrangé
2026-03-05 17:47 ` [PULL 27/27] util/oslib-posix: increase memprealloc thread count to 32 Daniel P. Berrangé
2026-03-06 9:49 ` [PULL v2 00/27] Misc patches queue Peter Maydell
-- strict thread matches above, loose matches on Subject: below --
2026-03-03 15:05 [PULL " Daniel P. Berrangé
2026-03-03 15:05 ` [PULL 02/27] io: separate freeing of tasks from marking them as complete Daniel P. Berrangé
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260305174743.3084606-3-berrange@redhat.com \
--to=berrange@redhat.com \
--cc=armbru@redhat.com \
--cc=dave@treblig.org \
--cc=devel@lists.libvirt.org \
--cc=eduardo@habkost.net \
--cc=kraxel@redhat.com \
--cc=manos.pitsidianakis@linaro.org \
--cc=marcandre.lureau@redhat.com \
--cc=pbonzini@redhat.com \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu-rust@nongnu.org \
--cc=qemu_oss@crudebyte.com \
--cc=richard.henderson@linaro.org \
--cc=sw@weilnetz.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.