pa_fdsem *pa_fdsem_new(void) { pa_fdsem *f; f = pa_xmalloc(PA_ALIGN(sizeof(pa_fdsem)) + PA_ALIGN(sizeof(pa_fdsem_data))); #ifdef HAVE_SYS_EVENTFD_H if ((f->efd = eventfd(0, EFD_CLOEXEC)) >= 0) f->fds[0] = f->fds[1] = -1; else #endif { if (pa_pipe_cloexec(f->fds) < 0) { pa_xfree(f); return NULL; } } f->data = (pa_fdsem_data*) ((uint8_t*) f + PA_ALIGN(sizeof(pa_fdsem))); pa_atomic_store(&f->data->waiting, 0); pa_atomic_store(&f->data->signalled, 0); pa_atomic_store(&f->data->in_pipe, 0); return f; }
pa_auth_cookie* pa_auth_cookie_get(pa_core *core, const char *cn, bool create, size_t size) { pa_auth_cookie *c; char *t; pa_assert(core); pa_assert(size > 0); t = pa_sprintf_malloc("auth-cookie%s%s", cn ? "@" : "", cn ? cn : ""); if ((c = pa_shared_get(core, t))) { pa_xfree(t); if (c->size != size) return NULL; return pa_auth_cookie_ref(c); } c = pa_xmalloc(PA_ALIGN(sizeof(pa_auth_cookie)) + size); PA_REFCNT_INIT(c); c->core = core; c->name = t; c->size = size; pa_assert_se(pa_shared_set(core, t, c) >= 0); if (pa_authkey_load(cn, create, (uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie)), size) < 0) { pa_auth_cookie_unref(c); return NULL; } return c; }
/* No lock necessary */ 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); if (mempool_disable == 0) mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1; 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 + PA_ALIGN(sizeof(pa_memblock))); } else if (p->block_size >= length) { if (!(slot = mempool_allocate_slot(p))) return NULL; if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) b = pa_xnew(pa_memblock, 1); b->type = PA_MEMBLOCK_POOL_EXTERNAL; pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); } else { pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size); pa_atomic_inc(&p->stat.n_too_large_for_pool); return NULL; } PA_REFCNT_INIT(b); b->pool = p; b->read_only = b->is_silence = false; b->length = length; pa_atomic_store(&b->n_acquired, 0); pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; }
/* The following is based on an example from the GNU libc documentation */ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { size_t size = 100; struct chunk *c = NULL; pa_assert(sb); pa_assert(format); for(;;) { va_list ap; int r; c = pa_xrealloc(c, PA_ALIGN(sizeof(struct chunk)) + size); va_start(ap, format); r = vsnprintf(CHUNK_TO_TEXT(c), size, format, ap); CHUNK_TO_TEXT(c)[size-1] = 0; va_end(ap); if (r > -1 && (size_t) r < size) { c->length = (size_t) r; append(sb, c); return (size_t) r; } if (r > -1) /* glibc 2.1 */ size = (size_t) r+1; else /* glibc 2.0 */ size *= 2; } }
const uint8_t* pa_auth_cookie_read(pa_auth_cookie *c, size_t size) { pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); pa_assert(c->size == size); return (const uint8_t*) c + PA_ALIGN(sizeof(pa_auth_cookie)); }
pa_mempool* pa_mempool_new(int shared) { pa_mempool *p; p = pa_xnew(pa_mempool, 1); p->mutex = pa_mutex_new(TRUE, TRUE); 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; p->n_blocks = PA_MEMPOOL_SLOTS_MAX; pa_assert(p->block_size > PA_ALIGN(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; } 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); return p; }
/* No lock necessary. This function is not multiple caller safe! */ static void memblock_make_local(pa_memblock *b) { pa_assert(b); pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) { struct mempool_slot *slot; if ((slot = mempool_allocate_slot(b->pool))) { void *new_data; /* We can move it into a local pool, perfect! */ 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); b->type = PA_MEMBLOCK_POOL_EXTERNAL; b->read_only = 0; goto finish; } } /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */ b->per_type.user.free_cb = pa_xfree; 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; finish: pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); memblock_wait(b); }
pa_asyncq *pa_asyncq_new(unsigned size) { pa_asyncq *l; if (!size) size = ASYNCQ_SIZE; pa_assert(pa_is_power_of_two(size)); l = pa_xmalloc0(PA_ALIGN(sizeof(pa_asyncq)) + (sizeof(pa_atomic_ptr_t) * size)); l->size = size; PA_LLIST_HEAD_INIT(struct localq, l->localq); l->last_localq = NULL; l->waiting_for_post = FALSE; if (!(l->read_fdsem = pa_fdsem_new())) { pa_xfree(l); return NULL; } if (!(l->write_fdsem = pa_fdsem_new())) { pa_fdsem_free(l->read_fdsem); pa_xfree(l); return NULL; } return l; }
pa_srbchannel* pa_srbchannel_new(pa_mainloop_api *m, pa_mempool *p) { int capacity; int readfd; struct srbheader *srh; pa_srbchannel* sr = pa_xmalloc0(sizeof(pa_srbchannel)); sr->mainloop = m; sr->memblock = pa_memblock_new_pool(p, -1); if (!sr->memblock) goto fail; srh = pa_memblock_acquire(sr->memblock); pa_zero(*srh); sr->rb_read.memory = (uint8_t*) srh + PA_ALIGN(sizeof(*srh)); srh->readbuf_offset = sr->rb_read.memory - (uint8_t*) srh; capacity = (pa_memblock_get_length(sr->memblock) - srh->readbuf_offset) / 2; sr->rb_write.memory = PA_ALIGN_PTR(sr->rb_read.memory + capacity); srh->writebuf_offset = sr->rb_write.memory - (uint8_t*) srh; capacity = PA_MIN(capacity, srh->writebuf_offset - srh->readbuf_offset); pa_log_debug("SHM block is %d bytes, ringbuffer capacity is 2 * %d bytes", (int) pa_memblock_get_length(sr->memblock), capacity); srh->capacity = sr->rb_read.capacity = sr->rb_write.capacity = capacity; sr->rb_read.count = &srh->read_count; sr->rb_write.count = &srh->write_count; sr->sem_read = pa_fdsem_new_shm(&srh->read_semdata); if (!sr->sem_read) goto fail; sr->sem_write = pa_fdsem_new_shm(&srh->write_semdata); if (!sr->sem_write) goto fail; readfd = pa_fdsem_get(sr->sem_read); #ifdef DEBUG_SRBCHANNEL pa_log("Enabling io event on fd %d", readfd); #endif sr->read_event = m->io_new(m, readfd, PA_IO_EVENT_INPUT, semread_cb, sr); m->io_enable(sr->read_event, PA_IO_EVENT_INPUT); return sr; fail: pa_srbchannel_free(sr); return NULL; }
/* No lock necessary */ void pa_mempool_vacuum(pa_mempool *p) { struct mempool_slot *slot; pa_flist *list; pa_assert(p); list = pa_flist_new(p->n_blocks*2); 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 + PA_ALIGN(sizeof(struct mempool_slot)), p->block_size - PA_ALIGN(sizeof(struct mempool_slot))); while (pa_flist_push(p->free_slots, slot)) ; } pa_flist_free(list, NULL); }
pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) { pa_idxset *s; s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*)); s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func; s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func; s->current_index = 0; s->n_entries = 0; s->iterate_list_head = s->iterate_list_tail = NULL; return s; }
int pa_shm_attach(pa_shm *m, unsigned id, bool writable) { char fn[32]; int fd = -1; int prot; struct stat st; pa_assert(m); segment_name(fn, sizeof(fn), m->id = id); if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) { if (errno != EACCES && errno != ENOENT) pa_log("shm_open() failed: %s", pa_cstrerror(errno)); goto fail; } if (fstat(fd, &st) < 0) { pa_log("fstat() failed: %s", pa_cstrerror(errno)); goto fail; } if (st.st_size <= 0 || st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) || PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) { pa_log("Invalid shared memory segment size"); goto fail; } m->size = (size_t) st.st_size; prot = writable ? PROT_READ | PROT_WRITE : PROT_READ; if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } m->do_unlink = false; m->shared = true; pa_assert_se(pa_close(fd) == 0); return 0; fail: if (fd >= 0) pa_close(fd); return -1; }
/* Append up to l bytes of a string to the string buffer */ void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t l) { struct chunk *c; pa_assert(sb); pa_assert(t); if (!l) return; c = pa_xmalloc(PA_ALIGN(sizeof(struct chunk)) + l); c->length = l; memcpy(CHUNK_TO_TEXT(c), t, l); append(sb, c); }
/* No lock necessary */ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { pa_memblock *b; 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 = 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); pa_atomic_store(&b->please_signal, 0); stat_add(b); return b; }
int pa_shm_attach_ro(pa_shm *m, unsigned id) { char fn[32]; int fd = -1; struct stat st; pa_assert(m); segment_name(fn, sizeof(fn), m->id = id); if ((fd = shm_open(fn, O_RDONLY, 0)) < 0) { if (errno != EACCES) pa_log("shm_open() failed: %s", pa_cstrerror(errno)); goto fail; } if (fstat(fd, &st) < 0) { pa_log("fstat() failed: %s", pa_cstrerror(errno)); goto fail; } if (st.st_size <= 0 || st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) || PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) { pa_log("Invalid shared memory segment size"); goto fail; } m->size = (size_t) st.st_size; if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } m->do_unlink = FALSE; m->shared = TRUE; pa_assert_se(pa_close(fd) == 0); return 0; fail: if (fd >= 0) pa_close(fd); return -1; }
pa_flist *pa_flist_new(unsigned size) { pa_flist *l; if (!size) size = FLIST_SIZE; pa_assert(pa_is_power_of_two(size)); l = pa_xmalloc0(PA_ALIGN(sizeof(pa_flist)) + (sizeof(pa_atomic_ptr_t) * size)); l->size = size; pa_atomic_store(&l->read_idx, 0); pa_atomic_store(&l->write_idx, 0); pa_atomic_store(&l->length, 0); return l; }
pa_device_port *pa_device_port_new(pa_core *c, const char *name, const char *description, size_t extra) { pa_device_port *p; pa_assert(name); p = PA_DEVICE_PORT(pa_object_new_internal(PA_ALIGN(sizeof(pa_device_port)) + extra, pa_device_port_type_id, pa_device_port_check_type)); p->parent.free = device_port_free; p->name = pa_xstrdup(name); p->description = pa_xstrdup(description); p->core = c; p->priority = 0; p->available = PA_PORT_AVAILABLE_UNKNOWN; p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); p->is_input = FALSE; p->is_output = FALSE; p->latency_offset = 0; p->proplist = pa_proplist_new(); return p; }
static void *get_cell_data(pa_atomic_t *a) { return (uint8_t*) a + PA_ALIGN(sizeof(atomic_t)); }
static pa_atomic_t* get_cell(pa_shmasyncq *l, unsigned i) { pa_assert(i < l->data->n_elements); return (pa_atomic_t*) ((uint8*t) l->data + PA_ALIGN(sizeof(pa_shmasyncq_data)) + i * (PA_ALIGN(sizeof(pa_atomic_t)) + PA_ALIGN(element_size))) }
/* No lock necessary */ static void* mempool_slot_data(struct mempool_slot *slot) { pa_assert(slot); return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot)); }
static int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) { #if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) char fn[32]; int fd = -1; int prot; struct stat st; pa_assert(m); switch (type) { #ifdef HAVE_SHM_OPEN case PA_MEM_TYPE_SHARED_POSIX: pa_assert(memfd_fd == -1); segment_name(fn, sizeof(fn), id); if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) { if ((errno != EACCES && errno != ENOENT) || !for_cleanup) pa_log("shm_open() failed: %s", pa_cstrerror(errno)); goto fail; } break; #endif #ifdef HAVE_MEMFD case PA_MEM_TYPE_SHARED_MEMFD: pa_assert(memfd_fd != -1); fd = memfd_fd; break; #endif default: goto fail; } if (fstat(fd, &st) < 0) { pa_log("fstat() failed: %s", pa_cstrerror(errno)); goto fail; } if (st.st_size <= 0 || st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(type) || PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) { pa_log("Invalid shared memory segment size"); goto fail; } prot = writable ? PROT_READ | PROT_WRITE : PROT_READ; if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) { pa_log("mmap() failed: %s", pa_cstrerror(errno)); goto fail; } /* In case of attaching to memfd areas, _the caller_ maintains * ownership of the passed fd and has the sole responsibility * of closing it down.. For other types, we're the code path * which created the fd in the first place and we're thus the * ones responsible for closing it down */ if (type != PA_MEM_TYPE_SHARED_MEMFD) pa_assert_se(pa_close(fd) == 0); m->type = type; m->id = id; m->size = (size_t) st.st_size; m->do_unlink = false; m->fd = -1; return 0; fail: /* In case of memfds, caller maintains fd ownership */ if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD) pa_close(fd); #endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */ return -1; }
static inline size_t shm_marker_size(pa_mem_type_t type) { if (type == PA_MEM_TYPE_SHARED_POSIX) return PA_ALIGN(sizeof(struct shm_marker)); return 0; }
/* 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)); }