Branch data Line data Source code
1 : : /***
2 : : This file is part of PulseAudio.
3 : :
4 : : Copyright 2004-2006 Lennart Poettering
5 : : Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6 : :
7 : : PulseAudio is free software; you can redistribute it and/or modify
8 : : it under the terms of the GNU Lesser General Public License as
9 : : published by the Free Software Foundation; either version 2.1 of the
10 : : License, or (at your option) any later version.
11 : :
12 : : PulseAudio is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : Lesser General Public License for more details
16 : :
17 : : You should have received a copy of the GNU Lesser General Public
18 : : License along with PulseAudio; if not, write to the Free Software
19 : : Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 : : USA.
21 : : ***/
22 : :
23 : : #ifdef HAVE_CONFIG_H
24 : : #include <config.h>
25 : : #endif
26 : :
27 : : #include <stdio.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : : #include <unistd.h>
31 : : #include <signal.h>
32 : : #include <errno.h>
33 : :
34 : : #ifdef HAVE_VALGRIND_MEMCHECK_H
35 : : #include <valgrind/memcheck.h>
36 : : #endif
37 : :
38 : : #include <pulse/xmalloc.h>
39 : : #include <pulse/def.h>
40 : :
41 : : #include <pulsecore/shm.h>
42 : : #include <pulsecore/log.h>
43 : : #include <pulsecore/hashmap.h>
44 : : #include <pulsecore/semaphore.h>
45 : : #include <pulsecore/mutex.h>
46 : : #include <pulsecore/macro.h>
47 : : #include <pulsecore/refcnt.h>
48 : : #include <pulsecore/llist.h>
49 : : #include <pulsecore/flist.h>
50 : : #include <pulsecore/core-util.h>
51 : : #include <pulsecore/memtrap.h>
52 : :
53 : : #include "memblock.h"
54 : :
55 : : /* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
56 : : * note that the footprint is usually much smaller, since the data is
57 : : * stored in SHM and our OS does not commit the memory before we use
58 : : * it for the first time. */
59 : : #define PA_MEMPOOL_SLOTS_MAX 1024
60 : : #define PA_MEMPOOL_SLOT_SIZE (64*1024)
61 : :
62 : : #define PA_MEMEXPORT_SLOTS_MAX 128
63 : :
64 : : #define PA_MEMIMPORT_SLOTS_MAX 160
65 : : #define PA_MEMIMPORT_SEGMENTS_MAX 16
66 : :
67 : : struct pa_memblock {
68 : : PA_REFCNT_DECLARE; /* the reference counter */
69 : : pa_mempool *pool;
70 : :
71 : : pa_memblock_type_t type;
72 : :
73 : : pa_bool_t read_only:1;
74 : : pa_bool_t is_silence:1;
75 : :
76 : : pa_atomic_ptr_t data;
77 : : size_t length;
78 : :
79 : : pa_atomic_t n_acquired;
80 : : pa_atomic_t please_signal;
81 : :
82 : : union {
83 : : struct {
84 : : /* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
85 : : pa_free_cb_t free_cb;
86 : : } user;
87 : :
88 : : struct {
89 : : uint32_t id;
90 : : pa_memimport_segment *segment;
91 : : } imported;
92 : : } per_type;
93 : : };
94 : :
95 : : struct pa_memimport_segment {
96 : : pa_memimport *import;
97 : : pa_shm memory;
98 : : pa_memtrap *trap;
99 : : unsigned n_blocks;
100 : : };
101 : :
102 : : /* A collection of multiple segments */
103 : : struct pa_memimport {
104 : : pa_mutex *mutex;
105 : :
106 : : pa_mempool *pool;
107 : : pa_hashmap *segments;
108 : : pa_hashmap *blocks;
109 : :
110 : : /* Called whenever an imported memory block is no longer
111 : : * needed. */
112 : : pa_memimport_release_cb_t release_cb;
113 : : void *userdata;
114 : :
115 : : PA_LLIST_FIELDS(pa_memimport);
116 : : };
117 : :
118 : : struct memexport_slot {
119 : : PA_LLIST_FIELDS(struct memexport_slot);
120 : : pa_memblock *block;
121 : : };
122 : :
123 : : struct pa_memexport {
124 : : pa_mutex *mutex;
125 : : pa_mempool *pool;
126 : :
127 : : struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
128 : :
129 : : PA_LLIST_HEAD(struct memexport_slot, free_slots);
130 : : PA_LLIST_HEAD(struct memexport_slot, used_slots);
131 : : unsigned n_init;
132 : :
133 : : /* Called whenever a client from which we imported a memory block
134 : : which we in turn exported to another client dies and we need to
135 : : revoke the memory block accordingly */
136 : : pa_memexport_revoke_cb_t revoke_cb;
137 : : void *userdata;
138 : :
139 : : PA_LLIST_FIELDS(pa_memexport);
140 : : };
141 : :
142 : : struct pa_mempool {
143 : : pa_semaphore *semaphore;
144 : : pa_mutex *mutex;
145 : :
146 : : pa_shm memory;
147 : : size_t block_size;
148 : : unsigned n_blocks;
149 : :
150 : : pa_atomic_t n_init;
151 : :
152 : : PA_LLIST_HEAD(pa_memimport, imports);
153 : : PA_LLIST_HEAD(pa_memexport, exports);
154 : :
155 : : /* A list of free slots that may be reused */
156 : : pa_flist *free_slots;
157 : :
158 : : pa_mempool_stat stat;
159 : : };
160 : :
161 : : static void segment_detach(pa_memimport_segment *seg);
162 : :
163 [ - + ][ # # ]: 59 : PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
164 : :
165 : : /* No lock necessary */
166 : 789 : static void stat_add(pa_memblock*b) {
167 [ - + ]: 789 : pa_assert(b);
168 [ - + ]: 789 : pa_assert(b->pool);
169 : :
170 : 789 : pa_atomic_inc(&b->pool->stat.n_allocated);
171 : 789 : pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
172 : :
173 : 789 : pa_atomic_inc(&b->pool->stat.n_accumulated);
174 : 789 : pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
175 : :
176 [ + + ]: 789 : if (b->type == PA_MEMBLOCK_IMPORTED) {
177 : 8 : pa_atomic_inc(&b->pool->stat.n_imported);
178 : 8 : pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
179 : : }
180 : :
181 : 789 : pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
182 : 789 : pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
183 : 789 : }
184 : :
185 : : /* No lock necessary */
186 : 789 : static void stat_remove(pa_memblock *b) {
187 [ - + ]: 789 : pa_assert(b);
188 [ - + ]: 789 : pa_assert(b->pool);
189 : :
190 [ - + ]: 789 : pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
191 [ - + ]: 789 : pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
192 : :
193 : 789 : pa_atomic_dec(&b->pool->stat.n_allocated);
194 : 789 : pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
195 : :
196 [ + + ]: 789 : if (b->type == PA_MEMBLOCK_IMPORTED) {
197 [ - + ]: 8 : pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
198 [ - + ]: 8 : pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
199 : :
200 : 8 : pa_atomic_dec(&b->pool->stat.n_imported);
201 : 8 : pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
202 : : }
203 : :
204 : 789 : pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
205 : 789 : }
206 : :
207 : : static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
208 : :
209 : : /* No lock necessary */
210 : 771 : pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
211 : : pa_memblock *b;
212 : :
213 [ - + ]: 771 : pa_assert(p);
214 [ - + ]: 771 : pa_assert(length);
215 : :
216 [ - + ]: 771 : if (!(b = pa_memblock_new_pool(p, length)))
217 : 0 : b = memblock_new_appended(p, length);
218 : :
219 : 771 : return b;
220 : : }
221 : :
222 : : /* No lock necessary */
223 : 0 : static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
224 : : pa_memblock *b;
225 : :
226 [ # # ]: 0 : pa_assert(p);
227 [ # # ]: 0 : pa_assert(length);
228 : :
229 : : /* If -1 is passed as length we choose the size for the caller. */
230 : :
231 [ # # ]: 0 : if (length == (size_t) -1)
232 : 0 : length = pa_mempool_block_size_max(p);
233 : :
234 : 0 : b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
235 : 0 : PA_REFCNT_INIT(b);
236 : 0 : b->pool = p;
237 : 0 : b->type = PA_MEMBLOCK_APPENDED;
238 : 0 : b->read_only = b->is_silence = FALSE;
239 : 0 : pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
240 : 0 : b->length = length;
241 : 0 : pa_atomic_store(&b->n_acquired, 0);
242 : 0 : pa_atomic_store(&b->please_signal, 0);
243 : :
244 : 0 : stat_add(b);
245 : 0 : return b;
246 : : }
247 : :
248 : : /* No lock necessary */
249 : 774 : static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
250 : : struct mempool_slot *slot;
251 [ - + ]: 774 : pa_assert(p);
252 : :
253 [ + + ]: 774 : if (!(slot = pa_flist_pop(p->free_slots))) {
254 : : int idx;
255 : :
256 : : /* The free list was empty, we have to allocate a new entry */
257 : :
258 [ - + ]: 12 : if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
259 : 0 : pa_atomic_dec(&p->n_init);
260 : : else
261 : 12 : slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
262 : :
263 [ - + ]: 12 : if (!slot) {
264 [ # # ]: 0 : if (pa_log_ratelimit(PA_LOG_DEBUG))
265 : 0 : pa_log_debug("Pool full");
266 : 0 : pa_atomic_inc(&p->stat.n_pool_full);
267 : 0 : return NULL;
268 : : }
269 : : }
270 : :
271 : : /* #ifdef HAVE_VALGRIND_MEMCHECK_H */
272 : : /* if (PA_UNLIKELY(pa_in_valgrind())) { */
273 : : /* VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
274 : : /* } */
275 : : /* #endif */
276 : :
277 : 774 : return slot;
278 : : }
279 : :
280 : : /* No lock necessary, totally redundant anyway */
281 : : static inline void* mempool_slot_data(struct mempool_slot *slot) {
282 : : return slot;
283 : : }
284 : :
285 : : /* No lock necessary */
286 : 774 : static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
287 [ - + ]: 774 : pa_assert(p);
288 : :
289 [ - + ]: 774 : pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
290 [ - + ]: 774 : pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
291 : :
292 : 774 : return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
293 : : }
294 : :
295 : : /* No lock necessary */
296 : : static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
297 : : unsigned idx;
298 : :
299 [ + - ]: 774 : if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1)
300 : : return NULL;
301 : :
302 : 774 : return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
303 : : }
304 : :
305 : : /* No lock necessary */
306 : 774 : pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
307 : 774 : pa_memblock *b = NULL;
308 : : struct mempool_slot *slot;
309 : : static int mempool_disable = 0;
310 : :
311 [ - + ]: 774 : pa_assert(p);
312 [ - + ]: 774 : pa_assert(length);
313 : :
314 [ + + ]: 774 : if (mempool_disable == 0)
315 [ + - ]: 4 : mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1;
316 : :
317 [ + - ]: 774 : if (mempool_disable > 0)
318 : : return NULL;
319 : :
320 : : /* If -1 is passed as length we choose the size for the caller: we
321 : : * take the largest size that fits in one of our slots. */
322 : :
323 [ - + ]: 774 : if (length == (size_t) -1)
324 : 0 : length = pa_mempool_block_size_max(p);
325 : :
326 [ + - ]: 774 : if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
327 : :
328 [ + - ]: 774 : if (!(slot = mempool_allocate_slot(p)))
329 : : return NULL;
330 : :
331 : 774 : b = mempool_slot_data(slot);
332 : 774 : b->type = PA_MEMBLOCK_POOL;
333 : 774 : pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
334 : :
335 [ # # ]: 0 : } else if (p->block_size >= length) {
336 : :
337 [ # # ]: 0 : if (!(slot = mempool_allocate_slot(p)))
338 : : return NULL;
339 : :
340 [ # # ]: 0 : if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
341 : 0 : b = pa_xnew(pa_memblock, 1);
342 : :
343 : 0 : b->type = PA_MEMBLOCK_POOL_EXTERNAL;
344 : 0 : pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
345 : :
346 : : } else {
347 : 0 : pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
348 : 0 : pa_atomic_inc(&p->stat.n_too_large_for_pool);
349 : 0 : return NULL;
350 : : }
351 : :
352 : 774 : PA_REFCNT_INIT(b);
353 : 774 : b->pool = p;
354 : 774 : b->read_only = b->is_silence = FALSE;
355 : 774 : b->length = length;
356 : 774 : pa_atomic_store(&b->n_acquired, 0);
357 : 774 : pa_atomic_store(&b->please_signal, 0);
358 : :
359 : 774 : stat_add(b);
360 : 774 : return b;
361 : : }
362 : :
363 : : /* No lock necessary */
364 : 6 : pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
365 : : pa_memblock *b;
366 : :
367 [ - + ]: 6 : pa_assert(p);
368 [ - + ]: 6 : pa_assert(d);
369 [ - + ]: 6 : pa_assert(length != (size_t) -1);
370 [ - + ]: 6 : pa_assert(length);
371 : :
372 [ + - ]: 6 : if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
373 : 6 : b = pa_xnew(pa_memblock, 1);
374 : :
375 : 6 : PA_REFCNT_INIT(b);
376 : 6 : b->pool = p;
377 : 6 : b->type = PA_MEMBLOCK_FIXED;
378 : 6 : b->read_only = read_only;
379 : 6 : b->is_silence = FALSE;
380 : 6 : pa_atomic_ptr_store(&b->data, d);
381 : 6 : b->length = length;
382 : 6 : pa_atomic_store(&b->n_acquired, 0);
383 : 6 : pa_atomic_store(&b->please_signal, 0);
384 : :
385 : 6 : stat_add(b);
386 : 6 : return b;
387 : : }
388 : :
389 : : /* No lock necessary */
390 : 1 : pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
391 : : pa_memblock *b;
392 : :
393 [ - + ]: 1 : pa_assert(p);
394 [ - + ]: 1 : pa_assert(d);
395 [ - + ]: 1 : pa_assert(length);
396 [ - + ]: 1 : pa_assert(length != (size_t) -1);
397 [ - + ]: 1 : pa_assert(free_cb);
398 : :
399 [ + - ]: 1 : if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
400 : 1 : b = pa_xnew(pa_memblock, 1);
401 : :
402 : 1 : PA_REFCNT_INIT(b);
403 : 1 : b->pool = p;
404 : 1 : b->type = PA_MEMBLOCK_USER;
405 : 1 : b->read_only = read_only;
406 : 1 : b->is_silence = FALSE;
407 : 1 : pa_atomic_ptr_store(&b->data, d);
408 : 1 : b->length = length;
409 : 1 : pa_atomic_store(&b->n_acquired, 0);
410 : 1 : pa_atomic_store(&b->please_signal, 0);
411 : :
412 : 1 : b->per_type.user.free_cb = free_cb;
413 : :
414 : 1 : stat_add(b);
415 : 1 : return b;
416 : : }
417 : :
418 : : /* No lock necessary */
419 : 0 : pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
420 [ # # ]: 0 : pa_assert(b);
421 [ # # ]: 0 : pa_assert(PA_REFCNT_VALUE(b) > 0);
422 : :
423 [ # # ][ # # ]: 0 : return b->read_only && PA_REFCNT_VALUE(b) == 1;
424 : : }
425 : :
426 : : /* No lock necessary */
427 : 13 : pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
428 [ - + ]: 13 : pa_assert(b);
429 [ - + ]: 13 : pa_assert(PA_REFCNT_VALUE(b) > 0);
430 : :
431 : 13 : return b->is_silence;
432 : : }
433 : :
434 : : /* No lock necessary */
435 : 0 : void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
436 [ # # ]: 0 : pa_assert(b);
437 [ # # ]: 0 : pa_assert(PA_REFCNT_VALUE(b) > 0);
438 : :
439 : 0 : b->is_silence = v;
440 : 0 : }
441 : :
442 : : /* No lock necessary */
443 : 13 : pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
444 : : int r;
445 [ - + ]: 13 : pa_assert(b);
446 : :
447 [ - + ]: 13 : pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
448 : :
449 : 13 : return r == 1;
450 : : }
451 : :
452 : : /* No lock necessary */
453 : 1579 : void* pa_memblock_acquire(pa_memblock *b) {
454 [ - + ]: 1579 : pa_assert(b);
455 [ - + ]: 1579 : pa_assert(PA_REFCNT_VALUE(b) > 0);
456 : :
457 : 1579 : pa_atomic_inc(&b->n_acquired);
458 : :
459 : 1579 : return pa_atomic_ptr_load(&b->data);
460 : : }
461 : :
462 : : /* No lock necessary, in corner cases locks by its own */
463 : 1579 : void pa_memblock_release(pa_memblock *b) {
464 : : int r;
465 [ - + ]: 1579 : pa_assert(b);
466 [ - + ]: 1579 : pa_assert(PA_REFCNT_VALUE(b) > 0);
467 : :
468 : 1579 : r = pa_atomic_dec(&b->n_acquired);
469 [ - + ]: 1579 : pa_assert(r >= 1);
470 : :
471 : : /* Signal a waiting thread that this memblock is no longer used */
472 [ + - ][ - + ]: 1579 : if (r == 1 && pa_atomic_load(&b->please_signal))
473 : 0 : pa_semaphore_post(b->pool->semaphore);
474 : 1579 : }
475 : :
476 : 197 : size_t pa_memblock_get_length(pa_memblock *b) {
477 [ - + ]: 197 : pa_assert(b);
478 [ - + ]: 197 : pa_assert(PA_REFCNT_VALUE(b) > 0);
479 : :
480 : 197 : return b->length;
481 : : }
482 : :
483 : 15 : pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
484 [ - + ]: 15 : pa_assert(b);
485 [ - + ]: 15 : pa_assert(PA_REFCNT_VALUE(b) > 0);
486 : :
487 : 15 : return b->pool;
488 : : }
489 : :
490 : : /* No lock necessary */
491 : 116 : pa_memblock* pa_memblock_ref(pa_memblock*b) {
492 [ - + ]: 116 : pa_assert(b);
493 [ - + ]: 116 : pa_assert(PA_REFCNT_VALUE(b) > 0);
494 : :
495 : 116 : PA_REFCNT_INC(b);
496 : 116 : return b;
497 : : }
498 : :
499 : 789 : static void memblock_free(pa_memblock *b) {
500 [ - + ]: 789 : pa_assert(b);
501 : :
502 [ - + ]: 789 : pa_assert(pa_atomic_load(&b->n_acquired) == 0);
503 : :
504 : 789 : stat_remove(b);
505 : :
506 [ + + - + : 789 : switch (b->type) {
+ - ]
507 : : case PA_MEMBLOCK_USER :
508 [ - + ]: 1 : pa_assert(b->per_type.user.free_cb);
509 : 1 : b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
510 : :
511 : : /* Fall through */
512 : :
513 : : case PA_MEMBLOCK_FIXED:
514 [ - + ]: 7 : if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
515 : 0 : pa_xfree(b);
516 : :
517 : : break;
518 : :
519 : : case PA_MEMBLOCK_APPENDED:
520 : :
521 : : /* We could attach it to unused_memblocks, but that would
522 : : * probably waste some considerable amount of memory */
523 : 0 : pa_xfree(b);
524 : 0 : break;
525 : :
526 : : case PA_MEMBLOCK_IMPORTED: {
527 : : pa_memimport_segment *segment;
528 : : pa_memimport *import;
529 : :
530 : : /* FIXME! This should be implemented lock-free */
531 : :
532 [ - + ]: 8 : pa_assert_se(segment = b->per_type.imported.segment);
533 [ - + ]: 8 : pa_assert_se(import = segment->import);
534 : :
535 : 8 : pa_mutex_lock(import->mutex);
536 : :
537 [ - + ]: 8 : pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
538 : :
539 [ - + ]: 8 : pa_assert(segment->n_blocks >= 1);
540 [ + - ]: 8 : if (-- segment->n_blocks <= 0)
541 : 8 : segment_detach(segment);
542 : :
543 : 8 : pa_mutex_unlock(import->mutex);
544 : :
545 : 8 : import->release_cb(import, b->per_type.imported.id, import->userdata);
546 : :
547 [ - + ]: 8 : if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
548 : 0 : pa_xfree(b);
549 : :
550 : : break;
551 : : }
552 : :
553 : : case PA_MEMBLOCK_POOL_EXTERNAL:
554 : : case PA_MEMBLOCK_POOL: {
555 : : struct mempool_slot *slot;
556 : : pa_bool_t call_free;
557 : :
558 [ - + ]: 774 : pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)));
559 : :
560 : 774 : call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
561 : :
562 : : /* #ifdef HAVE_VALGRIND_MEMCHECK_H */
563 : : /* if (PA_UNLIKELY(pa_in_valgrind())) { */
564 : : /* VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
565 : : /* } */
566 : : /* #endif */
567 : :
568 : : /* The free list dimensions should easily allow all slots
569 : : * to fit in, hence try harder if pushing this slot into
570 : : * the free list fails */
571 [ - + ]: 774 : while (pa_flist_push(b->pool->free_slots, slot) < 0)
572 : : ;
573 : :
574 [ - + ]: 774 : if (call_free)
575 [ # # ]: 0 : if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
576 : 0 : pa_xfree(b);
577 : :
578 : : break;
579 : : }
580 : :
581 : : case PA_MEMBLOCK_TYPE_MAX:
582 : : default:
583 : 0 : pa_assert_not_reached();
584 : : }
585 : 789 : }
586 : :
587 : : /* No lock necessary */
588 : 905 : void pa_memblock_unref(pa_memblock*b) {
589 [ - + ]: 905 : pa_assert(b);
590 [ - + ]: 905 : pa_assert(PA_REFCNT_VALUE(b) > 0);
591 : :
592 [ + + ]: 905 : if (PA_REFCNT_DEC(b) > 0)
593 : 905 : return;
594 : :
595 : 789 : memblock_free(b);
596 : : }
597 : :
598 : : /* Self locked */
599 : : static void memblock_wait(pa_memblock *b) {
600 [ # # ]: 0 : pa_assert(b);
601 : :
602 [ # # ]: 0 : if (pa_atomic_load(&b->n_acquired) > 0) {
603 : : /* We need to wait until all threads gave up access to the
604 : : * memory block before we can go on. Unfortunately this means
605 : : * that we have to lock and wait here. Sniff! */
606 : :
607 : 0 : pa_atomic_inc(&b->please_signal);
608 : :
609 [ # # ]: 0 : while (pa_atomic_load(&b->n_acquired) > 0)
610 : 0 : pa_semaphore_wait(b->pool->semaphore);
611 : :
612 : 0 : pa_atomic_dec(&b->please_signal);
613 : : }
614 : : }
615 : :
616 : : /* No lock necessary. This function is not multiple caller safe! */
617 : 0 : static void memblock_make_local(pa_memblock *b) {
618 [ # # ]: 0 : pa_assert(b);
619 : :
620 : 0 : pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
621 : :
622 [ # # ]: 0 : if (b->length <= b->pool->block_size) {
623 : : struct mempool_slot *slot;
624 : :
625 [ # # ]: 0 : if ((slot = mempool_allocate_slot(b->pool))) {
626 : : void *new_data;
627 : : /* We can move it into a local pool, perfect! */
628 : :
629 : 0 : new_data = mempool_slot_data(slot);
630 : 0 : memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
631 : 0 : pa_atomic_ptr_store(&b->data, new_data);
632 : :
633 : 0 : b->type = PA_MEMBLOCK_POOL_EXTERNAL;
634 : 0 : b->read_only = FALSE;
635 : :
636 : 0 : goto finish;
637 : : }
638 : : }
639 : :
640 : : /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
641 : 0 : b->per_type.user.free_cb = pa_xfree;
642 : 0 : pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
643 : :
644 : 0 : b->type = PA_MEMBLOCK_USER;
645 : 0 : b->read_only = FALSE;
646 : :
647 : : finish:
648 : 0 : pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
649 : 0 : pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
650 : : memblock_wait(b);
651 : 0 : }
652 : :
653 : : /* No lock necessary. This function is not multiple caller safe*/
654 : 0 : void pa_memblock_unref_fixed(pa_memblock *b) {
655 [ # # ]: 0 : pa_assert(b);
656 [ # # ]: 0 : pa_assert(PA_REFCNT_VALUE(b) > 0);
657 [ # # ]: 0 : pa_assert(b->type == PA_MEMBLOCK_FIXED);
658 : :
659 [ # # ]: 0 : if (PA_REFCNT_VALUE(b) > 1)
660 : 0 : memblock_make_local(b);
661 : :
662 : 0 : pa_memblock_unref(b);
663 : 0 : }
664 : :
665 : : /* No lock necessary. */
666 : 0 : pa_memblock *pa_memblock_will_need(pa_memblock *b) {
667 : : void *p;
668 : :
669 [ # # ]: 0 : pa_assert(b);
670 [ # # ]: 0 : pa_assert(PA_REFCNT_VALUE(b) > 0);
671 : :
672 : 0 : p = pa_memblock_acquire(b);
673 : 0 : pa_will_need(p, b->length);
674 : 0 : pa_memblock_release(b);
675 : :
676 : 0 : return b;
677 : : }
678 : :
679 : : /* Self-locked. This function is not multiple-caller safe */
680 : 0 : static void memblock_replace_import(pa_memblock *b) {
681 : : pa_memimport_segment *segment;
682 : : pa_memimport *import;
683 : :
684 [ # # ]: 0 : pa_assert(b);
685 [ # # ]: 0 : pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
686 : :
687 [ # # ]: 0 : pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
688 [ # # ]: 0 : pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
689 : 0 : pa_atomic_dec(&b->pool->stat.n_imported);
690 : 0 : pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
691 : :
692 [ # # ]: 0 : pa_assert_se(segment = b->per_type.imported.segment);
693 [ # # ]: 0 : pa_assert_se(import = segment->import);
694 : :
695 : 0 : pa_mutex_lock(import->mutex);
696 : :
697 [ # # ]: 0 : pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
698 : :
699 : 0 : memblock_make_local(b);
700 : :
701 [ # # ]: 0 : pa_assert(segment->n_blocks >= 1);
702 [ # # ]: 0 : if (-- segment->n_blocks <= 0)
703 : 0 : segment_detach(segment);
704 : :
705 : 0 : pa_mutex_unlock(import->mutex);
706 : 0 : }
707 : :
708 : 7 : pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
709 : : pa_mempool *p;
710 : : char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
711 : :
712 : 7 : p = pa_xnew(pa_mempool, 1);
713 : :
714 : 7 : p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
715 [ - + ]: 7 : if (p->block_size < PA_PAGE_SIZE)
716 : 0 : p->block_size = PA_PAGE_SIZE;
717 : :
718 [ + - ]: 7 : if (size <= 0)
719 : 7 : p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
720 : : else {
721 : 0 : p->n_blocks = (unsigned) (size / p->block_size);
722 : :
723 [ # # ]: 0 : if (p->n_blocks < 2)
724 : 0 : p->n_blocks = 2;
725 : : }
726 : :
727 [ - + ]: 7 : if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
728 : 0 : pa_xfree(p);
729 : 0 : return NULL;
730 : : }
731 : :
732 [ + + ]: 7 : pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s, maximum usable slot size is %lu",
733 : : p->memory.shared ? "shared" : "private",
734 : : p->n_blocks,
735 : : pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
736 : : pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)),
737 : : (unsigned long) pa_mempool_block_size_max(p));
738 : :
739 : 7 : memset(&p->stat, 0, sizeof(p->stat));
740 : 7 : pa_atomic_store(&p->n_init, 0);
741 : :
742 : 7 : PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
743 : 7 : PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
744 : :
745 : 7 : p->mutex = pa_mutex_new(TRUE, TRUE);
746 : 7 : p->semaphore = pa_semaphore_new(0);
747 : :
748 : 7 : p->free_slots = pa_flist_new(p->n_blocks);
749 : :
750 : 7 : return p;
751 : : }
752 : :
753 : 6 : void pa_mempool_free(pa_mempool *p) {
754 [ - + ]: 6 : pa_assert(p);
755 : :
756 : 6 : pa_mutex_lock(p->mutex);
757 : :
758 [ - + ]: 6 : while (p->imports)
759 : 0 : pa_memimport_free(p->imports);
760 : :
761 [ - + ]: 6 : while (p->exports)
762 : 0 : pa_memexport_free(p->exports);
763 : :
764 : 6 : pa_mutex_unlock(p->mutex);
765 : :
766 : 6 : pa_flist_free(p->free_slots, NULL);
767 : :
768 [ - + ]: 6 : if (pa_atomic_load(&p->stat.n_allocated) > 0) {
769 : :
770 : : /* Ouch, somebody is retaining a memory block reference! */
771 : :
772 : : #ifdef DEBUG_REF
773 : : unsigned i;
774 : : pa_flist *list;
775 : :
776 : : /* Let's try to find at least one of those leaked memory blocks */
777 : :
778 : : list = pa_flist_new(p->n_blocks);
779 : :
780 : : for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) {
781 : : struct mempool_slot *slot;
782 : : pa_memblock *b, *k;
783 : :
784 : : slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i));
785 : : b = mempool_slot_data(slot);
786 : :
787 : : while ((k = pa_flist_pop(p->free_slots))) {
788 : : while (pa_flist_push(list, k) < 0)
789 : : ;
790 : :
791 : : if (b == k)
792 : : break;
793 : : }
794 : :
795 : : if (!k)
796 : : pa_log("REF: Leaked memory block %p", b);
797 : :
798 : : while ((k = pa_flist_pop(list)))
799 : : while (pa_flist_push(p->free_slots, k) < 0)
800 : : ;
801 : : }
802 : :
803 : : pa_flist_free(list, NULL);
804 : :
805 : : #endif
806 : :
807 : 0 : pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
808 : :
809 : : /* PA_DEBUG_TRAP; */
810 : : }
811 : :
812 : 6 : pa_shm_free(&p->memory);
813 : :
814 : 6 : pa_mutex_free(p->mutex);
815 : 6 : pa_semaphore_free(p->semaphore);
816 : :
817 : 6 : pa_xfree(p);
818 : 6 : }
819 : :
820 : : /* No lock necessary */
821 : 12 : const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
822 [ - + ]: 12 : pa_assert(p);
823 : :
824 : 12 : return &p->stat;
825 : : }
826 : :
827 : : /* No lock necessary */
828 : 7 : size_t pa_mempool_block_size_max(pa_mempool *p) {
829 [ - + ]: 7 : pa_assert(p);
830 : :
831 : 7 : return p->block_size - PA_ALIGN(sizeof(pa_memblock));
832 : : }
833 : :
834 : : /* No lock necessary */
835 : 3 : void pa_mempool_vacuum(pa_mempool *p) {
836 : : struct mempool_slot *slot;
837 : : pa_flist *list;
838 : :
839 [ - + ]: 3 : pa_assert(p);
840 : :
841 : 3 : list = pa_flist_new(p->n_blocks);
842 : :
843 [ + + ]: 6 : while ((slot = pa_flist_pop(p->free_slots)))
844 [ - + ]: 3 : while (pa_flist_push(list, slot) < 0)
845 : : ;
846 : :
847 [ + + ]: 6 : while ((slot = pa_flist_pop(list))) {
848 : 3 : pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
849 : :
850 [ - + ]: 6 : while (pa_flist_push(p->free_slots, slot))
851 : : ;
852 : : }
853 : :
854 : 3 : pa_flist_free(list, NULL);
855 : 3 : }
856 : :
857 : : /* No lock necessary */
858 : 3 : int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
859 [ - + ]: 3 : pa_assert(p);
860 : :
861 [ + - ]: 3 : if (!p->memory.shared)
862 : : return -1;
863 : :
864 : 3 : *id = p->memory.id;
865 : :
866 : 3 : return 0;
867 : : }
868 : :
869 : : /* No lock necessary */
870 : 0 : pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
871 [ # # ]: 0 : pa_assert(p);
872 : :
873 : 0 : return !!p->memory.shared;
874 : : }
875 : :
876 : : /* For receiving blocks from other nodes */
877 : 8 : pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
878 : : pa_memimport *i;
879 : :
880 [ - + ]: 8 : pa_assert(p);
881 [ - + ]: 8 : pa_assert(cb);
882 : :
883 : 8 : i = pa_xnew(pa_memimport, 1);
884 : 8 : i->mutex = pa_mutex_new(TRUE, TRUE);
885 : 8 : i->pool = p;
886 : 8 : i->segments = pa_hashmap_new(NULL, NULL);
887 : 8 : i->blocks = pa_hashmap_new(NULL, NULL);
888 : 8 : i->release_cb = cb;
889 : 8 : i->userdata = userdata;
890 : :
891 : 8 : pa_mutex_lock(p->mutex);
892 [ - + ][ - + ]: 8 : PA_LLIST_PREPEND(pa_memimport, p->imports, i);
893 : 8 : pa_mutex_unlock(p->mutex);
894 : :
895 : 8 : return i;
896 : : }
897 : :
898 : : static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
899 : :
900 : : /* Should be called locked */
901 : : static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
902 : : pa_memimport_segment* seg;
903 : :
904 [ + - ]: 8 : if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
905 : : return NULL;
906 : :
907 : 8 : seg = pa_xnew0(pa_memimport_segment, 1);
908 : :
909 [ - + ]: 8 : if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
910 : 0 : pa_xfree(seg);
911 : : return NULL;
912 : : }
913 : :
914 : 8 : seg->import = i;
915 : 8 : seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size);
916 : :
917 : 8 : pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg);
918 : : return seg;
919 : : }
920 : :
921 : : /* Should be called locked */
922 : 8 : static void segment_detach(pa_memimport_segment *seg) {
923 [ - + ]: 8 : pa_assert(seg);
924 : :
925 : 8 : pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
926 : 8 : pa_shm_free(&seg->memory);
927 : :
928 [ + - ]: 8 : if (seg->trap)
929 : 8 : pa_memtrap_remove(seg->trap);
930 : :
931 : 8 : pa_xfree(seg);
932 : 8 : }
933 : :
934 : : /* Self-locked. Not multiple-caller safe */
935 : 8 : void pa_memimport_free(pa_memimport *i) {
936 : : pa_memexport *e;
937 : : pa_memblock *b;
938 : :
939 [ - + ]: 8 : pa_assert(i);
940 : :
941 : 8 : pa_mutex_lock(i->mutex);
942 : :
943 [ - + ]: 8 : while ((b = pa_hashmap_first(i->blocks)))
944 : 0 : memblock_replace_import(b);
945 : :
946 [ - + ]: 8 : pa_assert(pa_hashmap_size(i->segments) == 0);
947 : :
948 : 8 : pa_mutex_unlock(i->mutex);
949 : :
950 : 8 : pa_mutex_lock(i->pool->mutex);
951 : :
952 : : /* If we've exported this block further we need to revoke that export */
953 [ - + ]: 8 : for (e = i->pool->exports; e; e = e->next)
954 : 0 : memexport_revoke_blocks(e, i);
955 : :
956 [ - + ][ - + ]: 8 : PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
[ - + ][ - + ]
957 : :
958 : 8 : pa_mutex_unlock(i->pool->mutex);
959 : :
960 : 8 : pa_hashmap_free(i->blocks, NULL, NULL);
961 : 8 : pa_hashmap_free(i->segments, NULL, NULL);
962 : :
963 : 8 : pa_mutex_free(i->mutex);
964 : :
965 : 8 : pa_xfree(i);
966 : 8 : }
967 : :
968 : : /* Self-locked */
969 : 8 : pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
970 : 8 : pa_memblock *b = NULL;
971 : : pa_memimport_segment *seg;
972 : :
973 [ - + ]: 8 : pa_assert(i);
974 : :
975 : 8 : pa_mutex_lock(i->mutex);
976 : :
977 [ - + ]: 8 : if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) {
978 : 0 : pa_memblock_ref(b);
979 : 0 : goto finish;
980 : : }
981 : :
982 [ + - ]: 8 : if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
983 : : goto finish;
984 : :
985 [ + - ]: 8 : if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
986 [ + - ]: 8 : if (!(seg = segment_attach(i, shm_id)))
987 : : goto finish;
988 : :
989 [ + - ]: 8 : if (offset+size > seg->memory.size)
990 : : goto finish;
991 : :
992 [ + + ]: 8 : if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
993 : 2 : b = pa_xnew(pa_memblock, 1);
994 : :
995 : 8 : PA_REFCNT_INIT(b);
996 : 8 : b->pool = i->pool;
997 : 8 : b->type = PA_MEMBLOCK_IMPORTED;
998 : 8 : b->read_only = TRUE;
999 : 8 : b->is_silence = FALSE;
1000 : 8 : pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
1001 : 8 : b->length = size;
1002 : 8 : pa_atomic_store(&b->n_acquired, 0);
1003 : 8 : pa_atomic_store(&b->please_signal, 0);
1004 : 8 : b->per_type.imported.id = block_id;
1005 : 8 : b->per_type.imported.segment = seg;
1006 : :
1007 : 8 : pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
1008 : :
1009 : 8 : seg->n_blocks++;
1010 : :
1011 : 8 : stat_add(b);
1012 : :
1013 : : finish:
1014 : 8 : pa_mutex_unlock(i->mutex);
1015 : :
1016 : 8 : return b;
1017 : : }
1018 : :
1019 : 0 : int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
1020 : : pa_memblock *b;
1021 : 0 : int ret = 0;
1022 [ # # ]: 0 : pa_assert(i);
1023 : :
1024 : 0 : pa_mutex_lock(i->mutex);
1025 : :
1026 [ # # ]: 0 : if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
1027 : : ret = -1;
1028 : : goto finish;
1029 : : }
1030 : :
1031 : 0 : memblock_replace_import(b);
1032 : :
1033 : : finish:
1034 : 0 : pa_mutex_unlock(i->mutex);
1035 : :
1036 : 0 : return ret;
1037 : : }
1038 : :
1039 : : /* For sending blocks to other nodes */
1040 : 8 : pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
1041 : : pa_memexport *e;
1042 : :
1043 [ - + ]: 8 : pa_assert(p);
1044 [ - + ]: 8 : pa_assert(cb);
1045 : :
1046 [ + - ]: 8 : if (!p->memory.shared)
1047 : : return NULL;
1048 : :
1049 : 8 : e = pa_xnew(pa_memexport, 1);
1050 : 8 : e->mutex = pa_mutex_new(TRUE, TRUE);
1051 : 8 : e->pool = p;
1052 : 8 : PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
1053 : 8 : PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
1054 : 8 : e->n_init = 0;
1055 : 8 : e->revoke_cb = cb;
1056 : 8 : e->userdata = userdata;
1057 : :
1058 : 8 : pa_mutex_lock(p->mutex);
1059 [ - + ][ - + ]: 8 : PA_LLIST_PREPEND(pa_memexport, p->exports, e);
1060 : 8 : pa_mutex_unlock(p->mutex);
1061 : 8 : return e;
1062 : : }
1063 : :
1064 : 8 : void pa_memexport_free(pa_memexport *e) {
1065 [ - + ]: 8 : pa_assert(e);
1066 : :
1067 : 8 : pa_mutex_lock(e->mutex);
1068 [ + + ]: 16 : while (e->used_slots)
1069 : 8 : pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots));
1070 : 8 : pa_mutex_unlock(e->mutex);
1071 : :
1072 : 8 : pa_mutex_lock(e->pool->mutex);
1073 [ - + ][ - + ]: 8 : PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
[ - + ][ - + ]
1074 : 8 : pa_mutex_unlock(e->pool->mutex);
1075 : :
1076 : 8 : pa_mutex_free(e->mutex);
1077 : 8 : pa_xfree(e);
1078 : 8 : }
1079 : :
1080 : : /* Self-locked */
1081 : 8 : int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
1082 : : pa_memblock *b;
1083 : :
1084 [ - + ]: 8 : pa_assert(e);
1085 : :
1086 : 8 : pa_mutex_lock(e->mutex);
1087 : :
1088 [ + - ]: 8 : if (id >= e->n_init)
1089 : : goto fail;
1090 : :
1091 [ + - ]: 8 : if (!e->slots[id].block)
1092 : : goto fail;
1093 : :
1094 : 8 : b = e->slots[id].block;
1095 : 8 : e->slots[id].block = NULL;
1096 : :
1097 [ - + ][ - + ]: 8 : PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
[ - + ][ - + ]
1098 [ - + ][ - + ]: 8 : PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
1099 : :
1100 : 8 : pa_mutex_unlock(e->mutex);
1101 : :
1102 : : /* pa_log("Processing release for %u", id); */
1103 : :
1104 [ - + ]: 8 : pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
1105 [ - + ]: 8 : pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
1106 : :
1107 : 8 : pa_atomic_dec(&e->pool->stat.n_exported);
1108 : 8 : pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
1109 : :
1110 : 8 : pa_memblock_unref(b);
1111 : :
1112 : 8 : return 0;
1113 : :
1114 : : fail:
1115 : 0 : pa_mutex_unlock(e->mutex);
1116 : :
1117 : 8 : return -1;
1118 : : }
1119 : :
1120 : : /* Self-locked */
1121 : 0 : static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
1122 : : struct memexport_slot *slot, *next;
1123 [ # # ]: 0 : pa_assert(e);
1124 [ # # ]: 0 : pa_assert(i);
1125 : :
1126 : 0 : pa_mutex_lock(e->mutex);
1127 : :
1128 [ # # ]: 0 : for (slot = e->used_slots; slot; slot = next) {
1129 : : uint32_t idx;
1130 : 0 : next = slot->next;
1131 : :
1132 [ # # ][ # # ]: 0 : if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
1133 : 0 : slot->block->per_type.imported.segment->import != i)
1134 : 0 : continue;
1135 : :
1136 : 0 : idx = (uint32_t) (slot - e->slots);
1137 : 0 : e->revoke_cb(e, idx, e->userdata);
1138 : 0 : pa_memexport_process_release(e, idx);
1139 : : }
1140 : :
1141 : 0 : pa_mutex_unlock(e->mutex);
1142 : 0 : }
1143 : :
1144 : : /* No lock necessary */
1145 : 8 : static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
1146 : : pa_memblock *n;
1147 : :
1148 [ - + ]: 8 : pa_assert(p);
1149 [ - + ]: 8 : pa_assert(b);
1150 : :
1151 [ + + ]: 8 : if (b->type == PA_MEMBLOCK_IMPORTED ||
1152 [ - + ]: 2 : b->type == PA_MEMBLOCK_POOL ||
1153 : 2 : b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
1154 [ - + ]: 6 : pa_assert(b->pool == p);
1155 : 6 : return pa_memblock_ref(b);
1156 : : }
1157 : :
1158 [ + - ]: 2 : if (!(n = pa_memblock_new_pool(p, b->length)))
1159 : : return NULL;
1160 : :
1161 : 4 : memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
1162 : 8 : return n;
1163 : : }
1164 : :
1165 : : /* Self-locked */
1166 : 8 : int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {
1167 : : pa_shm *memory;
1168 : : struct memexport_slot *slot;
1169 : : void *data;
1170 : :
1171 [ - + ]: 8 : pa_assert(e);
1172 [ - + ]: 8 : pa_assert(b);
1173 [ - + ]: 8 : pa_assert(block_id);
1174 [ - + ]: 8 : pa_assert(shm_id);
1175 [ - + ]: 8 : pa_assert(offset);
1176 [ - + ]: 8 : pa_assert(size);
1177 [ - + ]: 8 : pa_assert(b->pool == e->pool);
1178 : :
1179 [ + - ]: 8 : if (!(b = memblock_shared_copy(e->pool, b)))
1180 : : return -1;
1181 : :
1182 : 8 : pa_mutex_lock(e->mutex);
1183 : :
1184 [ - + ]: 8 : if (e->free_slots) {
1185 : 0 : slot = e->free_slots;
1186 [ # # ][ # # ]: 0 : PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
[ # # ][ # # ]
1187 [ + - ]: 8 : } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
1188 : 8 : slot = &e->slots[e->n_init++];
1189 : : else {
1190 : 0 : pa_mutex_unlock(e->mutex);
1191 : 0 : pa_memblock_unref(b);
1192 : 0 : return -1;
1193 : : }
1194 : :
1195 [ - + ][ - + ]: 8 : PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
1196 : 8 : slot->block = b;
1197 : 8 : *block_id = (uint32_t) (slot - e->slots);
1198 : :
1199 : 8 : pa_mutex_unlock(e->mutex);
1200 : : /* pa_log("Got block id %u", *block_id); */
1201 : :
1202 : 8 : data = pa_memblock_acquire(b);
1203 : :
1204 [ + + ]: 8 : if (b->type == PA_MEMBLOCK_IMPORTED) {
1205 [ - + ]: 4 : pa_assert(b->per_type.imported.segment);
1206 : 4 : memory = &b->per_type.imported.segment->memory;
1207 : : } else {
1208 [ - + ]: 4 : pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
1209 [ - + ]: 4 : pa_assert(b->pool);
1210 : 4 : memory = &b->pool->memory;
1211 : : }
1212 : :
1213 [ - + ]: 8 : pa_assert(data >= memory->ptr);
1214 [ - + ]: 8 : pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
1215 : :
1216 : 8 : *shm_id = memory->id;
1217 : 8 : *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
1218 : 8 : *size = b->length;
1219 : :
1220 : 8 : pa_memblock_release(b);
1221 : :
1222 : 8 : pa_atomic_inc(&e->pool->stat.n_exported);
1223 : 8 : pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
1224 : :
1225 : 8 : return 0;
1226 : : }
|