Annotation of qemu/ui/vnc-jobs-async.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * QEMU VNC display driver
        !             3:  *
        !             4:  * Copyright (C) 2006 Anthony Liguori <[email protected]>
        !             5:  * Copyright (C) 2006 Fabrice Bellard
        !             6:  * Copyright (C) 2009 Red Hat, Inc
        !             7:  * Copyright (C) 2010 Corentin Chary <[email protected]>
        !             8:  *
        !             9:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !            10:  * of this software and associated documentation files (the "Software"), to deal
        !            11:  * in the Software without restriction, including without limitation the rights
        !            12:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            13:  * copies of the Software, and to permit persons to whom the Software is
        !            14:  * furnished to do so, subject to the following conditions:
        !            15:  *
        !            16:  * The above copyright notice and this permission notice shall be included in
        !            17:  * all copies or substantial portions of the Software.
        !            18:  *
        !            19:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            20:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            21:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            22:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            23:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            24:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            25:  * THE SOFTWARE.
        !            26:  */
        !            27: 
        !            28: 
        !            29: #include "vnc.h"
        !            30: #include "vnc-jobs.h"
        !            31: 
        !            32: /*
        !            33:  * Locking:
        !            34:  *
        !            35:  * There is three levels of locking:
        !            36:  * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
        !            37:  * - VncDisplay global lock: mainly used for framebuffer updates to avoid
        !            38:  *                      screen corruption if the framebuffer is updated
        !            39:  *                     while the worker is doing something.
        !            40:  * - VncState::output lock: used to make sure the output buffer is not corrupted
        !            41:  *                      if two threads try to write on it at the same time
        !            42:  *
        !            43:  * While the VNC worker thread is working, the VncDisplay global lock is hold
        !            44:  * to avoid screen corruptions (this does not block vnc_refresh() because it
        !            45:  * uses trylock()) but the output lock is not hold because the thread work on
        !            46:  * its own output buffer.
        !            47:  * When the encoding job is done, the worker thread will hold the output lock
        !            48:  * and copy its output buffer in vs->output.
        !            49: */
        !            50: 
        !            51: struct VncJobQueue {
        !            52:     QemuCond cond;
        !            53:     QemuMutex mutex;
        !            54:     QemuThread thread;
        !            55:     Buffer buffer;
        !            56:     bool exit;
        !            57:     QTAILQ_HEAD(, VncJob) jobs;
        !            58: };
        !            59: 
        !            60: typedef struct VncJobQueue VncJobQueue;
        !            61: 
        !            62: /*
        !            63:  * We use a single global queue, but most of the functions are
        !            64:  * already reetrant, so we can easilly add more than one encoding thread
        !            65:  */
        !            66: static VncJobQueue *queue;
        !            67: 
        !            68: static void vnc_lock_queue(VncJobQueue *queue)
        !            69: {
        !            70:     qemu_mutex_lock(&queue->mutex);
        !            71: }
        !            72: 
        !            73: static void vnc_unlock_queue(VncJobQueue *queue)
        !            74: {
        !            75:     qemu_mutex_unlock(&queue->mutex);
        !            76: }
        !            77: 
        !            78: VncJob *vnc_job_new(VncState *vs)
        !            79: {
        !            80:     VncJob *job = qemu_mallocz(sizeof(VncJob));
        !            81: 
        !            82:     job->vs = vs;
        !            83:     vnc_lock_queue(queue);
        !            84:     QLIST_INIT(&job->rectangles);
        !            85:     vnc_unlock_queue(queue);
        !            86:     return job;
        !            87: }
        !            88: 
        !            89: int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
        !            90: {
        !            91:     VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry));
        !            92: 
        !            93:     entry->rect.x = x;
        !            94:     entry->rect.y = y;
        !            95:     entry->rect.w = w;
        !            96:     entry->rect.h = h;
        !            97: 
        !            98:     vnc_lock_queue(queue);
        !            99:     QLIST_INSERT_HEAD(&job->rectangles, entry, next);
        !           100:     vnc_unlock_queue(queue);
        !           101:     return 1;
        !           102: }
        !           103: 
        !           104: void vnc_job_push(VncJob *job)
        !           105: {
        !           106:     vnc_lock_queue(queue);
        !           107:     if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
        !           108:         qemu_free(job);
        !           109:     } else {
        !           110:         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
        !           111:         qemu_cond_broadcast(&queue->cond);
        !           112:     }
        !           113:     vnc_unlock_queue(queue);
        !           114: }
        !           115: 
        !           116: static bool vnc_has_job_locked(VncState *vs)
        !           117: {
        !           118:     VncJob *job;
        !           119: 
        !           120:     QTAILQ_FOREACH(job, &queue->jobs, next) {
        !           121:         if (job->vs == vs || !vs) {
        !           122:             return true;
        !           123:         }
        !           124:     }
        !           125:     return false;
        !           126: }
        !           127: 
        !           128: bool vnc_has_job(VncState *vs)
        !           129: {
        !           130:     bool ret;
        !           131: 
        !           132:     vnc_lock_queue(queue);
        !           133:     ret = vnc_has_job_locked(vs);
        !           134:     vnc_unlock_queue(queue);
        !           135:     return ret;
        !           136: }
        !           137: 
        !           138: void vnc_jobs_clear(VncState *vs)
        !           139: {
        !           140:     VncJob *job, *tmp;
        !           141: 
        !           142:     vnc_lock_queue(queue);
        !           143:     QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
        !           144:         if (job->vs == vs || !vs) {
        !           145:             QTAILQ_REMOVE(&queue->jobs, job, next);
        !           146:         }
        !           147:     }
        !           148:     vnc_unlock_queue(queue);
        !           149: }
        !           150: 
        !           151: void vnc_jobs_join(VncState *vs)
        !           152: {
        !           153:     vnc_lock_queue(queue);
        !           154:     while (vnc_has_job_locked(vs)) {
        !           155:         qemu_cond_wait(&queue->cond, &queue->mutex);
        !           156:     }
        !           157:     vnc_unlock_queue(queue);
        !           158: }
        !           159: 
        !           160: /*
        !           161:  * Copy data for local use
        !           162:  */
        !           163: static void vnc_async_encoding_start(VncState *orig, VncState *local)
        !           164: {
        !           165:     local->vnc_encoding = orig->vnc_encoding;
        !           166:     local->features = orig->features;
        !           167:     local->ds = orig->ds;
        !           168:     local->vd = orig->vd;
        !           169:     local->write_pixels = orig->write_pixels;
        !           170:     local->clientds = orig->clientds;
        !           171:     local->tight = orig->tight;
        !           172:     local->zlib = orig->zlib;
        !           173:     local->hextile = orig->hextile;
        !           174:     local->output =  queue->buffer;
        !           175:     local->csock = -1; /* Don't do any network work on this thread */
        !           176: 
        !           177:     buffer_reset(&local->output);
        !           178: }
        !           179: 
        !           180: static void vnc_async_encoding_end(VncState *orig, VncState *local)
        !           181: {
        !           182:     orig->tight = local->tight;
        !           183:     orig->zlib = local->zlib;
        !           184:     orig->hextile = local->hextile;
        !           185: }
        !           186: 
        !           187: static int vnc_worker_thread_loop(VncJobQueue *queue)
        !           188: {
        !           189:     VncJob *job;
        !           190:     VncRectEntry *entry, *tmp;
        !           191:     VncState vs;
        !           192:     int n_rectangles;
        !           193:     int saved_offset;
        !           194:     bool flush;
        !           195: 
        !           196:     vnc_lock_queue(queue);
        !           197:     while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
        !           198:         qemu_cond_wait(&queue->cond, &queue->mutex);
        !           199:     }
        !           200:     /* Here job can only be NULL if queue->exit is true */
        !           201:     job = QTAILQ_FIRST(&queue->jobs);
        !           202:     vnc_unlock_queue(queue);
        !           203: 
        !           204:     if (queue->exit) {
        !           205:         return -1;
        !           206:     }
        !           207: 
        !           208:     vnc_lock_output(job->vs);
        !           209:     if (job->vs->csock == -1 || job->vs->abort == true) {
        !           210:         goto disconnected;
        !           211:     }
        !           212:     vnc_unlock_output(job->vs);
        !           213: 
        !           214:     /* Make a local copy of vs and switch output buffers */
        !           215:     vnc_async_encoding_start(job->vs, &vs);
        !           216: 
        !           217:     /* Start sending rectangles */
        !           218:     n_rectangles = 0;
        !           219:     vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
        !           220:     vnc_write_u8(&vs, 0);
        !           221:     saved_offset = vs.output.offset;
        !           222:     vnc_write_u16(&vs, 0);
        !           223: 
        !           224:     vnc_lock_display(job->vs->vd);
        !           225:     QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
        !           226:         int n;
        !           227: 
        !           228:         if (job->vs->csock == -1) {
        !           229:             vnc_unlock_display(job->vs->vd);
        !           230:             goto disconnected;
        !           231:         }
        !           232: 
        !           233:         n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
        !           234:                                         entry->rect.w, entry->rect.h);
        !           235: 
        !           236:         if (n >= 0) {
        !           237:             n_rectangles += n;
        !           238:         }
        !           239:         qemu_free(entry);
        !           240:     }
        !           241:     vnc_unlock_display(job->vs->vd);
        !           242: 
        !           243:     /* Put n_rectangles at the beginning of the message */
        !           244:     vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
        !           245:     vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
        !           246: 
        !           247:     /* Switch back buffers */
        !           248:     vnc_lock_output(job->vs);
        !           249:     if (job->vs->csock == -1) {
        !           250:         goto disconnected;
        !           251:     }
        !           252: 
        !           253:     vnc_write(job->vs, vs.output.buffer, vs.output.offset);
        !           254: 
        !           255: disconnected:
        !           256:     /* Copy persistent encoding data */
        !           257:     vnc_async_encoding_end(job->vs, &vs);
        !           258:     flush = (job->vs->csock != -1 && job->vs->abort != true);
        !           259:     vnc_unlock_output(job->vs);
        !           260: 
        !           261:     if (flush) {
        !           262:         vnc_flush(job->vs);
        !           263:     }
        !           264: 
        !           265:     vnc_lock_queue(queue);
        !           266:     QTAILQ_REMOVE(&queue->jobs, job, next);
        !           267:     vnc_unlock_queue(queue);
        !           268:     qemu_cond_broadcast(&queue->cond);
        !           269:     qemu_free(job);
        !           270:     return 0;
        !           271: }
        !           272: 
        !           273: static VncJobQueue *vnc_queue_init(void)
        !           274: {
        !           275:     VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
        !           276: 
        !           277:     qemu_cond_init(&queue->cond);
        !           278:     qemu_mutex_init(&queue->mutex);
        !           279:     QTAILQ_INIT(&queue->jobs);
        !           280:     return queue;
        !           281: }
        !           282: 
        !           283: static void vnc_queue_clear(VncJobQueue *q)
        !           284: {
        !           285:     qemu_cond_destroy(&queue->cond);
        !           286:     qemu_mutex_destroy(&queue->mutex);
        !           287:     buffer_free(&queue->buffer);
        !           288:     qemu_free(q);
        !           289:     queue = NULL; /* Unset global queue */
        !           290: }
        !           291: 
        !           292: static void *vnc_worker_thread(void *arg)
        !           293: {
        !           294:     VncJobQueue *queue = arg;
        !           295: 
        !           296:     qemu_thread_self(&queue->thread);
        !           297: 
        !           298:     while (!vnc_worker_thread_loop(queue)) ;
        !           299:     vnc_queue_clear(queue);
        !           300:     return NULL;
        !           301: }
        !           302: 
        !           303: void vnc_start_worker_thread(void)
        !           304: {
        !           305:     VncJobQueue *q;
        !           306: 
        !           307:     if (vnc_worker_thread_running())
        !           308:         return ;
        !           309: 
        !           310:     q = vnc_queue_init();
        !           311:     qemu_thread_create(&q->thread, vnc_worker_thread, q);
        !           312:     queue = q; /* Set global queue */
        !           313: }
        !           314: 
        !           315: bool vnc_worker_thread_running(void)
        !           316: {
        !           317:     return queue; /* Check global queue */
        !           318: }
        !           319: 
        !           320: void vnc_stop_worker_thread(void)
        !           321: {
        !           322:     if (!vnc_worker_thread_running())
        !           323:         return ;
        !           324: 
        !           325:     /* Remove all jobs and wake up the thread */
        !           326:     vnc_lock_queue(queue);
        !           327:     queue->exit = true;
        !           328:     vnc_unlock_queue(queue);
        !           329:     vnc_jobs_clear(NULL);
        !           330:     qemu_cond_broadcast(&queue->cond);
        !           331: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.