4 This file is part of PulseAudio.
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 #include <pulse/xmalloc.h>
34 #include <pulsecore/shm.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/hashmap.h>
40 #define PA_MEMPOOL_SLOTS_MAX 128
41 #define PA_MEMPOOL_SLOT_SIZE (16*1024)
43 #define PA_MEMEXPORT_SLOTS_MAX 128
45 #define PA_MEMIMPORT_SLOTS_MAX 128
46 #define PA_MEMIMPORT_SEGMENTS_MAX 16
48 struct pa_memimport_segment
{
59 /* Called whenever an imported memory block is no longer
61 pa_memimport_release_cb_t release_cb
;
64 PA_LLIST_FIELDS(pa_memimport
);
67 struct memexport_slot
{
68 PA_LLIST_FIELDS(struct memexport_slot
);
75 struct memexport_slot slots
[PA_MEMEXPORT_SLOTS_MAX
];
76 PA_LLIST_HEAD(struct memexport_slot
, free_slots
);
77 PA_LLIST_HEAD(struct memexport_slot
, used_slots
);
80 /* Called whenever a client from which we imported a memory block
81 which we in turn exported to another client dies and we need to
82 revoke the memory block accordingly */
83 pa_memexport_revoke_cb_t revoke_cb
;
86 PA_LLIST_FIELDS(pa_memexport
);
90 PA_LLIST_FIELDS(struct mempool_slot
);
91 /* the actual data follows immediately hereafter */
97 unsigned n_blocks
, n_init
;
99 PA_LLIST_HEAD(pa_memimport
, imports
);
100 PA_LLIST_HEAD(pa_memexport
, exports
);
102 /* A list of free slots that may be reused */
103 PA_LLIST_HEAD(struct mempool_slot
, free_slots
);
104 PA_LLIST_HEAD(struct mempool_slot
, used_slots
);
106 pa_mempool_stat stat
;
109 static void segment_detach(pa_memimport_segment
*seg
);
111 static void stat_add(pa_memblock
*b
) {
115 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_allocated
);
116 AO_fetch_and_add_release_write(&b
->pool
->stat
.allocated_size
, (AO_t
) b
->length
);
118 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_accumulated
);
119 AO_fetch_and_add_release_write(&b
->pool
->stat
.accumulated_size
, (AO_t
) b
->length
);
121 if (b
->type
== PA_MEMBLOCK_IMPORTED
) {
122 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_imported
);
123 AO_fetch_and_add_release_write(&b
->pool
->stat
.imported_size
, (AO_t
) b
->length
);
126 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_allocated_by_type
[b
->type
]);
127 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_accumulated_by_type
[b
->type
]);
130 static void stat_remove(pa_memblock
*b
) {
134 assert(AO_load_acquire_read(&b
->pool
->stat
.n_allocated
) > 0);
135 assert(AO_load_acquire_read(&b
->pool
->stat
.allocated_size
) >= (AO_t
) b
->length
);
137 AO_fetch_and_sub1_release_write(&b
->pool
->stat
.n_allocated
);
138 AO_fetch_and_add_release_write(&b
->pool
->stat
.allocated_size
, (AO_t
) (-b
->length
));
140 if (b
->type
== PA_MEMBLOCK_IMPORTED
) {
141 assert(AO_load_acquire_read(&b
->pool
->stat
.n_imported
) > 0);
142 assert(AO_load_acquire_read(&b
->pool
->stat
.imported_size
) >= (AO_t
) b
->length
);
144 AO_fetch_and_sub1_release_write(&b
->pool
->stat
.n_imported
);
145 AO_fetch_and_add_release_write(&b
->pool
->stat
.imported_size
, (AO_t
) (-b
->length
));
148 AO_fetch_and_sub1_release_write(&b
->pool
->stat
.n_allocated_by_type
[b
->type
]);
151 static pa_memblock
*memblock_new_appended(pa_mempool
*p
, size_t length
);
153 pa_memblock
*pa_memblock_new(pa_mempool
*p
, size_t length
) {
159 if (!(b
= pa_memblock_new_pool(p
, length
)))
160 b
= memblock_new_appended(p
, length
);
165 static pa_memblock
*memblock_new_appended(pa_mempool
*p
, size_t length
) {
171 b
= pa_xmalloc(sizeof(pa_memblock
) + length
);
172 b
->type
= PA_MEMBLOCK_APPENDED
;
176 b
->data
= (uint8_t*) b
+ sizeof(pa_memblock
);
183 static struct mempool_slot
* mempool_allocate_slot(pa_mempool
*p
) {
184 struct mempool_slot
*slot
;
188 slot
= p
->free_slots
;
189 PA_LLIST_REMOVE(struct mempool_slot
, p
->free_slots
, slot
);
190 } else if (p
->n_init
< p
->n_blocks
)
191 slot
= (struct mempool_slot
*) ((uint8_t*) p
->memory
.ptr
+ (p
->block_size
* p
->n_init
++));
193 pa_log_debug("Pool full");
194 AO_fetch_and_add1_release_write(&p
->stat
.n_pool_full
);
198 PA_LLIST_PREPEND(struct mempool_slot
, p
->used_slots
, slot
);
202 static void* mempool_slot_data(struct mempool_slot
*slot
) {
205 return (uint8_t*) slot
+ sizeof(struct mempool_slot
);
208 static unsigned mempool_slot_idx(pa_mempool
*p
, void *ptr
) {
210 assert((uint8_t*) ptr
>= (uint8_t*) p
->memory
.ptr
);
211 assert((uint8_t*) ptr
< (uint8_t*) p
->memory
.ptr
+ p
->memory
.size
);
213 return ((uint8_t*) ptr
- (uint8_t*) p
->memory
.ptr
) / p
->block_size
;
216 static struct mempool_slot
* mempool_slot_by_ptr(pa_mempool
*p
, void *ptr
) {
219 if ((idx
= mempool_slot_idx(p
, ptr
)) == (unsigned) -1)
222 return (struct mempool_slot
*) ((uint8_t*) p
->memory
.ptr
+ (idx
* p
->block_size
));
225 pa_memblock
*pa_memblock_new_pool(pa_mempool
*p
, size_t length
) {
226 pa_memblock
*b
= NULL
;
227 struct mempool_slot
*slot
;
232 if (p
->block_size
- sizeof(struct mempool_slot
) >= sizeof(pa_memblock
) + length
) {
234 if (!(slot
= mempool_allocate_slot(p
)))
237 b
= mempool_slot_data(slot
);
238 b
->type
= PA_MEMBLOCK_POOL
;
239 b
->data
= (uint8_t*) b
+ sizeof(pa_memblock
);
241 } else if (p
->block_size
- sizeof(struct mempool_slot
) >= length
) {
243 if (!(slot
= mempool_allocate_slot(p
)))
246 b
= pa_xnew(pa_memblock
, 1);
247 b
->type
= PA_MEMBLOCK_POOL_EXTERNAL
;
248 b
->data
= mempool_slot_data(slot
);
250 pa_log_debug("Memory block too large for pool: %u > %u", length
, p
->block_size
- sizeof(struct mempool_slot
));
251 AO_fetch_and_add1_release_write(&p
->stat
.n_too_large_for_pool
);
264 pa_memblock
*pa_memblock_new_fixed(pa_mempool
*p
, void *d
, size_t length
, int read_only
) {
271 b
= pa_xnew(pa_memblock
, 1);
272 b
->type
= PA_MEMBLOCK_FIXED
;
273 b
->read_only
= read_only
;
283 pa_memblock
*pa_memblock_new_user(pa_mempool
*p
, void *d
, size_t length
, void (*free_cb
)(void *p
), int read_only
) {
291 b
= pa_xnew(pa_memblock
, 1);
292 b
->type
= PA_MEMBLOCK_USER
;
293 b
->read_only
= read_only
;
297 b
->per_type
.user
.free_cb
= free_cb
;
304 pa_memblock
* pa_memblock_ref(pa_memblock
*b
) {
306 assert(PA_REFCNT_VALUE(b
) > 0);
312 void pa_memblock_unref(pa_memblock
*b
) {
314 assert(PA_REFCNT_VALUE(b
) > 0);
316 if (PA_REFCNT_DEC(b
) > 0)
322 case PA_MEMBLOCK_USER
:
323 assert(b
->per_type
.user
.free_cb
);
324 b
->per_type
.user
.free_cb(b
->data
);
328 case PA_MEMBLOCK_FIXED
:
329 case PA_MEMBLOCK_APPENDED
:
333 case PA_MEMBLOCK_IMPORTED
: {
334 pa_memimport_segment
*segment
;
336 segment
= b
->per_type
.imported
.segment
;
338 assert(segment
->import
);
340 pa_hashmap_remove(segment
->import
->blocks
, PA_UINT32_TO_PTR(b
->per_type
.imported
.id
));
341 segment
->import
->release_cb(segment
->import
, b
->per_type
.imported
.id
, segment
->import
->userdata
);
343 if (-- segment
->n_blocks
<= 0)
344 segment_detach(segment
);
350 case PA_MEMBLOCK_POOL_EXTERNAL
:
351 case PA_MEMBLOCK_POOL
: {
352 struct mempool_slot
*slot
;
354 slot
= mempool_slot_by_ptr(b
->pool
, b
->data
);
357 PA_LLIST_REMOVE(struct mempool_slot
, b
->pool
->used_slots
, slot
);
358 PA_LLIST_PREPEND(struct mempool_slot
, b
->pool
->free_slots
, slot
);
360 if (b
->type
== PA_MEMBLOCK_POOL_EXTERNAL
)
366 case PA_MEMBLOCK_TYPE_MAX
:
372 static void memblock_make_local(pa_memblock
*b
) {
375 AO_fetch_and_sub1_release_write(&b
->pool
->stat
.n_allocated_by_type
[b
->type
]);
377 if (b
->length
<= b
->pool
->block_size
- sizeof(struct mempool_slot
)) {
378 struct mempool_slot
*slot
;
380 if ((slot
= mempool_allocate_slot(b
->pool
))) {
382 /* We can move it into a local pool, perfect! */
384 b
->type
= PA_MEMBLOCK_POOL_EXTERNAL
;
387 new_data
= mempool_slot_data(slot
);
388 memcpy(new_data
, b
->data
, b
->length
);
394 /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
395 b
->type
= PA_MEMBLOCK_USER
;
396 b
->per_type
.user
.free_cb
= pa_xfree
;
398 b
->data
= pa_xmemdup(b
->data
, b
->length
);
401 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_allocated_by_type
[b
->type
]);
402 AO_fetch_and_add1_release_write(&b
->pool
->stat
.n_accumulated_by_type
[b
->type
]);
405 void pa_memblock_unref_fixed(pa_memblock
*b
) {
407 assert(PA_REFCNT_VALUE(b
) > 0);
408 assert(b
->type
== PA_MEMBLOCK_FIXED
);
410 if (PA_REFCNT_VALUE(b
) > 1)
411 memblock_make_local(b
);
413 pa_memblock_unref(b
);
416 static void memblock_replace_import(pa_memblock
*b
) {
417 pa_memimport_segment
*seg
;
420 assert(b
->type
== PA_MEMBLOCK_IMPORTED
);
422 assert(AO_load_acquire_read(&b
->pool
->stat
.n_imported
) > 0);
423 assert(AO_load_acquire_read(&b
->pool
->stat
.imported_size
) >= (AO_t
) b
->length
);
424 AO_fetch_and_sub1_release_write(&b
->pool
->stat
.n_imported
);
425 AO_fetch_and_add_release_write(&b
->pool
->stat
.imported_size
, (AO_t
) - b
->length
);
427 seg
= b
->per_type
.imported
.segment
;
433 PA_UINT32_TO_PTR(b
->per_type
.imported
.id
));
435 memblock_make_local(b
);
437 if (-- seg
->n_blocks
<= 0)
441 pa_mempool
* pa_mempool_new(int shared
) {
445 p
= pa_xnew(pa_mempool
, 1);
448 ps
= (size_t) sysconf(_SC_PAGESIZE
);
449 #elif defined(PAGE_SIZE)
450 ps
= (size_t) PAGE_SIZE
;
452 ps
= 4096; /* Let's hope it's like x86. */
455 p
->block_size
= (PA_MEMPOOL_SLOT_SIZE
/ps
)*ps
;
457 if (p
->block_size
< ps
)
460 p
->n_blocks
= PA_MEMPOOL_SLOTS_MAX
;
462 assert(p
->block_size
> sizeof(struct mempool_slot
));
464 if (pa_shm_create_rw(&p
->memory
, p
->n_blocks
* p
->block_size
, shared
, 0700) < 0) {
471 PA_LLIST_HEAD_INIT(pa_memimport
, p
->imports
);
472 PA_LLIST_HEAD_INIT(pa_memexport
, p
->exports
);
473 PA_LLIST_HEAD_INIT(struct mempool_slot
, p
->free_slots
);
474 PA_LLIST_HEAD_INIT(struct mempool_slot
, p
->used_slots
);
476 memset(&p
->stat
, 0, sizeof(p
->stat
));
481 void pa_mempool_free(pa_mempool
*p
) {
485 pa_memimport_free(p
->imports
);
488 pa_memexport_free(p
->exports
);
490 if (AO_load_acquire_read(&p
->stat
.n_allocated
) > 0)
491 pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
493 pa_shm_free(&p
->memory
);
497 const pa_mempool_stat
* pa_mempool_get_stat(pa_mempool
*p
) {
503 void pa_mempool_vacuum(pa_mempool
*p
) {
504 struct mempool_slot
*slot
;
508 for (slot
= p
->free_slots
; slot
; slot
= slot
->next
) {
509 pa_shm_punch(&p
->memory
, (uint8_t*) slot
+ sizeof(struct mempool_slot
) - (uint8_t*) p
->memory
.ptr
, p
->block_size
- sizeof(struct mempool_slot
));
513 int pa_mempool_get_shm_id(pa_mempool
*p
, uint32_t *id
) {
516 if (!p
->memory
.shared
)
524 int pa_mempool_is_shared(pa_mempool
*p
) {
527 return !!p
->memory
.shared
;
530 /* For recieving blocks from other nodes */
531 pa_memimport
* pa_memimport_new(pa_mempool
*p
, pa_memimport_release_cb_t cb
, void *userdata
) {
537 i
= pa_xnew(pa_memimport
, 1);
539 i
->segments
= pa_hashmap_new(NULL
, NULL
);
540 i
->blocks
= pa_hashmap_new(NULL
, NULL
);
542 i
->userdata
= userdata
;
544 PA_LLIST_PREPEND(pa_memimport
, p
->imports
, i
);
548 static void memexport_revoke_blocks(pa_memexport
*e
, pa_memimport
*i
);
550 static pa_memimport_segment
* segment_attach(pa_memimport
*i
, uint32_t shm_id
) {
551 pa_memimport_segment
* seg
;
553 if (pa_hashmap_size(i
->segments
) >= PA_MEMIMPORT_SEGMENTS_MAX
)
556 seg
= pa_xnew(pa_memimport_segment
, 1);
558 if (pa_shm_attach_ro(&seg
->memory
, shm_id
) < 0) {
566 pa_hashmap_put(i
->segments
, PA_UINT32_TO_PTR(shm_id
), seg
);
570 static void segment_detach(pa_memimport_segment
*seg
) {
573 pa_hashmap_remove(seg
->import
->segments
, PA_UINT32_TO_PTR(seg
->memory
.id
));
574 pa_shm_free(&seg
->memory
);
578 void pa_memimport_free(pa_memimport
*i
) {
584 /* If we've exported this block further we need to revoke that export */
585 for (e
= i
->pool
->exports
; e
; e
= e
->next
)
586 memexport_revoke_blocks(e
, i
);
588 while ((b
= pa_hashmap_get_first(i
->blocks
)))
589 memblock_replace_import(b
);
591 assert(pa_hashmap_size(i
->segments
) == 0);
593 pa_hashmap_free(i
->blocks
, NULL
, NULL
);
594 pa_hashmap_free(i
->segments
, NULL
, NULL
);
596 PA_LLIST_REMOVE(pa_memimport
, i
->pool
->imports
, i
);
600 pa_memblock
* pa_memimport_get(pa_memimport
*i
, uint32_t block_id
, uint32_t shm_id
, size_t offset
, size_t size
) {
602 pa_memimport_segment
*seg
;
606 if (pa_hashmap_size(i
->blocks
) >= PA_MEMIMPORT_SLOTS_MAX
)
609 if (!(seg
= pa_hashmap_get(i
->segments
, PA_UINT32_TO_PTR(shm_id
))))
610 if (!(seg
= segment_attach(i
, shm_id
)))
613 if (offset
+size
> seg
->memory
.size
)
616 b
= pa_xnew(pa_memblock
, 1);
617 b
->type
= PA_MEMBLOCK_IMPORTED
;
621 b
->data
= (uint8_t*) seg
->memory
.ptr
+ offset
;
623 b
->per_type
.imported
.id
= block_id
;
624 b
->per_type
.imported
.segment
= seg
;
626 pa_hashmap_put(i
->blocks
, PA_UINT32_TO_PTR(block_id
), b
);
635 int pa_memimport_process_revoke(pa_memimport
*i
, uint32_t id
) {
639 if (!(b
= pa_hashmap_get(i
->blocks
, PA_UINT32_TO_PTR(id
))))
642 memblock_replace_import(b
);
646 /* For sending blocks to other nodes */
647 pa_memexport
* pa_memexport_new(pa_mempool
*p
, pa_memexport_revoke_cb_t cb
, void *userdata
) {
653 if (!p
->memory
.shared
)
656 e
= pa_xnew(pa_memexport
, 1);
658 PA_LLIST_HEAD_INIT(struct memexport_slot
, e
->free_slots
);
659 PA_LLIST_HEAD_INIT(struct memexport_slot
, e
->used_slots
);
662 e
->userdata
= userdata
;
664 PA_LLIST_PREPEND(pa_memexport
, p
->exports
, e
);
668 void pa_memexport_free(pa_memexport
*e
) {
671 while (e
->used_slots
)
672 pa_memexport_process_release(e
, e
->used_slots
- e
->slots
);
674 PA_LLIST_REMOVE(pa_memexport
, e
->pool
->exports
, e
);
678 int pa_memexport_process_release(pa_memexport
*e
, uint32_t id
) {
684 if (!e
->slots
[id
].block
)
687 /* pa_log("Processing release for %u", id); */
689 assert(AO_load_acquire_read(&e
->pool
->stat
.n_exported
) > 0);
690 assert(AO_load_acquire_read(&e
->pool
->stat
.exported_size
) >= (AO_t
) e
->slots
[id
].block
->length
);
692 AO_fetch_and_sub1_release_write(&e
->pool
->stat
.n_exported
);
693 AO_fetch_and_add_release_write(&e
->pool
->stat
.exported_size
, (AO_t
) -e
->slots
[id
].block
->length
);
695 pa_memblock_unref(e
->slots
[id
].block
);
696 e
->slots
[id
].block
= NULL
;
698 PA_LLIST_REMOVE(struct memexport_slot
, e
->used_slots
, &e
->slots
[id
]);
699 PA_LLIST_PREPEND(struct memexport_slot
, e
->free_slots
, &e
->slots
[id
]);
704 static void memexport_revoke_blocks(pa_memexport
*e
, pa_memimport
*i
) {
705 struct memexport_slot
*slot
, *next
;
709 for (slot
= e
->used_slots
; slot
; slot
= next
) {
713 if (slot
->block
->type
!= PA_MEMBLOCK_IMPORTED
||
714 slot
->block
->per_type
.imported
.segment
->import
!= i
)
717 idx
= slot
- e
->slots
;
718 e
->revoke_cb(e
, idx
, e
->userdata
);
719 pa_memexport_process_release(e
, idx
);
723 static pa_memblock
*memblock_shared_copy(pa_mempool
*p
, pa_memblock
*b
) {
729 if (b
->type
== PA_MEMBLOCK_IMPORTED
||
730 b
->type
== PA_MEMBLOCK_POOL
||
731 b
->type
== PA_MEMBLOCK_POOL_EXTERNAL
) {
732 assert(b
->pool
== p
);
733 return pa_memblock_ref(b
);
736 if (!(n
= pa_memblock_new_pool(p
, b
->length
)))
739 memcpy(n
->data
, b
->data
, b
->length
);
743 int pa_memexport_put(pa_memexport
*e
, pa_memblock
*b
, uint32_t *block_id
, uint32_t *shm_id
, size_t *offset
, size_t * size
) {
745 struct memexport_slot
*slot
;
753 assert(b
->pool
== e
->pool
);
755 if (!(b
= memblock_shared_copy(e
->pool
, b
)))
759 slot
= e
->free_slots
;
760 PA_LLIST_REMOVE(struct memexport_slot
, e
->free_slots
, slot
);
761 } else if (e
->n_init
< PA_MEMEXPORT_SLOTS_MAX
) {
762 slot
= &e
->slots
[e
->n_init
++];
764 pa_memblock_unref(b
);
768 PA_LLIST_PREPEND(struct memexport_slot
, e
->used_slots
, slot
);
770 *block_id
= slot
- e
->slots
;
772 /* pa_log("Got block id %u", *block_id); */
774 if (b
->type
== PA_MEMBLOCK_IMPORTED
) {
775 assert(b
->per_type
.imported
.segment
);
776 memory
= &b
->per_type
.imported
.segment
->memory
;
778 assert(b
->type
== PA_MEMBLOCK_POOL
|| b
->type
== PA_MEMBLOCK_POOL_EXTERNAL
);
780 memory
= &b
->pool
->memory
;
783 assert(b
->data
>= memory
->ptr
);
784 assert((uint8_t*) b
->data
+ b
->length
<= (uint8_t*) memory
->ptr
+ memory
->size
);
786 *shm_id
= memory
->id
;
787 *offset
= (uint8_t*) b
->data
- (uint8_t*) memory
->ptr
;
790 AO_fetch_and_add1_release_write(&e
->pool
->stat
.n_exported
);
791 AO_fetch_and_add_release_write(&e
->pool
->stat
.exported_size
, (AO_t
) b
->length
);