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

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"
1.1.1.5 ! root       31: #include "qemu_socket.h"
1.1       root       32: 
                     33: /*
                     34:  * Locking:
                     35:  *
                     36:  * There is three levels of locking:
                     37:  * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?)
                     38:  * - VncDisplay global lock: mainly used for framebuffer updates to avoid
                     39:  *                      screen corruption if the framebuffer is updated
                     40:  *                     while the worker is doing something.
                     41:  * - VncState::output lock: used to make sure the output buffer is not corrupted
                     42:  *                      if two threads try to write on it at the same time
                     43:  *
                     44:  * While the VNC worker thread is working, the VncDisplay global lock is hold
                     45:  * to avoid screen corruptions (this does not block vnc_refresh() because it
                     46:  * uses trylock()) but the output lock is not hold because the thread work on
                     47:  * its own output buffer.
                     48:  * When the encoding job is done, the worker thread will hold the output lock
                     49:  * and copy its output buffer in vs->output.
                     50: */
                     51: 
                     52: struct VncJobQueue {
                     53:     QemuCond cond;
                     54:     QemuMutex mutex;
                     55:     QemuThread thread;
                     56:     Buffer buffer;
                     57:     bool exit;
                     58:     QTAILQ_HEAD(, VncJob) jobs;
                     59: };
                     60: 
                     61: typedef struct VncJobQueue VncJobQueue;
                     62: 
                     63: /*
                     64:  * We use a single global queue, but most of the functions are
                     65:  * already reetrant, so we can easilly add more than one encoding thread
                     66:  */
                     67: static VncJobQueue *queue;
                     68: 
                     69: static void vnc_lock_queue(VncJobQueue *queue)
                     70: {
                     71:     qemu_mutex_lock(&queue->mutex);
                     72: }
                     73: 
                     74: static void vnc_unlock_queue(VncJobQueue *queue)
                     75: {
                     76:     qemu_mutex_unlock(&queue->mutex);
                     77: }
                     78: 
                     79: VncJob *vnc_job_new(VncState *vs)
                     80: {
1.1.1.4   root       81:     VncJob *job = g_malloc0(sizeof(VncJob));
1.1       root       82: 
                     83:     job->vs = vs;
                     84:     vnc_lock_queue(queue);
                     85:     QLIST_INIT(&job->rectangles);
                     86:     vnc_unlock_queue(queue);
                     87:     return job;
                     88: }
                     89: 
                     90: int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h)
                     91: {
1.1.1.4   root       92:     VncRectEntry *entry = g_malloc0(sizeof(VncRectEntry));
1.1       root       93: 
                     94:     entry->rect.x = x;
                     95:     entry->rect.y = y;
                     96:     entry->rect.w = w;
                     97:     entry->rect.h = h;
                     98: 
                     99:     vnc_lock_queue(queue);
                    100:     QLIST_INSERT_HEAD(&job->rectangles, entry, next);
                    101:     vnc_unlock_queue(queue);
                    102:     return 1;
                    103: }
                    104: 
                    105: void vnc_job_push(VncJob *job)
                    106: {
                    107:     vnc_lock_queue(queue);
                    108:     if (queue->exit || QLIST_EMPTY(&job->rectangles)) {
1.1.1.4   root      109:         g_free(job);
1.1       root      110:     } else {
                    111:         QTAILQ_INSERT_TAIL(&queue->jobs, job, next);
                    112:         qemu_cond_broadcast(&queue->cond);
                    113:     }
                    114:     vnc_unlock_queue(queue);
                    115: }
                    116: 
                    117: static bool vnc_has_job_locked(VncState *vs)
                    118: {
                    119:     VncJob *job;
                    120: 
                    121:     QTAILQ_FOREACH(job, &queue->jobs, next) {
                    122:         if (job->vs == vs || !vs) {
                    123:             return true;
                    124:         }
                    125:     }
                    126:     return false;
                    127: }
                    128: 
                    129: bool vnc_has_job(VncState *vs)
                    130: {
                    131:     bool ret;
                    132: 
                    133:     vnc_lock_queue(queue);
                    134:     ret = vnc_has_job_locked(vs);
                    135:     vnc_unlock_queue(queue);
                    136:     return ret;
                    137: }
                    138: 
                    139: void vnc_jobs_clear(VncState *vs)
                    140: {
                    141:     VncJob *job, *tmp;
                    142: 
                    143:     vnc_lock_queue(queue);
                    144:     QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) {
                    145:         if (job->vs == vs || !vs) {
                    146:             QTAILQ_REMOVE(&queue->jobs, job, next);
                    147:         }
                    148:     }
                    149:     vnc_unlock_queue(queue);
                    150: }
                    151: 
                    152: void vnc_jobs_join(VncState *vs)
                    153: {
                    154:     vnc_lock_queue(queue);
                    155:     while (vnc_has_job_locked(vs)) {
                    156:         qemu_cond_wait(&queue->cond, &queue->mutex);
                    157:     }
                    158:     vnc_unlock_queue(queue);
1.1.1.5 ! root      159:     vnc_jobs_consume_buffer(vs);
        !           160: }
        !           161: 
        !           162: void vnc_jobs_consume_buffer(VncState *vs)
        !           163: {
        !           164:     bool flush;
        !           165: 
        !           166:     vnc_lock_output(vs);
        !           167:     if (vs->jobs_buffer.offset) {
        !           168:         vnc_write(vs, vs->jobs_buffer.buffer, vs->jobs_buffer.offset);
        !           169:         buffer_reset(&vs->jobs_buffer);
        !           170:     }
        !           171:     flush = vs->csock != -1 && vs->abort != true;
        !           172:     vnc_unlock_output(vs);
        !           173: 
        !           174:     if (flush) {
        !           175:       vnc_flush(vs);
        !           176:     }
1.1       root      177: }
                    178: 
                    179: /*
                    180:  * Copy data for local use
                    181:  */
                    182: static void vnc_async_encoding_start(VncState *orig, VncState *local)
                    183: {
                    184:     local->vnc_encoding = orig->vnc_encoding;
                    185:     local->features = orig->features;
                    186:     local->ds = orig->ds;
                    187:     local->vd = orig->vd;
1.1.1.3   root      188:     local->lossy_rect = orig->lossy_rect;
1.1       root      189:     local->write_pixels = orig->write_pixels;
                    190:     local->clientds = orig->clientds;
                    191:     local->tight = orig->tight;
                    192:     local->zlib = orig->zlib;
                    193:     local->hextile = orig->hextile;
1.1.1.3   root      194:     local->zrle = orig->zrle;
1.1       root      195:     local->output =  queue->buffer;
                    196:     local->csock = -1; /* Don't do any network work on this thread */
                    197: 
                    198:     buffer_reset(&local->output);
                    199: }
                    200: 
                    201: static void vnc_async_encoding_end(VncState *orig, VncState *local)
                    202: {
                    203:     orig->tight = local->tight;
                    204:     orig->zlib = local->zlib;
                    205:     orig->hextile = local->hextile;
1.1.1.3   root      206:     orig->zrle = local->zrle;
                    207:     orig->lossy_rect = local->lossy_rect;
                    208: 
                    209:     queue->buffer = local->output;
1.1       root      210: }
                    211: 
                    212: static int vnc_worker_thread_loop(VncJobQueue *queue)
                    213: {
                    214:     VncJob *job;
                    215:     VncRectEntry *entry, *tmp;
                    216:     VncState vs;
                    217:     int n_rectangles;
                    218:     int saved_offset;
                    219: 
                    220:     vnc_lock_queue(queue);
                    221:     while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
                    222:         qemu_cond_wait(&queue->cond, &queue->mutex);
                    223:     }
                    224:     /* Here job can only be NULL if queue->exit is true */
                    225:     job = QTAILQ_FIRST(&queue->jobs);
                    226:     vnc_unlock_queue(queue);
                    227: 
                    228:     if (queue->exit) {
                    229:         return -1;
                    230:     }
                    231: 
                    232:     vnc_lock_output(job->vs);
                    233:     if (job->vs->csock == -1 || job->vs->abort == true) {
1.1.1.5 ! root      234:         vnc_unlock_output(job->vs);
1.1       root      235:         goto disconnected;
                    236:     }
                    237:     vnc_unlock_output(job->vs);
                    238: 
                    239:     /* Make a local copy of vs and switch output buffers */
                    240:     vnc_async_encoding_start(job->vs, &vs);
                    241: 
                    242:     /* Start sending rectangles */
                    243:     n_rectangles = 0;
                    244:     vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
                    245:     vnc_write_u8(&vs, 0);
                    246:     saved_offset = vs.output.offset;
                    247:     vnc_write_u16(&vs, 0);
                    248: 
                    249:     vnc_lock_display(job->vs->vd);
                    250:     QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
                    251:         int n;
                    252: 
                    253:         if (job->vs->csock == -1) {
                    254:             vnc_unlock_display(job->vs->vd);
                    255:             goto disconnected;
                    256:         }
                    257: 
                    258:         n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
                    259:                                         entry->rect.w, entry->rect.h);
                    260: 
                    261:         if (n >= 0) {
                    262:             n_rectangles += n;
                    263:         }
