|
|
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;
1.1.1.3 ! root 169: local->lossy_rect = orig->lossy_rect;
1.1 root 170: local->write_pixels = orig->write_pixels;
171: local->clientds = orig->clientds;
172: local->tight = orig->tight;
173: local->zlib = orig->zlib;
174: local->hextile = orig->hextile;
1.1.1.3 ! root 175: local->zrle = orig->zrle;
1.1 root 176: local->output = queue->buffer;
177: local->csock = -1; /* Don't do any network work on this thread */
178:
179: buffer_reset(&local->output);
180: }
181:
182: static void vnc_async_encoding_end(VncState *orig, VncState *local)
183: {
184: orig->tight = local->tight;
185: orig->zlib = local->zlib;
186: orig->hextile = local->hextile;
1.1.1.3 ! root 187: orig->zrle = local->zrle;
! 188: orig->lossy_rect = local->lossy_rect;
! 189:
! 190: queue->buffer = local->output;
1.1 root 191: }
192:
193: static int vnc_worker_thread_loop(VncJobQueue *queue)
194: {
195: VncJob *job;
196: VncRectEntry *entry, *tmp;
197: VncState vs;
198: int n_rectangles;
199: int saved_offset;
200: bool flush;
201:
202: vnc_lock_queue(queue);
203: while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) {
204: qemu_cond_wait(&queue->cond, &queue->mutex);
205: }
206: /* Here job can only be NULL if queue->exit is true */
207: job = QTAILQ_FIRST(&queue->jobs);
208: vnc_unlock_queue(queue);
209:
210: if (queue->exit) {
211: return -1;
212: }
213:
214: vnc_lock_output(job->vs);
215: if (job->vs->csock == -1 || job->vs->abort == true) {
216: goto disconnected;
217: }
218: vnc_unlock_output(job->vs);
219:
220: /* Make a local copy of vs and switch output buffers */
221: vnc_async_encoding_start(job->vs, &vs);
222:
223: /* Start sending rectangles */
224: n_rectangles = 0;
225: vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE);
226: vnc_write_u8(&vs, 0);
227: saved_offset = vs.output.offset;
228: vnc_write_u16(&vs, 0);
229:
230: vnc_lock_display(job->vs->vd);
231: QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) {
232: int n;
233:
234: if (job->vs->csock == -1) {
235: vnc_unlock_display(job->vs->vd);
1.1.1.2 root 236: /* output mutex must be locked before going to
237: * disconnected:
238: */
239: vnc_lock_output(job->vs);
1.1 root 240: goto disconnected;
241: }
242:
243: n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
244: entry->rect.w, entry->rect.h);
245:
246: if (n >= 0) {
247: n_rectangles += n;
248: }
249: qemu_free(entry);
250: }
251: vnc_unlock_display(job->vs->vd);
252:
253: /* Put n_rectangles at the beginning of the message */
254: vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
255: vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
256:
257: /* Switch back buffers */
258: vnc_lock_output(job->vs);
259: if (job->vs->csock == -1) {
260: goto disconnected;
261: }
262:
263: vnc_write(job->vs, vs.output.buffer, vs.output.offset);
264:
265: disconnected:
266: /* Copy persistent encoding data */
267: vnc_async_encoding_end(job->vs, &vs);
268: flush = (job->vs->csock != -1 && job->vs->abort != true);
269: vnc_unlock_output(job->vs);
270:
271: if (flush) {
272: vnc_flush(job->vs);
273: }
274:
275: vnc_lock_queue(queue);
276: QTAILQ_REMOVE(&queue->jobs, job, next);
277: vnc_unlock_queue(queue);
278: qemu_cond_broadcast(&queue->cond);
279: qemu_free(job);
280: return 0;
281: }
282:
283: static VncJobQueue *vnc_queue_init(void)
284: {
285: VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
286:
287: qemu_cond_init(&queue->cond);
288: qemu_mutex_init(&queue->mutex);
289: QTAILQ_INIT(&queue->jobs);
290: return queue;
291: }
292:
293: static void vnc_queue_clear(VncJobQueue *q)
294: {
295: qemu_cond_destroy(&queue->cond);
296: qemu_mutex_destroy(&queue->mutex);
297: buffer_free(&queue->buffer);
298: qemu_free(q);
299: queue = NULL; /* Unset global queue */
300: }
301:
302: static void *vnc_worker_thread(void *arg)
303: {
304: VncJobQueue *queue = arg;
305:
1.1.1.3 ! root 306: qemu_thread_get_self(&queue->thread);
1.1 root 307:
308: while (!vnc_worker_thread_loop(queue)) ;
309: vnc_queue_clear(queue);
310: return NULL;
311: }
312:
313: void vnc_start_worker_thread(void)
314: {
315: VncJobQueue *q;
316:
317: if (vnc_worker_thread_running())
318: return ;
319:
320: q = vnc_queue_init();
321: qemu_thread_create(&q->thread, vnc_worker_thread, q);
322: queue = q; /* Set global queue */
323: }
324:
325: bool vnc_worker_thread_running(void)
326: {
327: return queue; /* Check global queue */
328: }
329:
330: void vnc_stop_worker_thread(void)
331: {
332: if (!vnc_worker_thread_running())
333: return ;
334:
335: /* Remove all jobs and wake up the thread */
336: vnc_lock_queue(queue);
337: queue->exit = true;
338: vnc_unlock_queue(queue);
339: vnc_jobs_clear(NULL);
340: qemu_cond_broadcast(&queue->cond);
341: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.