beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-xcb-shm.c
blob2be2dac5bf924a430fab3edeacaa98a7bb2e2c70
1 /* Cairo - a vector graphics library with display and print output
3 * Copyright © 2007 Chris Wilson
4 * Copyright © 2009 Intel Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is Red Hat, Inc.
33 * Contributors(s):
34 * Chris Wilson <chris@chris-wilson.co.uk>
37 #include "cairoint.h"
39 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
41 #include "cairo-xcb-private.h"
42 #include "cairo-list-inline.h"
43 #include "cairo-mempool-private.h"
45 #include <xcb/shm.h>
46 #include <sys/ipc.h>
47 #include <sys/shm.h>
48 #include <errno.h>
50 #define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
52 /* a simple buddy allocator for memory pools
53 * XXX fragmentation? use Doug Lea's malloc?
56 typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
58 typedef enum {
59 PENDING_WAIT,
60 PENDING_POLL
61 } shm_wait_type_t;
63 struct _cairo_xcb_shm_mem_pool {
64 int shmid;
65 uint32_t shmseg;
66 void *shm;
68 cairo_mempool_t mem;
70 cairo_list_t link;
73 static void
74 _cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
76 cairo_list_del (&pool->link);
78 shmdt (pool->shm);
79 _cairo_mempool_fini (&pool->mem);
81 free (pool);
84 static void
85 _cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
87 cairo_xcb_connection_t *connection = shm_info->connection;
89 assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
91 _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
92 _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
94 /* scan for old, unused pools - hold at least one in reserve */
95 if (! cairo_list_is_singular (&connection->shm_pools))
97 cairo_xcb_shm_mem_pool_t *pool, *next;
98 cairo_list_t head;
100 cairo_list_init (&head);
101 cairo_list_move (connection->shm_pools.next, &head);
103 cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
104 &connection->shm_pools, link)
106 if (pool->mem.free_bytes == pool->mem.max_bytes) {
107 _cairo_xcb_connection_shm_detach (connection, pool->shmseg);
108 _cairo_xcb_shm_mem_pool_destroy (pool);
112 cairo_list_move (head.next, &connection->shm_pools);
116 static void
117 _cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
119 cairo_xcb_shm_info_t *info, *next;
120 xcb_get_input_focus_reply_t *reply;
122 assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
123 cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
124 &connection->shm_pending, pending)
126 switch (wait) {
127 case PENDING_WAIT:
128 reply = xcb_wait_for_reply (connection->xcb_connection,
129 info->sync.sequence, NULL);
130 break;
131 case PENDING_POLL:
132 if (! xcb_poll_for_reply (connection->xcb_connection,
133 info->sync.sequence,
134 (void **) &reply, NULL))
135 /* We cannot be sure the server finished with this image yet, so
136 * try again later. All other shm info are guaranteed to have a
137 * larger sequence number and thus don't have to be checked. */
138 return;
139 break;
140 default:
141 /* silence Clang static analyzer warning */
142 ASSERT_NOT_REACHED;
143 reply = NULL;
146 free (reply);
147 cairo_list_del (&info->pending);
148 _cairo_xcb_shm_info_finalize (info);
152 cairo_int_status_t
153 _cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
154 size_t size,
155 cairo_bool_t might_reuse,
156 cairo_xcb_shm_info_t **shm_info_out)
158 cairo_xcb_shm_info_t *shm_info;
159 cairo_xcb_shm_mem_pool_t *pool, *next;
160 size_t bytes, maxbits = 16, minbits = 8;
161 size_t shm_allocated = 0;
162 void *mem = NULL;
163 cairo_status_t status;
165 assert (connection->flags & CAIRO_XCB_HAS_SHM);
167 CAIRO_MUTEX_LOCK (connection->shm_mutex);
168 _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
170 if (might_reuse) {
171 cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
172 &connection->shm_pending, pending) {
173 if (shm_info->size >= size) {
174 cairo_list_del (&shm_info->pending);
175 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
177 xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
178 shm_info->sync.sequence = XCB_NONE;
180 *shm_info_out = shm_info;
181 return CAIRO_STATUS_SUCCESS;
186 cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
187 &connection->shm_pools, link)
189 if (pool->mem.free_bytes > size) {
190 mem = _cairo_mempool_alloc (&pool->mem, size);
191 if (mem != NULL) {
192 /* keep the active pools towards the front */
193 cairo_list_move (&pool->link, &connection->shm_pools);
194 goto allocate_shm_info;
197 /* scan for old, unused pools */
198 if (pool->mem.free_bytes == pool->mem.max_bytes) {
199 _cairo_xcb_connection_shm_detach (connection,
200 pool->shmseg);
201 _cairo_xcb_shm_mem_pool_destroy (pool);
202 } else {
203 shm_allocated += pool->mem.max_bytes;
207 if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
208 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
209 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
212 pool = malloc (sizeof (cairo_xcb_shm_mem_pool_t));
213 if (unlikely (pool == NULL)) {
214 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
215 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
218 bytes = 1 << maxbits;
219 while (bytes <= size)
220 bytes <<= 1, maxbits++;
221 bytes <<= 3;
223 do {
224 pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
225 if (pool->shmid != -1)
226 break;
228 /* If the allocation failed because we asked for too much memory, we try
229 * again with a smaller request, as long as our allocation still fits. */
230 bytes >>= 1;
231 if (errno != EINVAL || bytes < size)
232 break;
233 } while (TRUE);
234 if (pool->shmid == -1) {
235 int err = errno;
236 if (! (err == EINVAL || err == ENOMEM))
237 connection->flags &= ~CAIRO_XCB_HAS_SHM;
238 free (pool);
239 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
240 return CAIRO_INT_STATUS_UNSUPPORTED;
243 pool->shm = shmat (pool->shmid, NULL, 0);
244 if (unlikely (pool->shm == (char *) -1)) {
245 shmctl (pool->shmid, IPC_RMID, NULL);
246 free (pool);
247 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
248 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
251 status = _cairo_mempool_init (&pool->mem, pool->shm, bytes,
252 minbits, maxbits - minbits + 1);
253 if (unlikely (status)) {
254 shmdt (pool->shm);
255 free (pool);
256 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
257 return status;
260 pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
261 shmctl (pool->shmid, IPC_RMID, NULL);
263 cairo_list_add (&pool->link, &connection->shm_pools);
264 mem = _cairo_mempool_alloc (&pool->mem, size);
266 allocate_shm_info:
267 shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
268 if (unlikely (shm_info == NULL)) {
269 _cairo_mempool_free (&pool->mem, mem);
270 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
271 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
274 shm_info->connection = connection;
275 shm_info->pool = pool;
276 shm_info->shm = pool->shmseg;
277 shm_info->size = size;
278 shm_info->offset = (char *) mem - (char *) pool->shm;
279 shm_info->mem = mem;
280 shm_info->sync.sequence = XCB_NONE;
282 /* scan for old, unused pools */
283 cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
284 &connection->shm_pools, link)
286 if (pool->mem.free_bytes == pool->mem.max_bytes) {
287 _cairo_xcb_connection_shm_detach (connection,
288 pool->shmseg);
289 _cairo_xcb_shm_mem_pool_destroy (pool);
292 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
294 *shm_info_out = shm_info;
295 return CAIRO_STATUS_SUCCESS;
298 void
299 _cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
301 cairo_xcb_connection_t *connection = shm_info->connection;
303 /* We can only return shm_info->mem to the allocator when we can be sure
304 * that the X server no longer reads from it. Since the X server processes
305 * requests in order, we send a GetInputFocus here.
306 * _cairo_xcb_shm_process_pending () will later check if the reply for that
307 * request was received and then actually mark this memory area as free. */
309 CAIRO_MUTEX_LOCK (connection->shm_mutex);
310 assert (shm_info->sync.sequence == XCB_NONE);
311 shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
313 cairo_list_init (&shm_info->pending);
314 cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
315 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
318 void
319 _cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
321 CAIRO_MUTEX_LOCK (connection->shm_mutex);
322 _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
323 CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
326 void
327 _cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
329 assert (cairo_list_is_empty (&connection->shm_pending));
330 while (! cairo_list_is_empty (&connection->shm_pools)) {
331 _cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
332 cairo_xcb_shm_mem_pool_t,
333 link));
337 #endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */