]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/memblock.c
remap: Change remapping function argument type from void to int16_t / float as approp...
[pulseaudio] / src / pulsecore / memblock.c
index 71b576ac3ac73d48b0ee453eeb9a644f32cdc40d..9cc02c1a78343c1caf982a800c7855db85274b61 100644 (file)
@@ -1,5 +1,3 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
@@ -14,7 +12,7 @@
   PulseAudio is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
+  Lesser General Public License for more details
 
   You should have received a copy of the GNU Lesser General Public
   License along with PulseAudio; if not, write to the Free Software
 
 #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/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/semaphore.h>
+#include <pulsecore/mutex.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/llist.h>
 #include <pulsecore/flist.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/memtrap.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
 
-#define PA_MEMIMPORT_SLOTS_MAX 128
+#define PA_MEMIMPORT_SLOTS_MAX 160
 #define PA_MEMIMPORT_SEGMENTS_MAX 16
 
 struct pa_memblock {
@@ -57,7 +69,9 @@ struct pa_memblock {
     pa_mempool *pool;
 
     pa_memblock_type_t type;
-    int read_only; /* boolean */
+
+    bool read_only:1;
+    bool is_silence:1;
 
     pa_atomic_ptr_t data;
     size_t length;
@@ -71,7 +85,7 @@ struct pa_memblock {
             pa_free_cb_t free_cb;
         } user;
 
-        struct  {
+        struct {
             uint32_t id;
             pa_memimport_segment *segment;
         } imported;
@@ -81,9 +95,11 @@ struct pa_memblock {
 struct pa_memimport_segment {
     pa_memimport *import;
     pa_shm memory;
+    pa_memtrap *trap;
     unsigned n_blocks;
 };
 
+/* A collection of multiple segments */
 struct pa_memimport {
     pa_mutex *mutex;
 
@@ -123,11 +139,6 @@ struct pa_memexport {
     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;
@@ -149,20 +160,22 @@ struct pa_mempool {
 
 static void segment_detach(pa_memimport_segment *seg);
 
+PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
+
 /* No lock necessary */
 static void stat_add(pa_memblock*b) {
-    assert(b);
-    assert(b->pool);
+    pa_assert(b);
+    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]);
@@ -171,21 +184,21 @@ static void stat_add(pa_memblock*b) {
 
 /* No lock necessary */
 static void stat_remove(pa_memblock *b) {
-    assert(b);
-    assert(b->pool);
+    pa_assert(b);
+    pa_assert(b->pool);
 
-    assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
-    assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
+    pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
+    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) {
-        assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
-        assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+        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]);
@@ -197,8 +210,8 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
 pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
     pa_memblock *b;
 
-    assert(p);
-    assert(length > 0);
+    pa_assert(p);
+    pa_assert(length);
 
     if (!(b = pa_memblock_new_pool(p, length)))
         b = memblock_new_appended(p, length);
@@ -210,14 +223,19 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
 static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
     pa_memblock *b;
 
-    assert(p);
-    assert(length > 0);
+    pa_assert(p);
+    pa_assert(length);
+
+    /* If -1 is passed as length we choose the size for the caller. */
+
+    if (length == (size_t) -1)
+        length = pa_mempool_block_size_max(p);
 
     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);
@@ -230,7 +248,7 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
 /* No lock necessary */
 static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
     struct mempool_slot *slot;
-    assert(p);
+    pa_assert(p);
 
     if (!(slot = pa_flist_pop(p->free_slots))) {
         int idx;
@@ -240,33 +258,38 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
         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");
+            if (pa_log_ratelimit(PA_LOG_DEBUG))
+                pa_log_debug("Pool full");
             pa_atomic_inc(&p->stat.n_pool_full);
             return NULL;
         }
     }
 
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/*     if (PA_UNLIKELY(pa_in_valgrind())) { */
+/*         VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
+/*     } */
+/* #endif */
+
     return slot;
 }
 
-/* No lock necessary */
-static void* mempool_slot_data(struct mempool_slot *slot) {
-    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 */
 static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
-    assert(p);
+    pa_assert(p);
 
-    assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
-    assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
+    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 */
@@ -283,37 +306,52 @@ static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
 pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
     pa_memblock *b = NULL;
     struct mempool_slot *slot;
+    static int mempool_disable = 0;
+
+    pa_assert(p);
+    pa_assert(length);
 
-    assert(p);
-    assert(length > 0);
+    if (mempool_disable == 0)
+        mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1;
 
-    if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) {
+    if (mempool_disable > 0)
+        return NULL;
+
+    /* 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 >= 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;
 
-        b = pa_xnew(pa_memblock, 1);
+        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: %u > %u", length, 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);
@@ -323,18 +361,22 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
 }
 
 /* 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, bool read_only) {
     pa_memblock *b;
 
-    assert(p);
-    assert(d);
-    assert(length > 0);
+    pa_assert(p);
+    pa_assert(d);
+    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 = pa_xnew(pa_memblock, 1);
     PA_REFCNT_INIT(b);
     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);
@@ -345,19 +387,23 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
 }
 
 /* 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, bool read_only) {
     pa_memblock *b;
 
-    assert(p);
-    assert(d);
-    assert(length > 0);
-    assert(free_cb);
+    pa_assert(p);
+    pa_assert(d);
+    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 = pa_xnew(pa_memblock, 1);
 
-    b = pa_xnew(pa_memblock, 1);
     PA_REFCNT_INIT(b);
     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);
@@ -370,31 +416,64 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
 }
 
 /* No lock necessary */
-int pa_memblock_is_read_only(pa_memblock *b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+bool pa_memblock_is_read_only(pa_memblock *b) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     return b->read_only && PA_REFCNT_VALUE(b) == 1;
 }
 
+/* No lock necessary */
+bool 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, bool v) {
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+
+    b->is_silence = v;
+}
+
+/* No lock necessary */
+bool pa_memblock_ref_is_one(pa_memblock *b) {
+    int r;
+    pa_assert(b);
+
+    pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
+
+    return r == 1;
+}
+
 /* No lock necessary */
 void* pa_memblock_acquire(pa_memblock *b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     pa_atomic_inc(&b->n_acquired);
 
     return pa_atomic_ptr_load(&b->data);
 }
 
+/* No lock necessary */
+void *pa_memblock_acquire_chunk(const pa_memchunk *c) {
+    pa_assert(c);
+
+    return (uint8_t *) pa_memblock_acquire(c->memblock) + c->index;
+}
+
 /* No lock necessary, in corner cases locks by its own */
 void pa_memblock_release(pa_memblock *b) {
     int r;
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     r = pa_atomic_dec(&b->n_acquired);
-    assert(r >= 1);
+    pa_assert(r >= 1);
 
     /* Signal a waiting thread that this memblock is no longer used */
     if (r == 1 && pa_atomic_load(&b->please_signal))
@@ -402,60 +481,69 @@ void pa_memblock_release(pa_memblock *b) {
 }
 
 size_t pa_memblock_get_length(pa_memblock *b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     return b->length;
 }
 
 pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     return b->pool;
 }
 
 /* No lock necessary */
 pa_memblock* pa_memblock_ref(pa_memblock*b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     PA_REFCNT_INC(b);
     return b;
 }
 
 static void memblock_free(pa_memblock *b) {
-    assert(b);
+    pa_assert(b);
 
-    assert(pa_atomic_load(&b->n_acquired) == 0);
+    pa_assert(pa_atomic_load(&b->n_acquired) == 0);
 
     stat_remove(b);
 
     switch (b->type) {
         case PA_MEMBLOCK_USER :
-            assert(b->per_type.user.free_cb);
+            pa_assert(b->per_type.user.free_cb);
             b->per_type.user.free_cb(pa_atomic_ptr_load(&b->data));
 
             /* Fall through */
 
         case PA_MEMBLOCK_FIXED:
-        case PA_MEMBLOCK_APPENDED :
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+
+            break;
+
+        case PA_MEMBLOCK_APPENDED:
+
+            /* We could attach it to unused_memblocks, but that would
+             * probably waste some considerable amount of memory */
             pa_xfree(b);
             break;
 
-        case PA_MEMBLOCK_IMPORTED : {
+        case PA_MEMBLOCK_IMPORTED: {
             pa_memimport_segment *segment;
             pa_memimport *import;
 
             /* FIXME! This should be implemented lock-free */
 
-            segment = b->per_type.imported.segment;
-            assert(segment);
-            import = segment->import;
-            assert(import);
+            pa_assert_se(segment = b->per_type.imported.segment);
+            pa_assert_se(import = segment->import);
 
             pa_mutex_lock(import->mutex);
-            pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
+
+            pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
+
+            pa_assert(segment->n_blocks >= 1);
             if (-- segment->n_blocks <= 0)
                 segment_detach(segment);
 
@@ -463,20 +551,27 @@ static void memblock_free(pa_memblock *b) {
 
             import->release_cb(import, b->per_type.imported.id, import->userdata);
 
-            pa_xfree(b);
+            if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                pa_xfree(b);
+
             break;
         }
 
         case PA_MEMBLOCK_POOL_EXTERNAL:
         case PA_MEMBLOCK_POOL: {
             struct mempool_slot *slot;
-            int call_free;
+            bool call_free;
 
-            slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
-            assert(slot);
+            pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)));
 
             call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
 
+/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
+/*             if (PA_UNLIKELY(pa_in_valgrind())) { */
+/*                 VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
+/*             } */
+/* #endif */
+
             /* The free list dimensions should easily allow all slots
              * to fit in, hence try harder if pushing this slot into
              * the free list fails */
@@ -484,21 +579,22 @@ static void memblock_free(pa_memblock *b) {
                 ;
 
             if (call_free)
-                pa_xfree(b);
+                if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
+                    pa_xfree(b);
 
             break;
         }
 
         case PA_MEMBLOCK_TYPE_MAX:
         default:
-            abort();
+            pa_assert_not_reached();
     }
 }
 
 /* No lock necessary */
 void pa_memblock_unref(pa_memblock*b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
     if (PA_REFCNT_DEC(b) > 0)
         return;
@@ -508,7 +604,7 @@ void pa_memblock_unref(pa_memblock*b) {
 
 /* Self locked */
 static void memblock_wait(pa_memblock *b) {
-    assert(b);
+    pa_assert(b);
 
     if (pa_atomic_load(&b->n_acquired) > 0) {
         /* We need to wait until all threads gave up access to the
@@ -526,11 +622,11 @@ static void memblock_wait(pa_memblock *b) {
 
 /* No lock necessary. This function is not multiple caller safe! */
 static void memblock_make_local(pa_memblock *b) {
-    assert(b);
+    pa_assert(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))) {
@@ -542,7 +638,7 @@ static void memblock_make_local(pa_memblock *b) {
             pa_atomic_ptr_store(&b->data, new_data);
 
             b->type = PA_MEMBLOCK_POOL_EXTERNAL;
-            b->read_only = 0;
+            b->read_only = false;
 
             goto finish;
         }
@@ -553,7 +649,7 @@ static void memblock_make_local(pa_memblock *b) {
     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]);
@@ -561,92 +657,108 @@ finish:
     memblock_wait(b);
 }
 
-/* No lock necessary. This function is not multiple caller safe*/
+/* No lock necessary. This function is not multiple caller safe */
 void pa_memblock_unref_fixed(pa_memblock *b) {
-    assert(b);
-    assert(PA_REFCNT_VALUE(b) > 0);
-    assert(b->type == PA_MEMBLOCK_FIXED);
+    pa_assert(b);
+    pa_assert(PA_REFCNT_VALUE(b) > 0);
+    pa_assert(b->type == PA_MEMBLOCK_FIXED);
 
-    if (PA_REFCNT_DEC(b) > 0)
+    if (PA_REFCNT_VALUE(b) > 1)
         memblock_make_local(b);
-    else
-        memblock_free(b);
+
+    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_memimport_segment *segment;
+    pa_memimport *import;
 
-    assert(b);
-    assert(b->type == PA_MEMBLOCK_IMPORTED);
+    pa_assert(b);
+    pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
 
-    assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
-    assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
+    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;
-    assert(seg);
-    assert(seg->import);
+    pa_assert_se(segment = b->per_type.imported.segment);
+    pa_assert_se(import = segment->import);
 
-    pa_mutex_lock(seg->import->mutex);
+    pa_mutex_lock(import->mutex);
 
-    pa_hashmap_remove(
-            seg->import->blocks,
-            PA_UINT32_TO_PTR(b->per_type.imported.id));
+    pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
 
     memblock_make_local(b);
 
-    if (-- seg->n_blocks <= 0)
-        segment_detach(seg);
+    pa_assert(segment->n_blocks >= 1);
+    if (-- segment->n_blocks <= 0)
+        segment_detach(segment);
 
-    pa_mutex_unlock(seg->import->mutex);
+    pa_mutex_unlock(import->mutex);
 }
 
-pa_mempool* pa_mempool_new(int shared) {
-    size_t ps;
+pa_mempool* pa_mempool_new(bool shared, size_t size) {
     pa_mempool *p;
+    char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
 
     p = pa_xnew(pa_mempool, 1);
 
-    p->mutex = pa_mutex_new(1);
-    p->semaphore = pa_semaphore_new(0);
+    p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
+    if (p->block_size < PA_PAGE_SIZE)
+        p->block_size = PA_PAGE_SIZE;
 
-#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->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);
 
-    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, maximum usable slot size is %lu",
+                 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)),
+                 (unsigned long) pa_mempool_block_size_max(p));
+
     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->mutex = pa_mutex_new(true, true);
+    p->semaphore = pa_semaphore_new(0);
+
+    p->free_slots = pa_flist_new(p->n_blocks);
 
     return p;
 }
 
 void pa_mempool_free(pa_mempool *p) {
-    assert(p);
+    pa_assert(p);
 
     pa_mutex_lock(p->mutex);
 
@@ -658,10 +770,52 @@ void pa_mempool_free(pa_mempool *p) {
 
     pa_mutex_unlock(p->mutex);
 
-    if (pa_atomic_load(&p->stat.n_allocated) > 0)
-        pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
-
     pa_flist_free(p->free_slots, NULL);
+
+    if (pa_atomic_load(&p->stat.n_allocated) > 0) {
+
+        /* Ouch, somebody is retaining a memory block reference! */
+
+#ifdef DEBUG_REF
+        unsigned i;
+        pa_flist *list;
+
+        /* Let's try to find at least one of those leaked memory blocks */
+
+        list = pa_flist_new(p->n_blocks);
+
+        for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) {
+            struct mempool_slot *slot;
+            pa_memblock *b, *k;
+
+            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i));
+            b = mempool_slot_data(slot);
+
+            while ((k = pa_flist_pop(p->free_slots))) {
+                while (pa_flist_push(list, k) < 0)
+                    ;
+
+                if (b == k)
+                    break;
+            }
+
+            if (!k)
+                pa_log("REF: Leaked memory block %p", b);
+
+            while ((k = pa_flist_pop(list)))
+                while (pa_flist_push(p->free_slots, k) < 0)
+                    ;
+        }
+
+        pa_flist_free(list, NULL);
+
+#endif
+
+        pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", pa_atomic_load(&p->stat.n_allocated));
+
+/*         PA_DEBUG_TRAP; */
+    }
+
     pa_shm_free(&p->memory);
 
     pa_mutex_free(p->mutex);
@@ -672,28 +826,33 @@ void pa_mempool_free(pa_mempool *p) {
 
 /* No lock necessary */
 const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
-    assert(p);
+    pa_assert(p);
 
     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_flist *list;
 
-    assert(p);
+    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))
             ;
@@ -704,7 +863,7 @@ void pa_mempool_vacuum(pa_mempool *p) {
 
 /* No lock necessary */
 int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
-    assert(p);
+    pa_assert(p);
 
     if (!p->memory.shared)
         return -1;
@@ -715,21 +874,21 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
 }
 
 /* No lock necessary */
-int pa_mempool_is_shared(pa_mempool *p) {
-    assert(p);
+bool pa_mempool_is_shared(pa_mempool *p) {
+    pa_assert(p);
 
     return !!p->memory.shared;
 }
 
-/* For recieving blocks from other nodes */
+/* For receiving blocks from other nodes */
 pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
     pa_memimport *i;
 
-    assert(p);
-    assert(cb);
+    pa_assert(p);
+    pa_assert(cb);
 
     i = pa_xnew(pa_memimport, 1);
-    i->mutex = pa_mutex_new(0);
+    i->mutex = pa_mutex_new(true, true);
     i->pool = p;
     i->segments = pa_hashmap_new(NULL, NULL);
     i->blocks = pa_hashmap_new(NULL, NULL);
@@ -752,7 +911,7 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
     if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
         return NULL;
 
-    seg = pa_xnew(pa_memimport_segment, 1);
+    seg = pa_xnew0(pa_memimport_segment, 1);
 
     if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
         pa_xfree(seg);
@@ -760,18 +919,22 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
     }
 
     seg->import = i;
-    seg->n_blocks = 0;
+    seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size);
 
-    pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(shm_id), seg);
+    pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg);
     return seg;
 }
 
 /* Should be called locked */
 static void segment_detach(pa_memimport_segment *seg) {
-    assert(seg);
+    pa_assert(seg);
 
     pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
     pa_shm_free(&seg->memory);
+
+    if (seg->trap)
+        pa_memtrap_remove(seg->trap);
+
     pa_xfree(seg);
 }
 
@@ -780,14 +943,14 @@ void pa_memimport_free(pa_memimport *i) {
     pa_memexport *e;
     pa_memblock *b;
 
-    assert(i);
+    pa_assert(i);
 
     pa_mutex_lock(i->mutex);
 
-    while ((b = pa_hashmap_get_first(i->blocks)))
+    while ((b = pa_hashmap_first(i->blocks)))
         memblock_replace_import(b);
 
-    assert(pa_hashmap_size(i->segments) == 0);
+    pa_assert(pa_hashmap_size(i->segments) == 0);
 
     pa_mutex_unlock(i->mutex);
 
@@ -801,8 +964,8 @@ void pa_memimport_free(pa_memimport *i) {
 
     pa_mutex_unlock(i->pool->mutex);
 
-    pa_hashmap_free(i->blocks, NULL, NULL);
-    pa_hashmap_free(i->segments, NULL, NULL);
+    pa_hashmap_free(i->blocks);
+    pa_hashmap_free(i->segments);
 
     pa_mutex_free(i->mutex);
 
@@ -814,10 +977,15 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
     pa_memblock *b = NULL;
     pa_memimport_segment *seg;
 
-    assert(i);
+    pa_assert(i);
 
     pa_mutex_lock(i->mutex);
 
+    if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) {
+        pa_memblock_ref(b);
+        goto finish;
+    }
+
     if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
         goto finish;
 
@@ -828,11 +996,14 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
     if (offset+size > seg->memory.size)
         goto finish;
 
-    b = pa_xnew(pa_memblock, 1);
+    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);
@@ -844,43 +1015,46 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
 
     seg->n_blocks++;
 
+    stat_add(b);
+
 finish:
     pa_mutex_unlock(i->mutex);
 
-    if (b)
-    stat_add(b);
-
     return b;
 }
 
 int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
     pa_memblock *b;
-    assert(i);
+    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 */
 pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
     pa_memexport *e;
 
-    assert(p);
-    assert(cb);
+    pa_assert(p);
+    pa_assert(cb);
 
     if (!p->memory.shared)
         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);
@@ -895,17 +1069,18 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
 }
 
 void pa_memexport_free(pa_memexport *e) {
-    assert(e);
+    pa_assert(e);
 
     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_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
     pa_mutex_unlock(e->pool->mutex);
 
+    pa_mutex_free(e->mutex);
     pa_xfree(e);
 }
 
@@ -913,7 +1088,7 @@ void pa_memexport_free(pa_memexport *e) {
 int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
     pa_memblock *b;
 
-    assert(e);
+    pa_assert(e);
 
     pa_mutex_lock(e->mutex);
 
@@ -933,11 +1108,11 @@ int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
 
 /*     pa_log("Processing release for %u", id); */
 
-    assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
-    assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
+    pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
+    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);
 
@@ -952,8 +1127,8 @@ fail:
 /* Self-locked */
 static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
     struct memexport_slot *slot, *next;
