]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/memblock.c
don't allow --start in system mode
[pulseaudio] / src / pulsecore / memblock.c
index f0e2b4c9e1235fe3d1dc977c24d899c67823a896..b43113d60cf405f803f47d8b38e9932c1e6ce863 100644 (file)
@@ -1,5 +1,3 @@
-/* $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>
 
 #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
 
@@ -58,7 +61,9 @@ struct pa_memblock {
     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;
@@ -124,11 +129,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;
@@ -201,7 +201,7 @@ pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
     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);
@@ -214,13 +214,18 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t 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);
@@ -246,7 +251,7 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
             slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
 
         if (!slot) {
-            pa_log_debug("Pool full");
+            pa_log_info("Pool full");
             pa_atomic_inc(&p->stat.n_pool_full);
             return NULL;
         }
@@ -255,11 +260,9 @@ static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
     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 */
@@ -288,37 +291,43 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
     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 (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) {
+    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;
 
         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);
@@ -328,12 +337,13 @@ 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, 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);
@@ -341,6 +351,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int re
     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);
@@ -351,12 +362,13 @@ 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, 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))))
@@ -365,6 +377,7 @@ pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*
     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);
@@ -377,13 +390,39 @@ 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) {
+pa_bool_t 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 */
+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);
+
+    pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
+
+    return r == 1;
+}
+
 /* No lock necessary */
 void* pa_memblock_acquire(pa_memblock *b) {
     pa_assert(b);
@@ -449,7 +488,7 @@ static void memblock_free(pa_memblock *b) {
         case PA_MEMBLOCK_APPENDED :
             if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
                 pa_xfree(b);
-            
+
             break;
 
         case PA_MEMBLOCK_IMPORTED : {
@@ -541,7 +580,7 @@ static void memblock_make_local(pa_memblock *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))) {
@@ -553,7 +592,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;
         }
@@ -564,7 +603,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]);
@@ -584,6 +623,20 @@ void pa_memblock_unref_fixed(pa_memblock *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;
@@ -608,38 +661,27 @@ static void memblock_replace_import(pa_memblock *b) {
 
     memblock_make_local(b);
 
-    if (-- seg->n_blocks <= 0)
+    if (-- seg->n_blocks <= 0) {
+        pa_mutex_unlock(seg->import->mutex);
         segment_detach(seg);
-
-    pa_mutex_unlock(seg->import->mutex);
+    } 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) {
     pa_mempool *p;
 
     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;
 
-    pa_assert(p->block_size > sizeof(struct mempool_slot));
-
     if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
         pa_xfree(p);
         return NULL;
@@ -651,7 +693,7 @@ pa_mempool* pa_mempool_new(int shared) {
     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;
 }
@@ -673,7 +715,7 @@ void pa_mempool_free(pa_mempool *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);
@@ -691,6 +733,13 @@ const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *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;
@@ -698,16 +747,14 @@ void pa_mempool_vacuum(pa_mempool *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, (uint8_t*) slot - (uint8_t*) p->memory.ptr, p->block_size);
 
         while (pa_flist_push(p->free_slots, slot))
             ;
@@ -729,7 +776,7 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
 }
 
 /* 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;
@@ -743,7 +790,7 @@ pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void
     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);
@@ -798,7 +845,7 @@ void pa_memimport_free(pa_memimport *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);
 
     pa_assert(pa_hashmap_size(i->segments) == 0);
@@ -844,11 +891,12 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
 
     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);
@@ -871,18 +919,22 @@ finish:
 
 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 */
@@ -896,7 +948,7 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
         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);