|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.