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
/**
40
 * CAIRO_HAS_XCB_SHM_FUNCTIONS:
41
 *
42
 * Defined if Cairo has SHM functions for XCB.
43
 * This macro can be used to conditionally compile backend-specific code.
44
 *
45
 * Since: 1.10
46
 **/
47

            
48
#if CAIRO_HAS_XCB_SHM_FUNCTIONS
49

            
50
#include "cairo-xcb-private.h"
51
#include "cairo-list-inline.h"
52
#include "cairo-mempool-private.h"
53

            
54
#include <xcb/shm.h>
55
#include <sys/ipc.h>
56
#include <sys/shm.h>
57
#include <errno.h>
58

            
59
#define CAIRO_MAX_SHM_MEMORY (16*1024*1024)
60

            
61
/* a simple buddy allocator for memory pools
62
 * XXX fragmentation? use Doug Lea's malloc?
63
 */
64

            
65
typedef struct _cairo_xcb_shm_mem_block cairo_xcb_shm_mem_block_t;
66

            
67
typedef enum {
68
    PENDING_WAIT,
69
    PENDING_POLL
70
} shm_wait_type_t;
71

            
72
struct _cairo_xcb_shm_mem_pool {
73
    int shmid;
74
    uint32_t shmseg;
75
    void *shm;
76

            
77
    cairo_mempool_t mem;
78

            
79
    cairo_list_t link;
80
};
81

            
82
static void
83
_cairo_xcb_shm_mem_pool_destroy (cairo_xcb_shm_mem_pool_t *pool)
84
{
85
    cairo_list_del (&pool->link);
86

            
87
    shmdt (pool->shm);
88
    _cairo_mempool_fini (&pool->mem);
89

            
90
    free (pool);
91
}
92

            
93
static void
94
_cairo_xcb_shm_info_finalize (cairo_xcb_shm_info_t *shm_info)
95
{
96
    cairo_xcb_connection_t *connection = shm_info->connection;
97

            
98
    assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
99

            
100
    _cairo_mempool_free (&shm_info->pool->mem, shm_info->mem);
101
    _cairo_freepool_free (&connection->shm_info_freelist, shm_info);
102

            
103
    /* scan for old, unused pools - hold at least one in reserve */
104
    if (! cairo_list_is_singular (&connection->shm_pools))
105
    {
106
	cairo_xcb_shm_mem_pool_t *pool, *next;
107
	cairo_list_t head;
108

            
109
	cairo_list_init (&head);
110
	cairo_list_move (connection->shm_pools.next, &head);
111

            
112
	cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
113
				       &connection->shm_pools, link)
114
	{
115
	    if (pool->mem.free_bytes == pool->mem.max_bytes) {
116
		_cairo_xcb_connection_shm_detach (connection, pool->shmseg);
117
		_cairo_xcb_shm_mem_pool_destroy (pool);
118
	    }
119
	}
120

            
121
	cairo_list_move (head.next, &connection->shm_pools);
122
    }
123
}
124

            
125
static void
126
8
_cairo_xcb_shm_process_pending (cairo_xcb_connection_t *connection, shm_wait_type_t wait)
127
{
128
    cairo_xcb_shm_info_t *info, *next;
129
    xcb_get_input_focus_reply_t *reply;
130

            
131
    assert (CAIRO_MUTEX_IS_LOCKED (connection->shm_mutex));
132
8
    cairo_list_foreach_entry_safe (info, next, cairo_xcb_shm_info_t,
133
				   &connection->shm_pending, pending)
134
    {
135
	switch (wait) {
136
	case PENDING_WAIT:
137
	     reply = xcb_wait_for_reply (connection->xcb_connection,
138
					 info->sync.sequence, NULL);
139
	     break;
140
	case PENDING_POLL:
141
	    if (! xcb_poll_for_reply (connection->xcb_connection,
142
				      info->sync.sequence,
143
				      (void **) &reply, NULL))
144
		/* We cannot be sure the server finished with this image yet, so
145
		 * try again later. All other shm info are guaranteed to have a
146
		 * larger sequence number and thus don't have to be checked. */
147
		return;
148
	    break;
149
	default:
150
	    /* silence Clang static analyzer warning */
151
	    ASSERT_NOT_REACHED;
152
	    reply = NULL;
153
	}
154

            
155
	free (reply);
156
	cairo_list_del (&info->pending);
157
	_cairo_xcb_shm_info_finalize (info);
158
    }
159
}
160

            
161
cairo_int_status_t
162
_cairo_xcb_connection_allocate_shm_info (cairo_xcb_connection_t *connection,
163
					 size_t size,
164
					 cairo_bool_t might_reuse,
165
					 cairo_xcb_shm_info_t **shm_info_out)
