tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-xcb-shm.c
1 /* Cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2007 Chris Wilson
4  * Copyright © 2009 Intel Corporation
5  *
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.
13  *
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
19  *
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/
24  *
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.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is Red Hat, Inc.
32  *
33  * Contributors(s):
34  *      Chris Wilson <chris@chris-wilson.co.uk>
35  */
36
37 #include "cairoint.h"
38
39 #if CAIRO_HAS_XCB_SHM_FUNCTIONS
40
41 #include "cairo-xcb-private.h"
42 #include "cairo-list-inline.h"
43 #include "cairo-mempool-private.h"
44
45 #include <xcb/shm.h>
46 #include <sys/ipc.h>
47 #include <sys/shm.h>
48 #include <errno.h>
49
50 #define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
51
52 /* a simple buddy allocator for memory pools
53  * XXX fragmentation? use Doug Lea's malloc?
54  */
55
56 typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
57
58 typedef enum {
59     PENDING_WAIT,
60     PENDING_POLL
61 } shm_wait_type_t;
62
63 struct _cairo_xcb_shm_mem_pool {
64     int shmid;
65     uint32_t shmseg;
66     void *shm;
67
68     cairo_mempool_t mem;
69
70     cairo_list_t link;
71 };
72
73 static void
74 _cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
75 {
76     cairo_list_del (&pool->link);
77
78     shmdt (pool->shm);
79     _cairo_mempool_fini (&pool->mem);
80
81     free (pool);
82 }
83
84 static void
85 _cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
86 {
87     cairo_xcb_connection_t *connection = shm_info->connection;
88
89     assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
90
91     _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
92     _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
93
94     /* scan for old, unused pools - hold at least one in reserve */
95     if (! cairo_list_is_singular (&connection->shm_pools))
96     {
97         cairo_xcb_shm_mem_pool_t *pool, *next;
98         cairo_list_t head;
99
100         cairo_list_init (&head);
101         cairo_list_move (connection->shm_pools.next, &head);
102
103         cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
104                                        &connection->shm_pools, link)
105         {
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);
109             }
110         }
111
112         cairo_list_move (head.next, &connection->shm_pools);
113     }
114 }
115
116 static void
117 _cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
118 {
119     cairo_xcb_shm_info_t *info, *next;
120     xcb_get_input_focus_reply_t *reply;
121
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)
125     {
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;
144         }
145
146         free (reply);
147         cairo_list_del (&info->pending);
148         _cairo_xcb_shm_info_finalize (info);
149     }
150 }
151
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)
157 {
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;
164
165     assert (connection->flags & CAIRO_XCB_HAS_SHM);
166
167     CAIRO_MUTEX_LOCK (connection->shm_mutex);
168     _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
169
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);
176
177                 xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
178                 shm_info->sync.sequence = XCB_NONE;
179
180                 *shm_info_out = shm_info;
181                 return CAIRO_STATUS_SUCCESS;
182             }
183         }
184     }
185
186     cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
187                                    &connection->shm_pools, link)
188     {
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;
195             }
196         }
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;
204         }
205     }
206
207     if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
208         CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
209         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
210     }
211
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);
216     }
217
218     bytes = 1 << maxbits;
219     while (bytes <= size)
220         bytes <<= 1, maxbits++;
221     bytes <<= 3;
222
223     do {
224         pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
225         if (pool->shmid != -1)
226             break;
227
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;
241     }
242
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);
249     }
250
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;
258     }
259
260     pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
261     shmctl (pool->shmid, IPC_RMID, NULL);
262
263     cairo_list_add (&pool->link, &connection->shm_pools);
264     mem = _cairo_mempool_alloc (&pool->mem, size);
265
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);
272     }
273
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;
281
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)
285     {
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);
290         }
291     }
292     CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
293
294     *shm_info_out = shm_info;
295     return CAIRO_STATUS_SUCCESS;
296 }
297
298 void
299 _cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
300 {
301     cairo_xcb_connection_t *connection = shm_info->connection;
302
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. */
308
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);
312
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);
316 }
317
318 void
319 _cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
320 {
321     CAIRO_MUTEX_LOCK (connection->shm_mutex);
322     _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
323     CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
324 }
325
326 void
327 _cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
328 {
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));
334     }
335 }
336
337 #endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */