Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 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 
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 
46
#include 
47
#include 
48
#include 
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 */