166
{
167
    cairo_xcb_shm_info_t *shm_info;
168
    cairo_xcb_shm_mem_pool_t *pool, *next;
169
    size_t bytes, maxbits = 16, minbits = 8;
170
    size_t shm_allocated = 0;
171
    void *mem = NULL;
172
    cairo_status_t status;
173

            
174
    assert (connection->flags & CAIRO_XCB_HAS_SHM);
175

            
176
    CAIRO_MUTEX_LOCK (connection->shm_mutex);
177
    _cairo_xcb_shm_process_pending (connection, PENDING_POLL);
178

            
179
    if (might_reuse) {
180
	cairo_list_foreach_entry (shm_info, cairo_xcb_shm_info_t,
181
		&connection->shm_pending, pending) {
182
	    if (shm_info->size >= size) {
183
		cairo_list_del (&shm_info->pending);
184
		CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
185

            
186
		xcb_discard_reply (connection->xcb_connection, shm_info->sync.sequence);
187
		shm_info->sync.sequence = XCB_NONE;
188

            
189
		*shm_info_out = shm_info;
190
		return CAIRO_STATUS_SUCCESS;
191
	    }
192
	}
193
    }
194

            
195
    cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
196
				   &connection->shm_pools, link)
197
    {
198
	if (pool->mem.free_bytes > size) {
199
	    mem = _cairo_mempool_alloc (&pool->mem, size);
200
	    if (mem != NULL) {
201
		/* keep the active pools towards the front */
202
		cairo_list_move (&pool->link, &connection->shm_pools);
203
		goto allocate_shm_info;
204
	    }
205
	}
206
	/* scan for old, unused pools */
207
	if (pool->mem.free_bytes == pool->mem.max_bytes) {
208
	    _cairo_xcb_connection_shm_detach (connection,
209
					      pool->shmseg);
210
	    _cairo_xcb_shm_mem_pool_destroy (pool);
211
	} else {
212
	    shm_allocated += pool->mem.max_bytes;
213
	}
214
    }
215

            
216
    if (unlikely (shm_allocated >= CAIRO_MAX_SHM_MEMORY)) {
217
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
218
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
219
    }
220

            
221
    pool = _cairo_calloc (sizeof (cairo_xcb_shm_mem_pool_t));
222
    if (unlikely (pool == NULL)) {
223
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
224
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
225
    }
226

            
227
    bytes = 1 << maxbits;
228
    while (bytes <= size)
229
	bytes <<= 1, maxbits++;
230
    bytes <<= 3;
231

            
232
    do {
233
	pool->shmid = shmget (IPC_PRIVATE, bytes, IPC_CREAT | 0600);
234
	if (pool->shmid != -1)
235
	    break;
236

            
237
	/* If the allocation failed because we asked for too much memory, we try
238
	 * again with a smaller request, as long as our allocation still fits. */
239
	bytes >>= 1;
240
	if (errno != EINVAL || bytes < size)
241
	    break;
242
    } while (TRUE);
243
    if (pool->shmid == -1) {
244
	int err = errno;
245
	if (! (err == EINVAL || err == ENOMEM))
246
	    connection->flags &= ~CAIRO_XCB_HAS_SHM;
247
	free (pool);
248
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
249
	return CAIRO_INT_STATUS_UNSUPPORTED;
250
    }
251

            
252
    pool->shm = shmat (pool->shmid, NULL, 0);
