2004-07-16 19:56:36 +00:00
|
|
|
/***
|
2006-06-19 21:53:48 +00:00
|
|
|
This file is part of PulseAudio.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-02-13 15:35:19 +00:00
|
|
|
Copyright 2004-2006 Lennart Poettering
|
|
|
|
|
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
2004-11-14 14:58:54 +00:00
|
|
|
it under the terms of the GNU Lesser General Public License as
|
|
|
|
|
published by the Free Software Foundation; either version 2.1 of the
|
|
|
|
|
License, or (at your option) any later version.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
2004-07-16 19:56:36 +00:00
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2007-10-28 19:13:50 +00:00
|
|
|
Lesser General Public License for more details
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-14 14:58:54 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2014-11-26 14:14:51 +01:00
|
|
|
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
2004-07-16 19:56:36 +00:00
|
|
|
***/
|
|
|
|
|
|
2004-07-16 19:16:42 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-06-15 15:18:33 +00:00
|
|
|
#include <stdio.h>
|
2004-06-08 23:54:24 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2006-08-18 19:55:18 +00:00
|
|
|
#include <unistd.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <signal.h>
|
|
|
|
|
#include <errno.h>
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2008-08-18 19:55:55 +02:00
|
|
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
|
|
|
|
#include <valgrind/memcheck.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/xmalloc.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulse/def.h>
|
2006-02-17 12:10:58 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
#include <pulsecore/shm.h>
|
|
|
|
|
#include <pulsecore/log.h>
|
|
|
|
|
#include <pulsecore/hashmap.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/semaphore.h>
|
2011-06-13 15:04:33 +02:00
|
|
|
#include <pulsecore/mutex.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/macro.h>
|
2011-06-13 15:04:33 +02:00
|
|
|
#include <pulsecore/refcnt.h>
|
|
|
|
|
#include <pulsecore/llist.h>
|
2007-10-28 19:13:50 +00:00
|
|
|
#include <pulsecore/flist.h>
|
|
|
|
|
#include <pulsecore/core-util.h>
|
2009-04-21 22:56:08 +02:00
|
|
|
#include <pulsecore/memtrap.h>
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2004-06-08 23:54:24 +00:00
|
|
|
#include "memblock.h"
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
/* 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
|
2009-10-28 21:29:01 +01:00
|
|
|
#define PA_MEMPOOL_SLOT_SIZE (64*1024)
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
#define PA_MEMEXPORT_SLOTS_MAX 128
|
|
|
|
|
|
2009-02-18 21:52:21 +01:00
|
|
|
#define PA_MEMIMPORT_SLOTS_MAX 160
|
2006-08-18 19:55:18 +00:00
|
|
|
#define PA_MEMIMPORT_SEGMENTS_MAX 16
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
struct pa_memblock {
|
|
|
|
|
PA_REFCNT_DECLARE; /* the reference counter */
|
|
|
|
|
pa_mempool *pool;
|
|
|
|
|
|
|
|
|
|
pa_memblock_type_t type;
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
bool read_only:1;
|
|
|
|
|
bool is_silence:1;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_atomic_ptr_t data;
|
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
|
|
pa_atomic_t n_acquired;
|
|
|
|
|
pa_atomic_t please_signal;
|
|
|
|
|
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
/* If type == PA_MEMBLOCK_USER this points to a function for freeing this memory block */
|
|
|
|
|
pa_free_cb_t free_cb;
|
2014-04-15 02:08:23 +02:00
|
|
|
/* If type == PA_MEMBLOCK_USER this is passed as free_cb argument */
|
|
|
|
|
void *free_cb_data;
|
2007-10-28 19:13:50 +00:00
|
|
|
} user;
|
|
|
|
|
|
2011-03-12 19:45:02 +01:00
|
|
|
struct {
|
2007-10-28 19:13:50 +00:00
|
|
|
uint32_t id;
|
|
|
|
|
pa_memimport_segment *segment;
|
|
|
|
|
} imported;
|
|
|
|
|
} per_type;
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
struct pa_memimport_segment {
|
|
|
|
|
pa_memimport *import;
|
|
|
|
|
pa_shm memory;
|
2009-04-21 22:56:08 +02:00
|
|
|
pa_memtrap *trap;
|
2006-08-18 19:55:18 +00:00
|
|
|
unsigned n_blocks;
|
2014-04-25 15:30:41 +02:00
|
|
|
bool writable;
|
2006-08-18 19:55:18 +00:00
|
|
|
};
|
|
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
/*
|
|
|
|
|
* If true, this segment's lifetime will not be limited by the
|
|
|
|
|
* number of active blocks (seg->n_blocks) using its shared memory.
|
|
|
|
|
* Rather, it will exist for the full lifetime of the memimport it
|
|
|
|
|
* is attached to.
|
|
|
|
|
*
|
|
|
|
|
* This is done to support memfd blocks transport.
|
|
|
|
|
*
|
|
|
|
|
* To transfer memfd-backed blocks without passing their fd every
|
|
|
|
|
* time, thus minimizing overhead and avoiding fd leaks, a command
|
|
|
|
|
* is sent with the memfd fd as ancil data very early on.
|
|
|
|
|
*
|
|
|
|
|
* This command has an ID that identifies the memfd region. Further
|
|
|
|
|
* block references are then exclusively done using this ID. On the
|
|
|
|
|
* receiving end, such logic is enabled by the memimport's segment
|
|
|
|
|
* hash and 'permanent' segments below.
|
|
|
|
|
*/
|
|
|
|
|
static bool segment_is_permanent(pa_memimport_segment *seg) {
|
|
|
|
|
pa_assert(seg);
|
|
|
|
|
return seg->memory.type == PA_MEM_TYPE_SHARED_MEMFD;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-24 15:49:22 +02:00
|
|
|
/* A collection of multiple segments */
|
2006-08-18 19:55:18 +00:00
|
|
|
struct pa_memimport {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex *mutex;
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_mempool *pool;
|
|
|
|
|
pa_hashmap *segments;
|
|
|
|
|
pa_hashmap *blocks;
|
|
|
|
|
|
|
|
|
|
/* Called whenever an imported memory block is no longer
|
|
|
|
|
* needed. */
|
|
|
|
|
pa_memimport_release_cb_t release_cb;
|
|
|
|
|
void *userdata;
|
|
|
|
|
|
|
|
|
|
PA_LLIST_FIELDS(pa_memimport);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct memexport_slot {
|
|
|
|
|
PA_LLIST_FIELDS(struct memexport_slot);
|
|
|
|
|
pa_memblock *block;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct pa_memexport {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex *mutex;
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_mempool *pool;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
PA_LLIST_HEAD(struct memexport_slot, free_slots);
|
|
|
|
|
PA_LLIST_HEAD(struct memexport_slot, used_slots);
|
|
|
|
|
unsigned n_init;
|
2014-12-05 11:54:39 +01:00
|
|
|
unsigned baseidx;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
/* Called whenever a client from which we imported a memory block
|
|
|
|
|
which we in turn exported to another client dies and we need to
|
|
|
|
|
revoke the memory block accordingly */
|
|
|
|
|
pa_memexport_revoke_cb_t revoke_cb;
|
|
|
|
|
void *userdata;
|
|
|
|
|
|
|
|
|
|
PA_LLIST_FIELDS(pa_memexport);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct pa_mempool {
|
2016-03-13 00:51:12 +02:00
|
|
|
/* Reference count the mempool
|
|
|
|
|
*
|
|
|
|
|
* Any block allocation from the pool itself, or even just imported from
|
|
|
|
|
* another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED),
|
|
|
|
|
* shall increase the refcount.
|
|
|
|
|
*
|
|
|
|
|
* This is done for per-client mempools: global references to blocks in
|
|
|
|
|
* the pool, or just to attached ones, can still be lingering around when
|
|
|
|
|
* the client connection dies and all per-client objects are to be freed.
|
|
|
|
|
* That is, current PulseAudio design does not guarantee that the client
|
|
|
|
|
* mempool blocks are referenced only by client-specific objects.
|
|
|
|
|
*
|
|
|
|
|
* For further details, please check:
|
|
|
|
|
* https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html
|
|
|
|
|
*/
|
|
|
|
|
PA_REFCNT_DECLARE;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_semaphore *semaphore;
|
|
|
|
|
pa_mutex *mutex;
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_shm memory;
|
|
|
|
|
size_t block_size;
|
2007-10-28 19:13:50 +00:00
|
|
|
unsigned n_blocks;
|
2014-04-25 15:30:41 +02:00
|
|
|
bool is_remote_writable;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_atomic_t n_init;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
PA_LLIST_HEAD(pa_memimport, imports);
|
|
|
|
|
PA_LLIST_HEAD(pa_memexport, exports);
|
|
|
|
|
|
|
|
|
|
/* A list of free slots that may be reused */
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_flist *free_slots;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_mempool_stat stat;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void segment_detach(pa_memimport_segment *seg);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree);
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void stat_add(pa_memblock*b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(b->pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&b->pool->stat.n_allocated);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
|
2006-08-29 02:01:39 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&b->pool->stat.n_accumulated);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (b->type == PA_MEMBLOCK_IMPORTED) {
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&b->pool->stat.n_imported);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
2006-08-19 16:25:15 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
|
|
|
|
|
pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
|
2004-08-17 19:37:29 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void stat_remove(pa_memblock *b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(b->pool);
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0);
|
|
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&b->pool->stat.n_allocated);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (b->type == PA_MEMBLOCK_IMPORTED) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
|
|
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&b->pool->stat.n_imported);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
2006-08-19 16:25:15 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
2004-06-15 15:18:33 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
|
|
|
|
|
pa_memblock *b;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(length);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (!(b = pa_memblock_new_pool(p, length)))
|
|
|
|
|
b = memblock_new_appended(p, length);
|
|
|
|
|
|
|
|
|
|
return b;
|
2004-08-17 19:37:29 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
|
|
|
|
|
pa_memblock *b;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* If -1 is passed as length we choose the size for the caller. */
|
|
|
|
|
|
|
|
|
|
if (length == (size_t) -1)
|
2011-08-15 13:24:51 +08:00
|
|
|
length = pa_mempool_block_size_max(p);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
|
|
|
|
|
PA_REFCNT_INIT(b);
|
|
|
|
|
b->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2004-07-03 23:35:12 +00:00
|
|
|
b->type = PA_MEMBLOCK_APPENDED;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->read_only = b->is_silence = false;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
|
2004-06-08 23:54:24 +00:00
|
|
|
b->length = length;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_store(&b->n_acquired, 0);
|
|
|
|
|
pa_atomic_store(&b->please_signal, 0);
|
2006-11-06 13:06:01 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
stat_add(b);
|
2004-06-08 23:54:24 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
|
|
|
|
|
struct mempool_slot *slot;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(slot = pa_flist_pop(p->free_slots))) {
|
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
|
|
/* The free list was empty, we have to allocate a new entry */
|
|
|
|
|
|
|
|
|
|
if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
|
|
|
|
|
pa_atomic_dec(&p->n_init);
|
|
|
|
|
else
|
2008-08-19 22:39:54 +02:00
|
|
|
slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
if (!slot) {
|
2011-01-22 01:08:36 +01:00
|
|
|
if (pa_log_ratelimit(PA_LOG_DEBUG))
|
2009-08-12 21:40:38 +02:00
|
|
|
pa_log_debug("Pool full");
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_inc(&p->stat.n_pool_full);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2008-10-05 22:51:25 +02:00
|
|
|
/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
|
|
|
|
|
/* if (PA_UNLIKELY(pa_in_valgrind())) { */
|
|
|
|
|
/* VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
|
|
|
|
|
/* } */
|
|
|
|
|
/* #endif */
|
2008-08-18 19:55:55 +02:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
return slot;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
/* No lock necessary, totally redundant anyway */
|
|
|
|
|
static inline void* mempool_slot_data(struct mempool_slot *slot) {
|
|
|
|
|
return slot;
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
|
|
|
|
|
pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
|
|
|
|
|
pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
|
|
|
|
|
unsigned idx;
|
|
|
|
|
|
|
|
|
|
if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-25 15:30:41 +02:00
|
|
|
/* No lock necessary */
|
|
|
|
|
bool pa_mempool_is_remote_writable(pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
return p->is_remote_writable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
|
|
|
|
void pa_mempool_set_is_remote_writable(pa_mempool *p, bool writable) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(!writable || pa_mempool_is_shared(p));
|
|
|
|
|
p->is_remote_writable = writable;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
|
|
|
|
|
pa_memblock *b = NULL;
|
|
|
|
|
struct mempool_slot *slot;
|
2009-09-17 01:21:56 +02:00
|
|
|
static int mempool_disable = 0;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(length);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2009-09-17 01:21:56 +02:00
|
|
|
if (mempool_disable == 0)
|
|
|
|
|
mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1;
|
|
|
|
|
|
|
|
|
|
if (mempool_disable > 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* 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. */
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (length == (size_t) -1)
|
|
|
|
|
length = pa_mempool_block_size_max(p);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (!(slot = mempool_allocate_slot(p)))
|
|
|
|
|
return NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
b = mempool_slot_data(slot);
|
|
|
|
|
b->type = PA_MEMBLOCK_POOL;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
} else if (p->block_size >= length) {
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (!(slot = mempool_allocate_slot(p)))
|
|
|
|
|
return NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
|
|
|
|
|
b = pa_xnew(pa_memblock, 1);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
} else {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&p->stat.n_too_large_for_pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-29 01:16:47 +00:00
|
|
|
PA_REFCNT_INIT(b);
|
2006-08-18 19:55:18 +00:00
|
|
|
b->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2013-06-27 19:28:09 +02:00
|
|
|
b->read_only = b->is_silence = false;
|
2007-10-28 19:13:50 +00:00
|
|
|
b->length = length;
|
|
|
|
|
pa_atomic_store(&b->n_acquired, 0);
|
|
|
|
|
pa_atomic_store(&b->please_signal, 0);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
stat_add(b);
|
2004-06-08 23:54:24 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, bool read_only) {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memblock *b;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(d);
|
|
|
|
|
pa_assert(length != (size_t) -1);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
|
|
|
|
|
b = pa_xnew(pa_memblock, 1);
|
2009-09-18 22:15:33 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INIT(b);
|
|
|
|
|
b->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2004-11-17 00:05:25 +00:00
|
|
|
b->type = PA_MEMBLOCK_FIXED;
|
2006-08-18 19:55:18 +00:00
|
|
|
b->read_only = read_only;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->is_silence = false;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, d);
|
2004-06-08 23:54:24 +00:00
|
|
|
b->length = length;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_store(&b->n_acquired, 0);
|
|
|
|
|
pa_atomic_store(&b->please_signal, 0);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
stat_add(b);
|
2004-08-14 20:25:32 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2014-04-15 02:08:23 +02:00
|
|
|
pa_memblock *pa_memblock_new_user(
|
|
|
|
|
pa_mempool *p,
|
|
|
|
|
void *d,
|
|
|
|
|
size_t length,
|
|
|
|
|
pa_free_cb_t free_cb,
|
|
|
|
|
void *free_cb_data,
|
|
|
|
|
bool read_only) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_memblock *b;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(d);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(length);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(length != (size_t) -1);
|
|
|
|
|
pa_assert(free_cb);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
|
|
|
|
|
b = pa_xnew(pa_memblock, 1);
|
2009-09-18 22:15:33 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_REFCNT_INIT(b);
|
|
|
|
|
b->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2004-08-14 20:25:32 +00:00
|
|
|
b->type = PA_MEMBLOCK_USER;
|
2006-08-18 19:55:18 +00:00
|
|
|
b->read_only = read_only;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->is_silence = false;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, d);
|
2004-08-14 20:25:32 +00:00
|
|
|
b->length = length;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_store(&b->n_acquired, 0);
|
|
|
|
|
pa_atomic_store(&b->please_signal, 0);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
b->per_type.user.free_cb = free_cb;
|
2014-04-15 02:08:23 +02:00
|
|
|
b->per_type.user.free_cb_data = free_cb_data;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
stat_add(b);
|
2004-06-08 23:54:24 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-25 15:30:41 +02:00
|
|
|
/* No lock necessary */
|
|
|
|
|
bool pa_memblock_is_ours(pa_memblock *b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
return b->type != PA_MEMBLOCK_IMPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2013-06-27 19:28:09 +02:00
|
|
|
bool pa_memblock_is_read_only(pa_memblock *b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
return b->read_only && PA_REFCNT_VALUE(b) == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2013-06-27 19:28:09 +02:00
|
|
|
bool pa_memblock_is_silence(pa_memblock *b) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
return b->is_silence;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2013-06-27 19:28:09 +02:00
|
|
|
void pa_memblock_set_is_silence(pa_memblock *b, bool v) {
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
b->is_silence = v;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2013-06-27 19:28:09 +02:00
|
|
|
bool pa_memblock_ref_is_one(pa_memblock *b) {
|
2008-05-15 23:34:41 +00:00
|
|
|
int r;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
return r == 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
|
|
|
|
void* pa_memblock_acquire(pa_memblock *b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
pa_atomic_inc(&b->n_acquired);
|
|
|
|
|
|
|
|
|
|
return pa_atomic_ptr_load(&b->data);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-17 18:09:34 +03:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary, in corner cases locks by its own */
|
|
|
|
|
void pa_memblock_release(pa_memblock *b) {
|
|
|
|
|
int r;
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
r = pa_atomic_dec(&b->n_acquired);
|
|
|
|
|
pa_assert(r >= 1);
|
|
|
|
|
|
|
|
|
|
/* Signal a waiting thread that this memblock is no longer used */
|
|
|
|
|
if (r == 1 && pa_atomic_load(&b->please_signal))
|
|
|
|
|
pa_semaphore_post(b->pool->semaphore);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t pa_memblock_get_length(pa_memblock *b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
return b->length;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
/* Note! Always unref the returned pool after use */
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_assert(b->pool);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2007-10-28 19:13:50 +00:00
|
|
|
return b->pool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_memblock* pa_memblock_ref(pa_memblock*b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
2006-08-29 01:16:47 +00:00
|
|
|
|
|
|
|
|
PA_REFCNT_INC(b);
|
2004-06-08 23:54:24 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
static void memblock_free(pa_memblock *b) {
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool *pool;
|
2006-09-26 23:50:56 +00:00
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(b->pool);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_atomic_load(&b->n_acquired) == 0);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pool = b->pool;
|
2006-08-18 19:55:18 +00:00
|
|
|
stat_remove(b);
|
|
|
|
|
|
|
|
|
|
switch (b->type) {
|
|
|
|
|
case PA_MEMBLOCK_USER :
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b->per_type.user.free_cb);
|
2014-04-15 02:08:23 +02:00
|
|
|
b->per_type.user.free_cb(b->per_type.user.free_cb_data);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
/* Fall through */
|
|
|
|
|
|
|
|
|
|
case PA_MEMBLOCK_FIXED:
|
2007-10-28 19:13:50 +00:00
|
|
|
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
|
|
|
|
|
pa_xfree(b);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
break;
|
|
|
|
|
|
2009-09-18 22:15:33 +02:00
|
|
|
case PA_MEMBLOCK_APPENDED:
|
|
|
|
|
|
2012-04-06 15:06:27 +02:00
|
|
|
/* We could attach it to unused_memblocks, but that would
|
|
|
|
|
* probably waste some considerable amount of memory */
|
2009-09-18 22:15:33 +02:00
|
|
|
pa_xfree(b);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_MEMBLOCK_IMPORTED: {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memimport_segment *segment;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memimport *import;
|
2006-11-06 13:06:01 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* FIXME! This should be implemented lock-free */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-07-01 20:20:55 +02:00
|
|
|
pa_assert_se(segment = b->per_type.imported.segment);
|
|
|
|
|
pa_assert_se(import = segment->import);
|
2006-11-06 13:06:01 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(import->mutex);
|
2009-07-01 20:20:55 +02:00
|
|
|
|
2011-03-02 12:41:26 +01:00
|
|
|
pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
|
2009-07-01 20:20:55 +02:00
|
|
|
|
|
|
|
|
pa_assert(segment->n_blocks >= 1);
|
2006-08-18 19:55:18 +00:00
|
|
|
if (-- segment->n_blocks <= 0)
|
|
|
|
|
segment_detach(segment);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(import->mutex);
|
|
|
|
|
|
|
|
|
|
import->release_cb(import, b->per_type.imported.id, import->userdata);
|
|
|
|
|
|
|
|
|
|
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
|
|
|
|
|
pa_xfree(b);
|
2009-07-01 20:20:55 +02:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2004-06-18 17:12:50 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
case PA_MEMBLOCK_POOL_EXTERNAL:
|
|
|
|
|
case PA_MEMBLOCK_POOL: {
|
|
|
|
|
struct mempool_slot *slot;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool call_free;
|
2004-08-14 20:25:32 +00:00
|
|
|
|
2009-09-18 22:15:33 +02:00
|
|
|
pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data)));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-10-05 22:51:25 +02:00
|
|
|
/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
|
|
|
|
|
/* if (PA_UNLIKELY(pa_in_valgrind())) { */
|
|
|
|
|
/* VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
|
|
|
|
|
/* } */
|
|
|
|
|
/* #endif */
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* The free list dimensions should easily allow all slots
|
|
|
|
|
* to fit in, hence try harder if pushing this slot into
|
|
|
|
|
* the free list fails */
|
|
|
|
|
while (pa_flist_push(b->pool->free_slots, slot) < 0)
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
if (call_free)
|
|
|
|
|
if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0)
|
|
|
|
|
pa_xfree(b);
|
2006-08-19 16:25:15 +00:00
|
|
|
|
|
|
|
|
break;
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
2006-08-19 16:25:15 +00:00
|
|
|
|
|
|
|
|
case PA_MEMBLOCK_TYPE_MAX:
|
|
|
|
|
default:
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert_not_reached();
|
2004-06-08 23:54:24 +00:00
|
|
|
}
|
2016-03-13 00:51:12 +02:00
|
|
|
|
|
|
|
|
pa_mempool_unref(pool);
|
2004-06-08 23:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
|
|
|
|
void pa_memblock_unref(pa_memblock*b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
|
|
|
|
|
if (PA_REFCNT_DEC(b) > 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memblock_free(b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Self locked */
|
|
|
|
|
static void memblock_wait(pa_memblock *b) {
|
|
|
|
|
pa_assert(b);
|
|
|
|
|
|
|
|
|
|
if (pa_atomic_load(&b->n_acquired) > 0) {
|
|
|
|
|
/* We need to wait until all threads gave up access to the
|
|
|
|
|
* memory block before we can go on. Unfortunately this means
|
|
|
|
|
* that we have to lock and wait here. Sniff! */
|
|
|
|
|
|
|
|
|
|
pa_atomic_inc(&b->please_signal);
|
|
|
|
|
|
|
|
|
|
while (pa_atomic_load(&b->n_acquired) > 0)
|
|
|
|
|
pa_semaphore_wait(b->pool->semaphore);
|
|
|
|
|
|
|
|
|
|
pa_atomic_dec(&b->please_signal);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary. This function is not multiple caller safe! */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void memblock_make_local(pa_memblock *b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
|
2006-08-19 16:25:15 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (b->length <= b->pool->block_size) {
|
2006-08-18 19:55:18 +00:00
|
|
|
struct mempool_slot *slot;
|
|
|
|
|
|
|
|
|
|
if ((slot = mempool_allocate_slot(b->pool))) {
|
|
|
|
|
void *new_data;
|
|
|
|
|
/* We can move it into a local pool, perfect! */
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
new_data = mempool_slot_data(slot);
|
|
|
|
|
memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length);
|
|
|
|
|
pa_atomic_ptr_store(&b->data, new_data);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
b->type = PA_MEMBLOCK_POOL_EXTERNAL;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->read_only = false;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2006-08-19 16:25:15 +00:00
|
|
|
goto finish;
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
|
2006-11-06 13:06:01 +00:00
|
|
|
b->per_type.user.free_cb = pa_xfree;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
|
2014-04-15 02:08:23 +02:00
|
|
|
b->per_type.user.free_cb_data = pa_atomic_ptr_load(&b->data);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
b->type = PA_MEMBLOCK_USER;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->read_only = false;
|
2006-08-19 16:25:15 +00:00
|
|
|
|
|
|
|
|
finish:
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
|
|
|
|
|
pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]);
|
2007-10-28 19:13:50 +00:00
|
|
|
memblock_wait(b);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2013-07-16 17:09:13 +02:00
|
|
|
/* No lock necessary. This function is not multiple caller safe */
|
2006-01-11 01:17:39 +00:00
|
|
|
void pa_memblock_unref_fixed(pa_memblock *b) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(b) > 0);
|
|
|
|
|
pa_assert(b->type == PA_MEMBLOCK_FIXED);
|
2004-06-08 23:54:24 +00:00
|
|
|
|
2006-11-06 13:06:01 +00:00
|
|
|
if (PA_REFCNT_VALUE(b) > 1)
|
2006-08-18 19:55:18 +00:00
|
|
|
memblock_make_local(b);
|
2006-11-06 13:06:01 +00:00
|
|
|
|
|
|
|
|
pa_memblock_unref(b);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* 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 */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void memblock_replace_import(pa_memblock *b) {
|
2009-07-01 20:20:55 +02:00
|
|
|
pa_memimport_segment *segment;
|
|
|
|
|
pa_memimport *import;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b);
|
|
|
|
|
pa_assert(b->type == PA_MEMBLOCK_IMPORTED);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
|
|
|
|
|
pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&b->pool->stat.n_imported);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2009-07-01 20:20:55 +02:00
|
|
|
pa_assert_se(segment = b->per_type.imported.segment);
|
|
|
|
|
pa_assert_se(import = segment->import);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
2009-07-01 20:20:55 +02:00
|
|
|
pa_mutex_lock(import->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2011-03-02 12:41:26 +01:00
|
|
|
pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id)));
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
memblock_make_local(b);
|
|
|
|
|
|
2009-07-01 20:20:55 +02:00
|
|
|
pa_assert(segment->n_blocks >= 1);
|
|
|
|
|
if (-- segment->n_blocks <= 0)
|
|
|
|
|
segment_detach(segment);
|
|
|
|
|
|
|
|
|
|
pa_mutex_unlock(import->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
pa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size) {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_mempool *p;
|
2009-09-06 22:33:04 +02:00
|
|
|
char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2014-12-09 22:58:36 +01:00
|
|
|
p = pa_xnew0(pa_mempool, 1);
|
2016-03-13 00:51:12 +02:00
|
|
|
PA_REFCNT_INIT(p);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
|
|
|
|
|
if (p->block_size < PA_PAGE_SIZE)
|
|
|
|
|
p->block_size = PA_PAGE_SIZE;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-10-01 01:14:36 +02:00
|
|
|
if (size <= 0)
|
|
|
|
|
p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
|
|
|
|
|
else {
|
|
|
|
|
p->n_blocks = (unsigned) (size / p->block_size);
|
|
|
|
|
|
|
|
|
|
if (p->n_blocks < 2)
|
|
|
|
|
p->n_blocks = 2;
|
|
|
|
|
}
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
if (pa_shm_create_rw(&p->memory, type, p->n_blocks * p->block_size, 0700) < 0) {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(p);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-10 02:55:57 +01:00
|
|
|
pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s, maximum usable slot size is %lu",
|
2016-03-13 00:57:06 +02:00
|
|
|
pa_mem_type_to_string(type),
|
2008-10-01 01:14:36 +02:00
|
|
|
p->n_blocks,
|
|
|
|
|
pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
|
2009-01-10 02:55:57 +01:00
|
|
|
pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)),
|
|
|
|
|
(unsigned long) pa_mempool_block_size_max(p));
|
2008-10-01 01:14:36 +02:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_store(&p->n_init, 0);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
|
|
|
|
|
PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
p->mutex = pa_mutex_new(true, true);
|
2011-08-15 09:51:37 +08:00
|
|
|
p->semaphore = pa_semaphore_new(0);
|
|
|
|
|
|
2008-06-27 20:13:26 +02:00
|
|
|
p->free_slots = pa_flist_new(p->n_blocks);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
static void mempool_free(pa_mempool *p) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(p->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
while (p->imports)
|
|
|
|
|
pa_memimport_free(p->imports);
|
|
|
|
|
|
|
|
|
|
while (p->exports)
|
|
|
|
|
pa_memexport_free(p->exports);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(p->mutex);
|
|
|
|
|
|
|
|
|
|
pa_flist_free(p->free_slots, NULL);
|
|
|
|
|
|
|
|
|
|
if (pa_atomic_load(&p->stat.n_allocated) > 0) {
|
2009-03-25 02:42:18 +01:00
|
|
|
|
|
|
|
|
/* 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; */
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_shm_free(&p->memory);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_mutex_free(p->mutex);
|
|
|
|
|
pa_semaphore_free(p->semaphore);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(p);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
return &p->stat;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
|
|
|
|
size_t pa_mempool_block_size_max(pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
return p->block_size - PA_ALIGN(sizeof(pa_memblock));
|
2007-10-28 19:13:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
void pa_mempool_vacuum(pa_mempool *p) {
|
|
|
|
|
struct mempool_slot *slot;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_flist *list;
|
|
|
|
|
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
|
2008-06-27 20:13:26 +02:00
|
|
|
list = pa_flist_new(p->n_blocks);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
while ((slot = pa_flist_pop(p->free_slots)))
|
|
|
|
|
while (pa_flist_push(list, slot) < 0)
|
|
|
|
|
;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
while ((slot = pa_flist_pop(list))) {
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
while (pa_flist_push(p->free_slots, slot))
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_flist_free(list, NULL);
|
2004-06-08 23:54:24 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
/* No lock necessary */
|
|
|
|
|
bool pa_mempool_is_shared(pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
|
|
|
|
|
return pa_mem_type_is_shared(p->memory.type);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
/* No lock necessary */
|
|
|
|
|
bool pa_mempool_is_memfd_backed(const pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
|
|
|
|
|
return (p->memory.type == PA_MEM_TYPE_SHARED_MEMFD);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
if (!pa_mempool_is_shared(p))
|
2006-08-18 19:55:18 +00:00
|
|
|
return -1;
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
*id = p->memory.id;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool* pa_mempool_ref(pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) > 0);
|
|
|
|
|
|
|
|
|
|
PA_REFCNT_INC(p);
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_mempool_unref(pa_mempool *p) {
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(PA_REFCNT_VALUE(p) > 0);
|
|
|
|
|
|
|
|
|
|
if (PA_REFCNT_DEC(p) <= 0)
|
|
|
|
|
mempool_free(p);
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-24 18:24:46 +02:00
|
|
|
/* For receiving blocks from other nodes */
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
|
|
|
|
|
pa_memimport *i;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(cb);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
i = pa_xnew(pa_memimport, 1);
|
2013-06-27 19:28:09 +02:00
|
|
|
i->mutex = pa_mutex_new(true, true);
|
2006-08-18 19:55:18 +00:00
|
|
|
i->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(i->pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
i->segments = pa_hashmap_new(NULL, NULL);
|
|
|
|
|
i->blocks = pa_hashmap_new(NULL, NULL);
|
|
|
|
|
i->release_cb = cb;
|
|
|
|
|
i->userdata = userdata;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(p->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
PA_LLIST_PREPEND(pa_memimport, p->imports, i);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(p->mutex);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
return i;
|
2004-08-17 19:37:29 +00:00
|
|
|
}
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Should be called locked */
|
2016-03-13 01:07:27 +02:00
|
|
|
static pa_memimport_segment* segment_attach(pa_memimport *i, pa_mem_type_t type, uint32_t shm_id,
|
|
|
|
|
int memfd_fd, bool writable) {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memimport_segment* seg;
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_assert(pa_mem_type_is_shared(type));
|
2004-08-17 19:37:29 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2009-09-18 21:24:26 +02:00
|
|
|
seg = pa_xnew0(pa_memimport_segment, 1);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
if (pa_shm_attach(&seg->memory, type, shm_id, memfd_fd, writable) < 0) {
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(seg);
|
|
|
|
|
return NULL;
|
2004-08-17 19:37:29 +00:00
|
|
|
}
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2014-04-25 15:30:41 +02:00
|
|
|
seg->writable = writable;
|
2006-08-18 19:55:18 +00:00
|
|
|
seg->import = i;
|
2009-04-21 22:56:08 +02:00
|
|
|
seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-09-18 21:24:26 +02:00
|
|
|
pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg);
|
2006-08-18 19:55:18 +00:00
|
|
|
return seg;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Should be called locked */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void segment_detach(pa_memimport_segment *seg) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(seg);
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_assert(seg->n_blocks == (segment_is_permanent(seg) ? 1 : 0));
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
|
|
|
|
|
pa_shm_free(&seg->memory);
|
2009-04-21 22:56:08 +02:00
|
|
|
|
|
|
|
|
if (seg->trap)
|
|
|
|
|
pa_memtrap_remove(seg->trap);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(seg);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Self-locked. Not multiple-caller safe */
|
2006-08-18 19:55:18 +00:00
|
|
|
void pa_memimport_free(pa_memimport *i) {
|
|
|
|
|
pa_memexport *e;
|
|
|
|
|
pa_memblock *b;
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_memimport_segment *seg;
|
|
|
|
|
void *state = NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(i);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(i->mutex);
|
|
|
|
|
|
2008-06-27 20:12:24 +02:00
|
|
|
while ((b = pa_hashmap_first(i->blocks)))
|
2007-10-28 19:13:50 +00:00
|
|
|
memblock_replace_import(b);
|
|
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
/* Permanent segments exist for the lifetime of the memimport. Now
|
|
|
|
|
* that we're freeing the memimport itself, clear them all up.
|
|
|
|
|
*
|
|
|
|
|
* Careful! segment_detach() internally removes itself from the
|
|
|
|
|
* memimport's hash; the same hash we're now using for iteration. */
|
|
|
|
|
PA_HASHMAP_FOREACH(seg, i->segments, state) {
|
|
|
|
|
if (segment_is_permanent(seg))
|
|
|
|
|
segment_detach(seg);
|
|
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_hashmap_size(i->segments) == 0);
|
|
|
|
|
|
|
|
|
|
pa_mutex_unlock(i->mutex);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(i->pool->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2006-11-06 13:06:01 +00:00
|
|
|
/* If we've exported this block further we need to revoke that export */
|
|
|
|
|
for (e = i->pool->exports; e; e = e->next)
|
|
|
|
|
memexport_revoke_blocks(e, i);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(i->pool->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_unref(i->pool);
|
2013-09-14 11:50:10 +05:30
|
|
|
pa_hashmap_free(i->blocks);
|
|
|
|
|
pa_hashmap_free(i->segments);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_free(i->mutex);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(i);
|
2004-07-15 20:12:21 +00:00
|
|
|
}
|
|
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
/* Create a new memimport's memfd segment entry, with passed SHM ID
|
|
|
|
|
* as key and the newly-created segment (with its mmap()-ed memfd
|
|
|
|
|
* memory region) as its value.
|
|
|
|
|
*
|
|
|
|
|
* Note! check comments at 'pa_shm->fd', 'segment_is_permanent()',
|
|
|
|
|
* and 'pa_pstream_register_memfd_mempool()' for further details. */
|
|
|
|
|
int pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable) {
|
|
|
|
|
pa_memimport_segment *seg;
|
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
|
|
pa_assert(i);
|
|
|
|
|
pa_assert(memfd_fd != -1);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(i->mutex);
|
|
|
|
|
|
|
|
|
|
if (!(seg = segment_attach(i, PA_MEM_TYPE_SHARED_MEMFD, shm_id, memfd_fd, writable)))
|
|
|
|
|
goto finish;
|
|
|
|
|
|
|
|
|
|
/* n_blocks acts as a segment reference count. To avoid the segment
|
|
|
|
|
* being deleted when receiving silent memchunks, etc., mark our
|
|
|
|
|
* permanent presence by incrementing that refcount. */
|
|
|
|
|
seg->n_blocks++;
|
|
|
|
|
|
|
|
|
|
pa_assert(segment_is_permanent(seg));
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_mutex_unlock(i->mutex);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Self-locked */
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id, uint32_t shm_id,
|
2014-04-25 15:30:41 +02:00
|
|
|
size_t offset, size_t size, bool writable) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memblock *b = NULL;
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memimport_segment *seg;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(i);
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_assert(pa_mem_type_is_shared(type));
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_mutex_lock(i->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2009-07-24 15:49:22 +02:00
|
|
|
if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) {
|
|
|
|
|
pa_memblock_ref(b);
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
|
2007-10-28 19:13:50 +00:00
|
|
|
goto finish;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) {
|
|
|
|
|
if (type == PA_MEM_TYPE_SHARED_MEMFD) {
|
|
|
|
|
pa_log("Bailing out! No cached memimport segment for memfd ID %u", shm_id);
|
|
|
|
|
pa_log("Did the other PA endpoint forget registering its memfd pool?");
|
2007-10-28 19:13:50 +00:00
|
|
|
goto finish;
|
2016-03-13 01:07:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_assert(type == PA_MEM_TYPE_SHARED_POSIX);
|
|
|
|
|
if (!(seg = segment_attach(i, type, shm_id, -1, writable)))
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
if (writable && !seg->writable) {
|
|
|
|
|
pa_log("Cannot import cached segment in write mode - previously mapped as read-only");
|
2014-04-25 15:30:41 +02:00
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (offset+size > seg->memory.size)
|
2007-10-28 19:13:50 +00:00
|
|
|
goto finish;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
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;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(b->pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
b->type = PA_MEMBLOCK_IMPORTED;
|
2014-04-25 15:30:41 +02:00
|
|
|
b->read_only = !writable;
|
2013-06-27 19:28:09 +02:00
|
|
|
b->is_silence = false;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
|
2006-08-18 19:55:18 +00:00
|
|
|
b->length = size;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_atomic_store(&b->n_acquired, 0);
|
|
|
|
|
pa_atomic_store(&b->please_signal, 0);
|
2006-08-18 19:55:18 +00:00
|
|
|
b->per_type.imported.id = block_id;
|
|
|
|
|
b->per_type.imported.segment = seg;
|
|
|
|
|
|
|
|
|
|
pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
|
|
|
|
|
|
|
|
|
|
seg->n_blocks++;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-07-24 15:49:22 +02:00
|
|
|
stat_add(b);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
finish:
|
|
|
|
|
pa_mutex_unlock(i->mutex);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
|
|
|
|
|
pa_memblock *b;
|
2007-10-28 19:13:50 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
pa_assert(i);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(i->mutex);
|
|
|
|
|
|
|
|
|
|
if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) {
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
memblock_replace_import(b);
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_mutex_unlock(i->mutex);
|
|
|
|
|
|
|
|
|
|
return ret;
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2015-01-08 13:11:17 +01:00
|
|
|
static pa_atomic_t export_baseidx = PA_ATOMIC_INIT(0);
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(cb);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2016-03-13 00:57:06 +02:00
|
|
|
if (!pa_mempool_is_shared(p))
|
2006-08-18 19:55:18 +00:00
|
|
|
return NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
e = pa_xnew(pa_memexport, 1);
|
2013-06-27 19:28:09 +02:00
|
|
|
e->mutex = pa_mutex_new(true, true);
|
2006-08-18 19:55:18 +00:00
|
|
|
e->pool = p;
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_ref(e->pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
|
|
|
|
|
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
|
|
|
|
|
e->n_init = 0;
|
|
|
|
|
e->revoke_cb = cb;
|
|
|
|
|
e->userdata = userdata;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(p->mutex);
|
2014-12-05 11:54:39 +01:00
|
|
|
|
2006-11-06 13:06:01 +00:00
|
|
|
PA_LLIST_PREPEND(pa_memexport, p->exports, e);
|
2015-01-08 13:11:17 +01:00
|
|
|
e->baseidx = (uint32_t) pa_atomic_add(&export_baseidx, PA_MEMEXPORT_SLOTS_MAX);
|
2014-12-05 11:54:39 +01:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(p->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
return e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void pa_memexport_free(pa_memexport *e) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(e);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
while (e->used_slots)
|
2014-12-05 11:54:39 +01:00
|
|
|
pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots + e->baseidx));
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(e->pool->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(e->pool->mutex);
|
|
|
|
|
|
2016-03-13 00:51:12 +02:00
|
|
|
pa_mempool_unref(e->pool);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_free(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_xfree(e);
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Self-locked */
|
2006-08-18 19:55:18 +00:00
|
|
|
int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memblock *b;
|
|
|
|
|
|
|
|
|
|
pa_assert(e);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2014-12-05 11:54:39 +01:00
|
|
|
if (id < e->baseidx)
|
|
|
|
|
goto fail;
|
|
|
|
|
id -= e->baseidx;
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (id >= e->n_init)
|
2007-10-28 19:13:50 +00:00
|
|
|
goto fail;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (!e->slots[id].block)
|
2007-10-28 19:13:50 +00:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
b = e->slots[id].block;
|
|
|
|
|
e->slots[id].block = NULL;
|
|
|
|
|
|
|
|
|
|
PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
|
|
|
|
|
PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
|
|
|
|
|
|
|
|
|
|
pa_mutex_unlock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2006-09-26 23:50:56 +00:00
|
|
|
/* pa_log("Processing release for %u", id); */
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0);
|
|
|
|
|
pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_dec(&e->pool->stat.n_exported);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memblock_unref(b);
|
2006-09-26 23:50:56 +00:00
|
|
|
|
2006-11-06 13:06:01 +00:00
|
|
|
return 0;
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
pa_mutex_unlock(e->mutex);
|
|
|
|
|
|
|
|
|
|
return -1;
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Self-locked */
|
2006-08-18 19:55:18 +00:00
|
|
|
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
|
|
|
|
|
struct memexport_slot *slot, *next;
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(e);
|
|
|
|
|
pa_assert(i);
|
|
|
|
|
|
|
|
|
|
pa_mutex_lock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
for (slot = e->used_slots; slot; slot = next) {
|
|
|
|
|
uint32_t idx;
|
|
|
|
|
next = slot->next;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
|
|
|
|
|
slot->block->per_type.imported.segment->import != i)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-12-05 11:54:39 +01:00
|
|
|
idx = (uint32_t) (slot - e->slots + e->baseidx);
|
2006-08-18 19:55:18 +00:00
|
|
|
e->revoke_cb(e, idx, e->userdata);
|
|
|
|
|
pa_memexport_process_release(e, idx);
|
|
|
|
|
}
|
2007-10-28 19:13:50 +00:00
|
|
|
|
|
|
|
|
pa_mutex_unlock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* No lock necessary */
|
2006-08-18 19:55:18 +00:00
|
|
|
static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
|
|
|
|
|
pa_memblock *n;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(b);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (b->type == PA_MEMBLOCK_IMPORTED ||
|
|
|
|
|
b->type == PA_MEMBLOCK_POOL ||
|
|
|
|
|
b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b->pool == p);
|
2006-08-18 19:55:18 +00:00
|
|
|
return pa_memblock_ref(b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(n = pa_memblock_new_pool(p, b->length)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
/* Self-locked */
|
2016-03-13 01:07:27 +02:00
|
|
|
int pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id,
|
|
|
|
|
uint32_t *shm_id, size_t *offset, size_t * size) {
|
|
|
|
|
pa_shm *memory;
|
2006-08-18 19:55:18 +00:00
|
|
|
struct memexport_slot *slot;
|
2007-10-28 19:13:50 +00:00
|
|
|
void *data;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(e);
|
|
|
|
|
pa_assert(b);
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_assert(type);
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(block_id);
|
|
|
|
|
pa_assert(shm_id);
|
|
|
|
|
pa_assert(offset);
|
|
|
|
|
pa_assert(size);
|
|
|
|
|
pa_assert(b->pool == e->pool);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
if (!(b = memblock_shared_copy(e->pool, b)))
|
|
|
|
|
return -1;
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_lock(e->mutex);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (e->free_slots) {
|
|
|
|
|
slot = e->free_slots;
|
|
|
|
|
PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
|
2007-10-28 19:13:50 +00:00
|
|
|
} else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX)
|
2006-08-18 19:55:18 +00:00
|
|
|
slot = &e->slots[e->n_init++];
|
2007-10-28 19:13:50 +00:00
|
|
|
else {
|
|
|
|
|
pa_mutex_unlock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
pa_memblock_unref(b);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
|
|
|
|
|
slot->block = b;
|
2014-12-05 11:54:39 +01:00
|
|
|
*block_id = (uint32_t) (slot - e->slots + e->baseidx);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_mutex_unlock(e->mutex);
|
2006-08-18 19:55:18 +00:00
|
|
|
/* pa_log("Got block id %u", *block_id); */
|
|
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
data = pa_memblock_acquire(b);
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
if (b->type == PA_MEMBLOCK_IMPORTED) {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b->per_type.imported.segment);
|
2006-08-18 19:55:18 +00:00
|
|
|
memory = &b->per_type.imported.segment->memory;
|
|
|
|
|
} else {
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
|
|
|
|
|
pa_assert(b->pool);
|
2016-03-13 01:07:27 +02:00
|
|
|
pa_assert(pa_mempool_is_shared(b->pool));
|
2006-08-18 19:55:18 +00:00
|
|
|
memory = &b->pool->memory;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_assert(data >= memory->ptr);
|
|
|
|
|
pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2016-03-13 01:07:27 +02:00
|
|
|
*type = memory->type;
|
2006-08-18 19:55:18 +00:00
|
|
|
*shm_id = memory->id;
|
2008-08-19 22:39:54 +02:00
|
|
|
*offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
|
2006-11-06 13:06:01 +00:00
|
|
|
*size = b->length;
|
2006-08-18 19:55:18 +00:00
|
|
|
|
2007-10-28 19:13:50 +00:00
|
|
|
pa_memblock_release(b);
|
|
|
|
|
|
2007-05-27 20:38:14 +00:00
|
|
|
pa_atomic_inc(&e->pool->stat.n_exported);
|
2008-08-19 22:39:54 +02:00
|
|
|
pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
|
2006-08-18 19:55:18 +00:00
|
|
|
|
|
|
|
|
return 0;
|
2004-07-15 20:12:21 +00:00
|
|
|
}
|