1.1.1.4   root      264:         g_free(entry);
1.1       root      265:     }
                    266:     vnc_unlock_display(job->vs->vd);
                    267: 
                    268:     /* Put n_rectangles at the beginning of the message */
                    269:     vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
                    270:     vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
                    271: 
                    272:     vnc_lock_output(job->vs);
1.1.1.5 ! root      273:     if (job->vs->csock != -1) {
        !           274:         buffer_reserve(&job->vs->jobs_buffer, vs.output.offset);
        !           275:         buffer_append(&job->vs->jobs_buffer, vs.output.buffer,
        !           276:                       vs.output.offset);
        !           277:         /* Copy persistent encoding data */
        !           278:         vnc_async_encoding_end(job->vs, &vs);
1.1       root      279: 
1.1.1.5 ! root      280:        qemu_bh_schedule(job->vs->bh);
1.1       root      281:     }
1.1.1.5 ! root      282:     vnc_unlock_output(job->vs);
1.1       root      283: 
1.1.1.5 ! root      284: disconnected:
1.1       root      285:     vnc_lock_queue(queue);
                    286:     QTAILQ_REMOVE(&queue->jobs, job, next);
                    287:     vnc_unlock_queue(queue);
                    288:     qemu_cond_broadcast(&queue->cond);