253
    if (unlikely (pool->shm == (char *) -1)) {
254
	shmctl (pool->shmid, IPC_RMID, NULL);
255
	free (pool);
256
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
257
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
258
    }
259

            
260
    status = _cairo_mempool_init (&pool->mem, pool->shm, bytes,
261
				  minbits, maxbits - minbits + 1);
262
    if (unlikely (status)) {
263
	shmdt (pool->shm);
264
	free (pool);
265
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
266
	return status;
267
    }
268

            
269
    pool->shmseg = _cairo_xcb_connection_shm_attach (connection, pool->shmid, FALSE);
270
    shmctl (pool->shmid, IPC_RMID, NULL);
271

            
272
    cairo_list_add (&pool->link, &connection->shm_pools);
273
    mem = _cairo_mempool_alloc (&pool->mem, size);
274

            
275
  allocate_shm_info:
276
    shm_info = _cairo_freepool_alloc (&connection->shm_info_freelist);
277
    if (unlikely (shm_info == NULL)) {
278
	_cairo_mempool_free (&pool->mem, mem);
279
	CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
280
	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
281
    }
282

            
283
    shm_info->connection = connection;
284
    shm_info->pool = pool;
285
    shm_info->shm = pool->shmseg;
286
    shm_info->size = size;
287
    shm_info->offset = (char *) mem - (char *) pool->shm;
288
    shm_info->mem = mem;
289
    shm_info->sync.sequence = XCB_NONE;
290

            
291
    /* scan for old, unused pools */
292
    cairo_list_foreach_entry_safe (pool, next, cairo_xcb_shm_mem_pool_t,
293
				   &connection->shm_pools, link)
294
    {
295
	if (pool->mem.free_bytes == pool->mem.max_bytes) {
296
	    _cairo_xcb_connection_shm_detach (connection,
297
					      pool->shmseg);
298
	    _cairo_xcb_shm_mem_pool_destroy (pool);
299
	}
300
    }
301
    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
302

            
303
    *shm_info_out = shm_info;
304
    return CAIRO_STATUS_SUCCESS;
305
}
306

            
307
void
308
_cairo_xcb_shm_info_destroy (cairo_xcb_shm_info_t *shm_info)
309
{
310
    cairo_xcb_connection_t *connection = shm_info->connection;
311

            
312
    /* We can only return shm_info->mem to the allocator when we can be sure
313
     * that the X server no longer reads from it. Since the X server processes
314
     * requests in order, we send a GetInputFocus here.
315
     * _cairo_xcb_shm_process_pending () will later check if the reply for that
316
     * request was received and then actually mark this memory area as free. */
317

            
318
    CAIRO_MUTEX_LOCK (connection->shm_mutex);
319
    assert (shm_info->sync.sequence == XCB_NONE);
320
    shm_info->sync = xcb_get_input_focus (connection->xcb_connection);
321

            
322
    cairo_list_init (&shm_info->pending);
323
    cairo_list_add_tail (&shm_info->pending, &connection->shm_pending);
324
    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
325
}
326

            
327
void
328
8
_cairo_xcb_connection_shm_mem_pools_flush (cairo_xcb_connection_t *connection)
329
{
330
8
    CAIRO_MUTEX_LOCK (connection->shm_mutex);
331
8
    _cairo_xcb_shm_process_pending (connection, PENDING_WAIT);
332
8
    CAIRO_MUTEX_UNLOCK (connection->shm_mutex);
333
8
}
334

            
335
void
336
4
_cairo_xcb_connection_shm_mem_pools_fini (cairo_xcb_connection_t *connection)
337
{
338
4
    assert (cairo_list_is_empty (&connection->shm_pending));
339
4
    while (! cairo_list_is_empty (&connection->shm_pools)) {
340
	_cairo_xcb_shm_mem_pool_destroy (cairo_list_first_entry (&connection->shm_pools,
341
								 cairo_xcb_shm_mem_pool_t,
342
								 link));
343
    }
344
4
}
345

            
346
#endif /* CAIRO_HAS_XCB_SHM_FUNCTIONS */