|
|
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);
1.1.1.2 ! root 230: /* output mutex must be locked before going to
! 231: * disconnected:
! 232: */
! 233: vnc_lock_output(job->vs);
1.1 root 234: goto disconnected;
235: }
236:
237: n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y,
238: entry->rect.w, entry->rect.h);
239:
240: if (n >= 0) {
241: n_rectangles += n;
242: }
243: qemu_free(entry);
244: }
245: vnc_unlock_display(job->vs->vd);
246:
247: /* Put n_rectangles at the beginning of the message */
248: vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
249: vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
250:
251: /* Switch back buffers */
252: vnc_lock_output(job->vs);
253: if (job->vs->csock == -1) {
254: goto disconnected;
255: }
256:
257: vnc_write(job->vs, vs.output.buffer, vs.output.offset);
258:
259: disconnected:
260: /* Copy persistent encoding data */
261: vnc_async_encoding_end(job->vs, &vs);
262: flush = (job->vs->csock != -1 && job->vs->abort != true);
263: vnc_unlock_output(job->vs);
264:
265: if (flush) {
266: vnc_flush(job->vs);
267: }
268:
269: vnc_lock_queue(queue);
270: QTAILQ_REMOVE(&queue->jobs, job, next);
271: vnc_unlock_queue(queue);
272: qemu_cond_broadcast(&queue->cond);
273: qemu_free(job);
274: return 0;
275: }
276:
277: static VncJobQueue *vnc_queue_init(void)
278: {
279: VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue));
280:
281: qemu_cond_init(&queue->cond);
282: qemu_mutex_init(&queue->mutex);
283: QTAILQ_INIT(&queue->jobs);
284: return queue;
285: }
286:
287: static void vnc_queue_clear(VncJobQueue *q)
288: {
289: qemu_cond_destroy(&queue->cond);
290: qemu_mutex_destroy(&queue->mutex);
291: buffer_free(&queue->buffer);
292: qemu_free(q);
293: queue = NULL; /* Unset global queue */
294: }
295:
296: static void *vnc_worker_thread(void *arg)
297: {
298: VncJobQueue *queue = arg;
299:
300: qemu_thread_self(&queue->thread);
301:
302: while (!vnc_worker_thread_loop(queue)) ;
303: vnc_queue_clear(queue);
304: return NULL;
305: }
306:
307: void vnc_start_worker_thread(void)
308: {
309: VncJobQueue *q;
310:
311: if (vnc_worker_thread_running())
312: return ;
313:
314: q = vnc_queue_init();
315: qemu_thread_create(&q->thread, vnc_worker_thread, q);
316: queue = q; /* Set global queue */
317: }
318:
319: bool vnc_worker_thread_running(void)
320: {
321: return queue; /* Check global queue */
322: }
323:
324: void vnc_stop_worker_thread(void)
325: {
326: if (!vnc_worker_thread_running())
327: return ;
328:
329: /* Remove all jobs and wake up the thread */
330: vnc_lock_queue(queue);
331: queue->exit = true;
332: vnc_unlock_queue(queue);
333: vnc_jobs_clear(NULL);
334: qemu_cond_broadcast(&queue->cond);
335: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.