-    assert(e);
-    assert(i);
+    pa_assert(e);
+    pa_assert(i);
 
     pa_mutex_lock(e->mutex);
 
@@ -965,7 +1140,7 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
             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);
     }
@@ -977,13 +1152,13 @@ static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
 static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
     pa_memblock *n;
 
-    assert(p);
-    assert(b);
+    pa_assert(p);
+    pa_assert(b);
 
     if (b->type == PA_MEMBLOCK_IMPORTED ||
         b->type == PA_MEMBLOCK_POOL ||
         b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
-        assert(b->pool == p);
+        pa_assert(b->pool == p);
         return pa_memblock_ref(b);
     }
 
@@ -999,15 +1174,14 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
     pa_shm *memory;
     struct memexport_slot *slot;
     void *data;
-    size_t length;
 
-    assert(e);
-    assert(b);
-    assert(block_id);
-    assert(shm_id);
-    assert(offset);
-    assert(size);
-    assert(b->pool == e->pool);
+    pa_assert(e);
+    pa_assert(b);
+    pa_assert(block_id);
+    pa_assert(shm_id);
+    pa_assert(offset);
+    pa_assert(size);
+    pa_assert(b->pool == e->pool);
 
     if (!(b = memblock_shared_copy(e->pool, b)))
         return -1;
@@ -1027,7 +1201,7 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
 
     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); */
@@ -1035,25 +1209,25 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
     data = pa_memblock_acquire(b);
 
     if (b->type == PA_MEMBLOCK_IMPORTED) {
-        assert(b->per_type.imported.segment);
+        pa_assert(b->per_type.imported.segment);
         memory = &b->per_type.imported.segment->memory;
     } else {
-        assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
-        assert(b->pool);
+        pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
+        pa_assert(b->pool);
         memory = &b->pool->memory;
     }
 
-    assert(data >= memory->ptr);
-    assert((uint8_t*) data + length <= (uint8_t*) memory->ptr + memory->size);
+    pa_assert(data >= memory->ptr);
+    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;
-    *size = length;
+    *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, length);
+    pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
 
     return 0;
 }