-/* $Id$ */
-
/***
This file is part of PulseAudio.
#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
+#include <errno.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
#include <pulse/xmalloc.h>
#include <pulse/def.h>
#include <pulsecore/semaphore.h>
#include <pulsecore/macro.h>
#include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
#include "memblock.h"
-#define PA_MEMPOOL_SLOTS_MAX 128
-#define PA_MEMPOOL_SLOT_SIZE (16*1024)
+/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
+ * note that the footprint is usually much smaller, since the data is
+ * stored in SHM and our OS does not commit the memory before we use
+ * it for the first time. */
+#define PA_MEMPOOL_SLOTS_MAX 1024
+#define PA_MEMPOOL_SLOT_SIZE (64*1024)
#define PA_MEMEXPORT_SLOTS_MAX 128
pa_mempool *pool;
pa_memblock_type_t type;
- int read_only; /* boolean */
+
+ pa_bool_t read_only:1;
+ pa_bool_t is_silence:1;
pa_atomic_ptr_t data;
size_t length;
PA_LLIST_FIELDS(pa_memexport);
};
-struct mempool_slot {
- PA_LLIST_FIELDS(struct mempool_slot);
- /* the actual data follows immediately hereafter */
-};
-
struct pa_mempool {
pa_semaphore *semaphore;
pa_mutex *mutex;
pa_assert(b->pool);
pa_atomic_inc(&b->pool->stat.n_allocated);
- pa_atomic_add(&b->pool->stat.allocated_size, b->length);
+ pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
pa_atomic_inc(&b->pool->stat.n_accumulated);
- pa_atomic_add(&b->pool->stat.accumulated_size, b->length);
+ pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
pa_atomic_inc(&b->pool->stat.n_imported);
- pa_atomic_add(&b->pool->stat.imported_size, b->length);
+ pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
}
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_allocated);
- pa_atomic_sub(&b->pool->stat.allocated_size, b->length);
+ pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
if (b->type == PA_MEMBLOCK_IMPORTED) {
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
}
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
if (!(b = pa_memblock_new_pool(p, length)))
b = memblock_new_appended(p, length);
pa_memblock *b;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
+
+ /* If -1 is passed as length we choose the size for the caller. */
+
+ if (length == (size_t) -1)
+ length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
PA_REFCNT_INIT(b);
b->pool = p;
b->type = PA_MEMBLOCK_APPENDED;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
pa_atomic_dec(&p->n_init);
else
- slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
+ slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
if (!slot) {
- pa_log_debug("Pool full");
+ pa_log_info("Pool full");
pa_atomic_inc(&p->stat.n_pool_full);
return NULL;
}
}
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0);
+#endif
+
return slot;
}
-/* No lock necessary */
-static void* mempool_slot_data(struct mempool_slot *slot) {
- pa_assert(slot);
-
- return (uint8_t*) slot + sizeof(struct mempool_slot);
+/* No lock necessary, totally redundant anyway */
+static inline void* mempool_slot_data(struct mempool_slot *slot) {
+ return slot;
}
/* No lock necessary */
pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
- return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
+ return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
}
/* No lock necessary */
struct mempool_slot *slot;
pa_assert(p);
- pa_assert(length > 0);
+ pa_assert(length);
+
+ /* If -1 is passed as length we choose the size for the caller: we
+ * take the largest size that fits in one of our slots. */
+
+ if (length == (size_t) -1)
+ length = pa_mempool_block_size_max(p);
- if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) {
+ if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
b = mempool_slot_data(slot);
b->type = PA_MEMBLOCK_POOL;
- pa_atomic_ptr_store(&b->data, (uint8_t*) b + sizeof(pa_memblock));
+ pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
- } else if (p->block_size - sizeof(struct mempool_slot) >= length) {
+ } else if (p->block_size >= length) {
if (!(slot = mempool_allocate_slot(p)))
return NULL;
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
-
+
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
} else {
- pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - sizeof(struct mempool_slot)));
+ pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
pa_atomic_inc(&p->stat.n_too_large_for_pool);
return NULL;
}
PA_REFCNT_INIT(b);
b->pool = p;
- b->read_only = 0;
+ b->read_only = b->is_silence = FALSE;
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
pa_atomic_store(&b->please_signal, 0);
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
+pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
- pa_assert(length > 0);
+ pa_assert(length != (size_t) -1);
+ pa_assert(length);
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
b->pool = p;
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
}
/* No lock necessary */
-pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
+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) {
pa_memblock *b;
pa_assert(p);
pa_assert(d);
- pa_assert(length > 0);
+ pa_assert(length);
+ pa_assert(length != (size_t) -1);
pa_assert(free_cb);
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b->pool = p;
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, d);
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
}
/* No lock necessary */
-int pa_memblock_is_read_only(pa_memblock *b) {
+pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) > 0);
}
/* No lock necessary */
-int pa_memblock_ref_is_one(pa_memblock *b) {
+pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ return b->is_silence;
+}
+
+/* No lock necessary */
+void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ b->is_silence = v;
+}
+
+/* No lock necessary */
+pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
int r;
-
pa_assert(b);
- r = PA_REFCNT_VALUE(b);
- pa_assert(r > 0);
+ pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
return r == 1;
}
case PA_MEMBLOCK_APPENDED :
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
-
+
break;
case PA_MEMBLOCK_IMPORTED : {
case PA_MEMBLOCK_POOL_EXTERNAL:
case PA_MEMBLOCK_POOL: {
struct mempool_slot *slot;
- int call_free;
+ pa_bool_t call_free;
slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
pa_assert(slot);
while (pa_flist_push(b->pool->free_slots, slot) < 0)
;
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size);
+#endif
+
if (call_free)
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
pa_xfree(b);
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
- if (b->length <= b->pool->block_size - sizeof(struct mempool_slot)) {
+ if (b->length <= b->pool->block_size) {
struct mempool_slot *slot;
if ((slot = mempool_allocate_slot(b->pool))) {
pa_atomic_ptr_store(&b->data, new_data);
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
- b->read_only = 0;
+ b->read_only = FALSE;
goto finish;
}
pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
b->type = PA_MEMBLOCK_USER;
- b->read_only = 0;
+ b->read_only = FALSE;
finish:
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
pa_memblock_unref(b);
}
+/* No lock necessary. */
+pa_memblock *pa_memblock_will_need(pa_memblock *b) {
+ void *p;
+
+ pa_assert(b);
+ pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+ p = pa_memblock_acquire(b);
+ pa_will_need(p, b->length);
+ pa_memblock_release(b);
+
+ return b;
+}
+
/* Self-locked. This function is not multiple-caller safe */
static void memblock_replace_import(pa_memblock *b) {
pa_memimport_segment *seg;
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
pa_atomic_dec(&b->pool->stat.n_imported);
- pa_atomic_sub(&b->pool->stat.imported_size, b->length);
+ pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
seg = b->per_type.imported.segment;
pa_assert(seg);
if (-- seg->n_blocks <= 0) {
pa_mutex_unlock(seg->import->mutex);
segment_detach(seg);
- } else
+ } else
pa_mutex_unlock(seg->import->mutex);
}
-pa_mempool* pa_mempool_new(int shared) {
- size_t ps;
+pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
pa_mempool *p;
+ char t1[64], t2[64];
p = pa_xnew(pa_mempool, 1);
- p->mutex = pa_mutex_new(1);
+ p->mutex = pa_mutex_new(TRUE, TRUE);
p->semaphore = pa_semaphore_new(0);
-#ifdef HAVE_SYSCONF
- ps = (size_t) sysconf(_SC_PAGESIZE);
-#elif defined(PAGE_SIZE)
- ps = (size_t) PAGE_SIZE;
-#else
- ps = 4096; /* Let's hope it's like x86. */
-#endif
-
- p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps;
-
- if (p->block_size < ps)
- p->block_size = ps;
+ p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+ if (p->block_size < PA_PAGE_SIZE)
+ p->block_size = PA_PAGE_SIZE;
- p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+ if (size <= 0)
+ p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
+ else {
+ p->n_blocks = (unsigned) (size / p->block_size);
- pa_assert(p->block_size > sizeof(struct mempool_slot));
+ if (p->n_blocks < 2)
+ p->n_blocks = 2;
+ }
if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
pa_xfree(p);
return NULL;
}
+ pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s",
+ p->memory.shared ? "shared" : "private",
+ p->n_blocks,
+ pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
+ pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)));
+
memset(&p->stat, 0, sizeof(p->stat));
pa_atomic_store(&p->n_init, 0);
PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
- p->free_slots = pa_flist_new(p->n_blocks*2);
+ p->free_slots = pa_flist_new(p->n_blocks);
return p;
}
if (pa_atomic_load(&p->stat.n_allocated) > 0) {
/* raise(SIGTRAP); */
- pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+ pa_log_warn("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
}
pa_shm_free(&p->memory);
return &p->stat;
}
+/* No lock necessary */
+size_t pa_mempool_block_size_max(pa_mempool *p) {
+ pa_assert(p);
+
+ return p->block_size - PA_ALIGN(sizeof(pa_memblock));
+}
+
/* No lock necessary */
void pa_mempool_vacuum(pa_mempool *p) {
struct mempool_slot *slot;
pa_assert(p);
- list = pa_flist_new(p->n_blocks*2);
+ list = pa_flist_new(p->n_blocks);
while ((slot = pa_flist_pop(p->free_slots)))
while (pa_flist_push(list, slot) < 0)
;
while ((slot = pa_flist_pop(list))) {
- pa_shm_punch(&p->memory,
- (uint8_t*) slot - (uint8_t*) p->memory.ptr + sizeof(struct mempool_slot),
- p->block_size - sizeof(struct mempool_slot));
+ pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
while (pa_flist_push(p->free_slots, slot))
;
}
/* No lock necessary */
-int pa_mempool_is_shared(pa_mempool *p) {
+pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
pa_assert(p);
return !!p->memory.shared;
pa_assert(cb);
i = pa_xnew(pa_memimport, 1);
- i->mutex = pa_mutex_new(1);
+ i->mutex = pa_mutex_new(TRUE, TRUE);
i->pool = p;
i->segments = pa_hashmap_new(NULL, NULL);
i->blocks = pa_hashmap_new(NULL, NULL);
pa_mutex_lock(i->mutex);
- while ((b = pa_hashmap_get_first(i->blocks)))
+ while ((b = pa_hashmap_first(i->blocks)))
memblock_replace_import(b);
pa_assert(pa_hashmap_size(i->segments) == 0);
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
b = pa_xnew(pa_memblock, 1);
-
+
PA_REFCNT_INIT(b);
b->pool = i->pool;
b->type = PA_MEMBLOCK_IMPORTED;
- b->read_only = 1;
+ b->read_only = TRUE;
+ b->is_silence = FALSE;
pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
b->length = size;
pa_atomic_store(&b->n_acquired, 0);
int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
pa_memblock *b;
+ int ret = 0;
pa_assert(i);
pa_mutex_lock(i->mutex);
- if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id))))
- return -1;
+ if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
+ ret = -1;
+ goto finish;
+ }
memblock_replace_import(b);
+finish:
pa_mutex_unlock(i->mutex);
- return 0;
+ return ret;
}
/* For sending blocks to other nodes */
return NULL;
e = pa_xnew(pa_memexport, 1);
- e->mutex = pa_mutex_new(1);
+ e->mutex = pa_mutex_new(TRUE, TRUE);
e->pool = p;
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
pa_mutex_lock(e->mutex);
while (e->used_slots)
- pa_memexport_process_release(e, e->used_slots - e->slots);
+ pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots));
pa_mutex_unlock(e->mutex);
pa_mutex_lock(e->pool->mutex);
pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
pa_atomic_dec(&e->pool->stat.n_exported);
- pa_atomic_sub(&e->pool->stat.exported_size, b->length);
+ pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
pa_memblock_unref(b);
slot->block->per_type.imported.segment->import != i)
continue;
- idx = slot - e->slots;
+ idx = (uint32_t) (slot - e->slots);
e->revoke_cb(e, idx, e->userdata);
pa_memexport_process_release(e, idx);
}
PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
slot->block = b;
- *block_id = slot - e->slots;
+ *block_id = (uint32_t) (slot - e->slots);
pa_mutex_unlock(e->mutex);
/* pa_log("Got block id %u", *block_id); */
pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
*shm_id = memory->id;
- *offset = (uint8_t*) data - (uint8_t*) memory->ptr;
+ *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
*size = b->length;
pa_memblock_release(b);
pa_atomic_inc(&e->pool->stat.n_exported);
- pa_atomic_add(&e->pool->stat.exported_size, b->length);
+ pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
return 0;
}