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