1.1.1.4   root      289:     g_free(job);
1.1       root      290:     return 0;
                    291: }
                    292: 
                    293: static VncJobQueue *vnc_queue_init(void)
                    294: {
1.1.1.4   root      295:     VncJobQueue *queue = g_malloc0(sizeof(VncJobQueue));
1.1       root      296: 
                    297:     qemu_cond_init(&queue->cond);
                    298:     qemu_mutex_init(&queue->mutex);
                    299:     QTAILQ_INIT(&queue->jobs);
                    300:     return queue;
                    301: }
                    302: 
                    303: static void vnc_queue_clear(VncJobQueue *q)
                    304: {
                    305:     qemu_cond_destroy(&queue->cond);
                    306:     qemu_mutex_destroy(&queue->mutex);
                    307:     buffer_free(&queue->buffer);
1.1.1.4   root      308:     g_free(q);
1.1       root      309:     queue = NULL; /* Unset global queue */
                    310: }
                    311: 
                    312: static void *vnc_worker_thread(void *arg)
                    313: {
                    314:     VncJobQueue *queue = arg;
                    315: 
1.1.1.3   root      316:     qemu_thread_get_self(&queue->thread);
1.1       root      317: 
                    318:     while (!vnc_worker_thread_loop(queue)) ;
                    319:     vnc_queue_clear(queue);
                    320:     return NULL;
                    321: }
                    322: 
                    323: void vnc_start_worker_thread(void)
                    324: {
                    325:     VncJobQueue *q;
                    326: 
                    327:     if (vnc_worker_thread_running())
                    328:         return ;
                    329: 
                    330:     q = vnc_queue_init();
1.1.1.5 ! root      331:     qemu_thread_create(&q->thread, vnc_worker_thread, q, QEMU_THREAD_DETACHED);
1.1       root      332:     queue = q; /* Set global queue */
                    333: }
                    334: 
                    335: bool vnc_worker_thread_running(void)
                    336: {
                    337:     return queue; /* Check global queue */
                    338: }
                    339: 
                    340: void vnc_stop_worker_thread(void)
                    341: {
                    342:     if (!vnc_worker_thread_running())
                    343:         return ;
                    344: 
                    345:     /* Remove all jobs and wake up the thread */
                    346:     vnc_lock_queue(queue);
                    347:     queue->exit = true;
                    348:     vnc_unlock_queue(queue);
                    349:     vnc_jobs_clear(NULL);
                    350:     qemu_cond_broadcast(&queue->cond);
                    351: }

unix.superglobalmegacorp.com

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