From: Paolo Bonzini <pbonzini@redhat.com>
To: Corentin Chary <corentincj@iksaif.net>
Cc: Anthony Liguori <aliguori@linux.vnet.ibm.com>,
Alexander Graf <agraf@suse.de>, Adam Litke <agl@us.ibm.com>,
qemu-devel@nongnu.org, Gautham R Shenoy <ego@in.ibm.com>
Subject: [Qemu-devel] Re: [PATCH 3/3] vnc: threaded VNC server
Date: Thu, 03 Jun 2010 09:55:12 +0200 [thread overview]
Message-ID: <4C075FE0.80704@redhat.com> (raw)
In-Reply-To: <1275118686-15649-4-git-send-email-corentincj@iksaif.net>
On 05/29/2010 09:38 AM, Corentin Chary wrote:
> Implement a threaded VNC server using the producer-consumer model.
> The main thread will push encoding jobs (a list a rectangles to update)
> in a queue, and the VNC worker thread will consume that queue and send
> framebuffer updates to the output buffer.
>
> There is three levels of locking:
> - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
> - VncState global lock: mainly used for framebuffer updates to avoid
> screen corruption if the framebuffer is updated
> while the worker threaded is doing something.
> - VncState::output lock: used to make sure the output buffer is not corrupted
> if two threads try to write on it at the same time
>
> While the VNC worker thread is working, the VncState global lock is hold
> to avoid screen corruptions (this block vnc_refresh() for a short time) but the
> output lock is not hold because the thread work on its own output buffer. When
> the encoding job is done, the worker thread will hold the output lock and copy
> its output buffer in vs->output.
This belong in a comment in the code, not in the commit message (or in
both).
> +void vnc_job_push(VncJob *job)
> +{
> + vnc_lock_queue(queue);
> + if (QLIST_EMPTY(&job->rectangles)) {
> + qemu_free(job);
No need to lock if you get into the "then" block.
> + } else {
> + QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
> + qemu_cond_broadcast(&queue->cond);
> + }
> + vnc_unlock_queue(queue);
> +}
...
> +static int vnc_worker_thread_loop(VncJobQueue *queue)
> +{
> + VncJob *job;
> + VncRectEntry *entry, *tmp;
> + VncState vs;
> + int n_rectangles;
> + int saved_offset;
> +
> + vnc_lock_queue(queue);
> + if (QTAILQ_EMPTY(&queue->jobs)) {
> + qemu_cond_wait(&queue->cond,&queue->mutex);
> + }
> +
> + /* If the queue is empty, it's an exit order */
> + if (QTAILQ_EMPTY(&queue->jobs)) {
> + vnc_unlock_queue(queue);
> + return -1;
> + }
This is not safe. It might work with a single consumer, but something
like this is better:
vnc_lock_queue(queue);
while (!queue->exit && QTAILQ_EMPTY(&queue->jobs)) {
qemu_cond_wait(&queue->cond,&queue->mutex);
}
if (queue->exit) {
vnc_unlock_queue(queue);
return -1;
}
(It occurred to me now that maybe you can reuse ->aborting. Not sure
though).
> + qemu_mutex_unlock(&job->vs->output_mutex);
> +
> + if (job->vs->csock != -1 && job->vs->abording != true) {
> + vnc_flush(job->vs);
> + }
> +
You're accessing the abort flag outside the mutex here. Also, you are
not using vnc_{,un}lock_output.
> + job = QTAILQ_FIRST(&queue->jobs);
> + vnc_unlock_queue(queue);
...
> +static void vnc_abord_display_jobs(VncDisplay *vd)
> +{
> + VncState *vs;
> +
> + QTAILQ_FOREACH(vs, &vd->clients, next) {
> + vnc_lock_output(vs);
> + vs->abording = true;
> + vnc_unlock_output(vs);
> + }
> + QTAILQ_FOREACH(vs, &vd->clients, next) {
> + vnc_jobs_join(vs);
> + }
> + QTAILQ_FOREACH(vs, &vd->clients, next) {
> + vnc_lock_output(vs);
> + vs->abording = false;
> + vnc_unlock_output(vs);
> + }
> +}
It's "abort" not "abord". :-)
...
> static void vnc_disconnect_finish(VncState *vs)
> {
> + vnc_jobs_join(vs); /* Wait encoding jobs */
> + vnc_lock(vs);
Possibly racy? Maybe you have to set the aforementioned new flag
queue->exit at the beginning of vnc_jobs_join, and refuse new jobs if it
is set.
Also, if anything waits on the same vs in vnc_refresh while you own it
in vnc_disconnect_finish, as soon as you unlock they'll have a dangling
pointer. (After you unlock the mutex the OS wakes the thread, but then
pthread_mutex_lock has to check again that no one got the lock in the
meanwhile; so QTAILQ_FOREACH_SAFE is not protecting you). Probably it's
better to use a single lock on vd->clients instead of one lock per VncState.
> +void vnc_client_write(void *opaque)
> +{
> + VncState *vs = opaque;
> +
> + vnc_lock_output(vs);
> + if (vs->output.offset) {
> + vnc_client_write_locked(opaque);
> + } else {
> + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
> + }
Why the if? The "else" branch is already done by vnc_client_write_plain.
This may be a good time to port qemu-threads to Windows too. IO thread
has no hope to work under Windows at least without major hacks (because
Windows has no asynchronous interrupts; the only way I can imagine to
emulate them is a breakpoint) but threaded VNC should work.
Paolo
next prev parent reply other threads:[~2010-06-03 7:55 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-05-29 7:38 [Qemu-devel] [PATCH 0/3] [RFC] Threaded vnc server Corentin Chary
2010-05-29 7:38 ` [Qemu-devel] [PATCH 1/3] qemu-thread: add qemu_mutex/cond_destroy and qemu_mutex_exit Corentin Chary
2010-05-29 7:38 ` [Qemu-devel] [PATCH 2/3] qemu-thread: add cleanup_push() and cleanup_pop() Corentin Chary
2010-06-03 5:50 ` Paul Brook
2010-06-03 7:27 ` [Qemu-devel] " Paolo Bonzini
2010-06-03 7:46 ` Corentin Chary
2010-05-29 7:38 ` [Qemu-devel] [PATCH 3/3] vnc: threaded VNC server Corentin Chary
2010-06-03 7:55 ` Paolo Bonzini [this message]
2010-06-03 8:26 ` [Qemu-devel] " Corentin Chary
[not found] <1152486922.2330781275561618453.JavaMail.root@zmail07.collab.prod.int.phx2.redhat.com>
2010-06-03 10:46 ` Paolo Bonzini
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=4C075FE0.80704@redhat.com \
--to=pbonzini@redhat.com \
--cc=agl@us.ibm.com \
--cc=agraf@suse.de \
--cc=aliguori@linux.vnet.ibm.com \
--cc=corentincj@iksaif.net \
--cc=ego@in.ibm.com \
--cc=qemu-devel@nongnu.org \
/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.