From nobody Sat Apr 11 13:56:04 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1775849091; cv=none; d=zohomail.com; s=zohoarc; b=jj3+XiHFPfNeiGCcKeyh8PjlG/F4IubGEpIoo0mbPN1rbTUtRrxP5/72VI2IVSD3j1IswIyVW8f0tsZcYb04xKgEGI8yLPL0nH1zskBwMx59C2iJFH5ZkXWwvNVbO/CgM4m2VLSSJUpsEGRf+qlGbJyl/+DmNxmQ0T/MHVP0SF0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1775849091; h=Content-Type:Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=RjANnCQtzhl6inA3QcoNqtJZnUi7lbkVyXloCs2jogQ=; b=FQer2QKuCkheV2GndjzhQGaR2AbUU3OFc5pvA3Ct8gTCZJ4y1MPKQFGGQyTIuforGkGv+cfGzo8CqkWgDE6yRDrw0v/zUsA+9JAg0hZZgwm3JEmKdVipJYu6BFxFOxUXs4nbOB3SfXhyX5Nj4hnbC8UhUXzjzypMMlIaikIJdJQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1775849091677549.3158001193387; Fri, 10 Apr 2026 12:24:51 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wBHS5-00066d-Iy; Fri, 10 Apr 2026 15:23:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wBHS4-00066S-KL for qemu-devel@nongnu.org; Fri, 10 Apr 2026 15:23:36 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wBHS2-000269-06 for qemu-devel@nongnu.org; Fri, 10 Apr 2026 15:23:36 -0400 Received: from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-520-bBjnj4wFMuSlttqBDi6Sjw-1; Fri, 10 Apr 2026 15:23:32 -0400 Received: from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 251C118007F2 for ; Fri, 10 Apr 2026 19:23:31 +0000 (UTC) Received: from localhost (unknown [10.44.22.4]) by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E272C18002A6; Fri, 10 Apr 2026 19:23:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1775849013; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=RjANnCQtzhl6inA3QcoNqtJZnUi7lbkVyXloCs2jogQ=; b=C7ohMJoHE2wLFKxLKoxCO1kjHfNJFkq6KMA1lbrW0+rCAUFGipnzmyq9K2LSerJ6NyuAxz ReEzsbfU7SuT75HtdCOLlmcrNqYoJ5j0Ala5yjRh+ZCzeTTe/AsiLEhFs+7Iq+kUB+8gKz W0Ovs6ObEb7Jga5dfr4md2jp50EQ1u0= X-MC-Unique: bBjnj4wFMuSlttqBDi6Sjw-1 X-Mimecast-MFC-AGG-ID: bBjnj4wFMuSlttqBDi6Sjw_1775849011 From: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= Date: Fri, 10 Apr 2026 23:19:08 +0400 Subject: [PATCH v2 46/67] ui/vnc: make the worker thread per-VncDisplay MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260410-qemu-vnc-v2-46-231416f76dc3@redhat.com> References: <20260410-qemu-vnc-v2-0-231416f76dc3@redhat.com> In-Reply-To: <20260410-qemu-vnc-v2-0-231416f76dc3@redhat.com> To: qemu-devel@nongnu.org Cc: =?utf-8?q?Marc-Andr=C3=A9_Lureau?= X-Developer-Signature: v=1; a=openpgp-sha256; l=6611; i=marcandre.lureau@redhat.com; h=from:subject:message-id; bh=wV7SjAq/U+a/ra5a7o/iGvyaoNptnoDE1TurKaPvPMg=; b=owEBbQKS/ZANAwAKAdro4Ql1lpzlAcsmYgBp2U0WEzI16+jx9n6AMtIuctj1KHkUtFoap7DHq D1N8ORn8oaJAjMEAAEKAB0WIQSHqb2TP4fGBtJ29i3a6OEJdZac5QUCadlNFgAKCRDa6OEJdZac 5aI4D/9tnb8M7lqML2GQunDfZM0ZxVFYOOury+BWxjW1+oiP+YEosFr4ouxXIgGzITbinRt+3g2 +IyE76wchhxheb0ccjr0CojnbCM2DSrof8FCr2mDpQJ7T1tViAJkwdQVXW1P5rCictnjw8bXmfe O5HxSPWKQmf4ujqZ1KirNwuMaVA5CBtASR3SW5WPcFOkGK/G/4j4IaAdkGYFy6DwII89i1vv5HU 7GBIFV97NByWRbQsqFWuB+HVMFqN+W+9f/AS92G61OUstQDd9c8cpvcusyBv12hlCRgr1rGpi2H EOSbmJHSnVgkYJyWWp4/PuuExxvk6xEZ2NX+WBLmLzSoyXAMvqhmxP1FK2sd0+5hEGtTztkqds6 3vAxBYvrqorN4hGJVbsKborpl3V1J3D2MQjUlkHqb2aF7es6CUeJf0t7PgAUhyI2tRlt3XZiO+c r7LK8gZdD9cVFda7RtWThRJ79Y6NCeUIB92RxZArfiPhohYfCPb2A7GxA0oqXq/gY50Zc23d/7l L7sfywr6UbZkU0wxYn0SWY8Fct+yETBW4Zidob7qWbtHAz96bbhwwvUnxgevJKIIRXesvYTt67e Iy9XmNKvGI/z9PSqQyH8qOM8dS1VpMKRGlbz9jUy/VBXcRtMiV5ZSKHbrZCtFPBbMglFtBS49tp RHuIzaNaOVizRLw== X-Developer-Key: i=marcandre.lureau@redhat.com; a=openpgp; fpr=87A9BD933F87C606D276F62DDAE8E10975969CE5 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.111 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=marcandre.lureau@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.54, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1775849094145154100 The VNC encoding worker thread was using a single global queue shared across all VNC displays, with no way to stop it. This made it impossible to properly clean up resources when a VncDisplay is freed. Move the VncJobQueue from a file-scoped global to a per-VncDisplay member, so each display owns its worker thread and queue. Add vnc_stop_worker_thread() to perform an orderly shutdown: signal the thread to exit, join it, and destroy the queue. The thread is now created as QEMU_THREAD_JOINABLE instead of QEMU_THREAD_DETACHED. Signed-off-by: Marc-Andr=C3=A9 Lureau --- ui/vnc-jobs.h | 3 ++- ui/vnc.h | 2 ++ ui/vnc-jobs.c | 76 +++++++++++++++++++++++++++++++++----------------------= ---- ui/vnc.c | 3 ++- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/ui/vnc-jobs.h b/ui/vnc-jobs.h index 59f66bcc353..e5ab55c1da6 100644 --- a/ui/vnc-jobs.h +++ b/ui/vnc-jobs.h @@ -37,7 +37,8 @@ void vnc_job_push(VncJob *job); void vnc_jobs_join(VncState *vs); =20 void vnc_jobs_consume_buffer(VncState *vs); -void vnc_start_worker_thread(void); +void vnc_start_worker_thread(VncDisplay *vd); +void vnc_stop_worker_thread(VncDisplay *vd); =20 /* Locks */ static inline int vnc_trylock_display(VncDisplay *vd) diff --git a/ui/vnc.h b/ui/vnc.h index 110c2bd4600..9a09fcdad8b 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -62,6 +62,7 @@ =20 typedef struct VncState VncState; typedef struct VncJob VncJob; +typedef struct VncJobQueue VncJobQueue; typedef struct VncRect VncRect; typedef struct VncRectEntry VncRectEntry; =20 @@ -158,6 +159,7 @@ struct VncDisplay int ledstate; QKbdState *kbd; QemuMutex mutex; + VncJobQueue *queue; =20 int cursor_msize; uint8_t *cursor_mask; diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index 5b17ef54091..c809287dd3a 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -29,8 +29,6 @@ #include "qemu/osdep.h" #include "vnc.h" #include "vnc-jobs.h" -#include "qemu/sockets.h" -#include "qemu/main-loop.h" #include "trace.h" =20 /* @@ -56,17 +54,10 @@ struct VncJobQueue { QemuCond cond; QemuMutex mutex; QemuThread thread; + bool exit; QTAILQ_HEAD(, VncJob) jobs; }; =20 -typedef struct VncJobQueue VncJobQueue; - -/* - * We use a single global queue, but most of the functions are - * already reentrant, so we can easily add more than one encoding thread - */ -static VncJobQueue *queue; - static void vnc_lock_queue(VncJobQueue *queue) { qemu_mutex_lock(&queue->mutex); @@ -125,19 +116,22 @@ static void vnc_job_free(VncJob *job) */ void vnc_job_push(VncJob *job) { + VncJobQueue *queue =3D job->vs->vd->queue; + assert(!QTAILQ_IN_USE(job, next)); =20 if (QLIST_EMPTY(&job->rectangles)) { vnc_job_free(job); } else { vnc_lock_queue(queue); + assert(!queue->exit); QTAILQ_INSERT_TAIL(&queue->jobs, job, next); qemu_cond_broadcast(&queue->cond); vnc_unlock_queue(queue); } } =20 -static bool vnc_has_job_locked(VncState *vs) +static bool vnc_has_job_locked(VncJobQueue *queue, VncState *vs) { VncJob *job; =20 @@ -151,8 +145,10 @@ static bool vnc_has_job_locked(VncState *vs) =20 void vnc_jobs_join(VncState *vs) { + VncJobQueue *queue =3D vs->vd->queue; + vnc_lock_queue(queue); - while (vnc_has_job_locked(vs)) { + while (vnc_has_job_locked(queue, vs)) { qemu_cond_wait(&queue->cond, &queue->mutex); } vnc_unlock_queue(queue); @@ -252,9 +248,13 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) int saved_offset; =20 vnc_lock_queue(queue); - while (QTAILQ_EMPTY(&queue->jobs)) { + while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) { qemu_cond_wait(&queue->cond, &queue->mutex); } + if (queue->exit) { + vnc_unlock_queue(queue); + return 1; + } job =3D QTAILQ_FIRST(&queue->jobs); vnc_unlock_queue(queue); =20 @@ -340,39 +340,49 @@ disconnected: return 0; } =20 -static VncJobQueue *vnc_queue_init(void) -{ - VncJobQueue *queue =3D g_new0(VncJobQueue, 1); - - qemu_cond_init(&queue->cond); - qemu_mutex_init(&queue->mutex); - QTAILQ_INIT(&queue->jobs); - return queue; -} - static void *vnc_worker_thread(void *arg) { VncJobQueue *queue =3D arg; =20 while (!vnc_worker_thread_loop(queue)) ; - g_assert_not_reached(); + return NULL; } =20 -static bool vnc_worker_thread_running(void) +void vnc_start_worker_thread(VncDisplay *vd) { - return queue; /* Check global queue */ + VncJobQueue *queue; + + assert(vd->queue =3D=3D NULL); + + queue =3D g_new0(VncJobQueue, 1); + qemu_cond_init(&queue->cond); + qemu_mutex_init(&queue->mutex); + QTAILQ_INIT(&queue->jobs); + vd->queue =3D queue; + + qemu_thread_create(&queue->thread, "vnc_worker", vnc_worker_thread, qu= eue, + QEMU_THREAD_JOINABLE); } =20 -void vnc_start_worker_thread(void) +void vnc_stop_worker_thread(VncDisplay *vd) { - VncJobQueue *q; + VncJobQueue *queue =3D vd->queue; =20 - if (vnc_worker_thread_running()) + if (!queue) { return; + } + + /* all VNC clients must have finished before we can stop the worker th= read */ + vnc_lock_queue(queue); + assert(QTAILQ_EMPTY(&queue->jobs)); + queue->exit =3D true; + qemu_cond_broadcast(&queue->cond); + vnc_unlock_queue(queue); =20 - q =3D vnc_queue_init(); - qemu_thread_create(&q->thread, "vnc_worker", vnc_worker_thread, q, - QEMU_THREAD_DETACHED); - queue =3D q; /* Set global queue */ + qemu_thread_join(&queue->thread); + qemu_cond_destroy(&queue->cond); + qemu_mutex_destroy(&queue->mutex); + g_free(queue); + vd->queue =3D NULL; } diff --git a/ui/vnc.c b/ui/vnc.c index ba7376360e6..4e5a9ee0341 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3457,7 +3457,7 @@ void vnc_display_init(const char *id, Error **errp) vd->share_policy =3D VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; vd->connections_limit =3D 32; =20 - vnc_start_worker_thread(); + vnc_start_worker_thread(vd); =20 register_displaychangelistener(&vd->dcl); vd->kbd =3D qkbd_state_init(vd->dcl.con); @@ -3517,6 +3517,7 @@ static void vnc_display_free(VncDisplay *vd) =20 assert(QTAILQ_EMPTY(&vd->clients)); =20 + vnc_stop_worker_thread(vd); vnc_display_close(vd); unregister_displaychangelistener(&vd->dcl); qkbd_state_free(vd->kbd); --=20 2.53.0