|
- /* -*- Mode: C ; c-basic-offset: 2 -*- */
- /*****************************************************************************
- *
- * This file is part of zynjacku
- *
- * Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov <nedko@arnaudov.name>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *****************************************************************************/
-
- #include <stddef.h>
- #include <stdbool.h>
- #include <string.h>
- #include <stdio.h> /* sprintf */
- #include <stdlib.h>
- #include <assert.h>
- #include <pthread.h>
- #include "lv2/lv2_rtmempool.h"
-
- #include "rtmempool.h"
-
- #include "list.h"
- //#define LOG_LEVEL LOG_LEVEL_DEBUG
- #include "log.h"
-
- struct rtsafe_memory_pool
- {
- char name[LV2_RTSAFE_MEMORY_POOL_NAME_MAX];
- size_t data_size;
- size_t min_preallocated;
- size_t max_preallocated;
-
- unsigned int used_count;
- struct list_head unused;
- struct list_head used;
- unsigned int unused_count;
-
- bool enforce_thread_safety;
- /* next members are initialized/used only if enforce_thread_safety is true */
- pthread_mutex_t mutex;
- unsigned int unused_count2;
- struct list_head pending;
-
- size_t used_size;
- };
-
- static
- void
- rtsafe_memory_pool_sleepy(
- lv2_rtsafe_memory_pool_handle pool_handle);
-
- static
- bool
- rtsafe_memory_pool_create(
- const char * pool_name,
- size_t data_size,
- size_t min_preallocated,
- size_t max_preallocated,
- bool enforce_thread_safety,
- lv2_rtsafe_memory_pool_handle * pool_handle_ptr)
- {
- int ret;
- struct rtsafe_memory_pool * pool_ptr;
-
- assert(min_preallocated <= max_preallocated);
-
- assert(pool_name == NULL || strlen(pool_name) < LV2_RTSAFE_MEMORY_POOL_NAME_MAX);
-
- LOG_DEBUG(
- "creating pool \"%s\" (size %u, min = %u, max = %u, enforce = %s)",
- pool_name,
- (unsigned int)data_size,
- (unsigned int)min_preallocated,
- (unsigned int)max_preallocated,
- enforce_thread_safety ? "true" : "false");
-
- pool_ptr = malloc(sizeof(struct rtsafe_memory_pool));
- if (pool_ptr == NULL)
- {
- return false;
- }
-
- if (pool_name != NULL)
- {
- strcpy(pool_ptr->name, pool_name);
- }
- else
- {
- sprintf(pool_ptr->name, "%p", pool_ptr);
- }
-
- pool_ptr->data_size = data_size;
- pool_ptr->min_preallocated = min_preallocated;
- pool_ptr->max_preallocated = max_preallocated;
-
- INIT_LIST_HEAD(&pool_ptr->used);
- pool_ptr->used_count = 0;
-
- INIT_LIST_HEAD(&pool_ptr->unused);
- pool_ptr->unused_count = 0;
-
- pool_ptr->enforce_thread_safety = enforce_thread_safety;
- if (enforce_thread_safety)
- {
- ret = pthread_mutex_init(&pool_ptr->mutex, NULL);
- if (ret != 0)
- {
- free(pool_ptr);
- return false;
- }
-
- INIT_LIST_HEAD(&pool_ptr->pending);
- pool_ptr->unused_count2 = 0;
- }
-
- pool_ptr->used_size = 0;
-
- rtsafe_memory_pool_sleepy((lv2_rtsafe_memory_pool_handle)pool_ptr);
- *pool_handle_ptr = (lv2_rtsafe_memory_pool_handle)pool_ptr;
-
- return true;
- }
-
- #define pool_ptr ((struct rtsafe_memory_pool *)pool_handle)
-
- static
- void
- rtsafe_memory_pool_destroy(
- lv2_rtsafe_memory_pool_handle pool_handle)
- {
- int ret;
- struct list_head * node_ptr;
-
- LOG_DEBUG("destroying pool \"%s\"", pool_ptr->name);
-
- /* caller should deallocate all chunks prior releasing pool itself */
- if (pool_ptr->used_count != 0)
- {
- LOG_WARNING("Deallocating non-empty pool \"%s\", leaking %u entries:", pool_ptr->name, pool_ptr->used_count);
-
- list_for_each(node_ptr, &pool_ptr->used)
- {
- LOG_WARNING(" %p", node_ptr + 1);
- }
-
- assert(0);
- }
-
- while (pool_ptr->unused_count != 0)
- {
- assert(!list_empty(&pool_ptr->unused));
-
- node_ptr = pool_ptr->unused.next;
-
- list_del(node_ptr);
- pool_ptr->unused_count--;
-
- free(node_ptr);
- }
-
- assert(list_empty(&pool_ptr->unused));
-
- if (pool_ptr->enforce_thread_safety)
- {
- while (!list_empty(&pool_ptr->pending))
- {
- node_ptr = pool_ptr->pending.next;
-
- list_del(node_ptr);
-
- free(node_ptr);
- }
-
- ret = pthread_mutex_destroy(&pool_ptr->mutex);
- assert(ret == 0);
- }
-
- free(pool_ptr);
-
- // unused variable
- (void)ret;
- }
-
- /* adjust unused list size */
- static
- void
- rtsafe_memory_pool_sleepy(
- lv2_rtsafe_memory_pool_handle pool_handle)
- {
- struct list_head * node_ptr;
- unsigned int count;
-
- LOG_DEBUG("pool \"%s\", sleepy", pool_ptr->name);
-
- if (pool_ptr->enforce_thread_safety)
- {
- pthread_mutex_lock(&pool_ptr->mutex);
-
- count = pool_ptr->unused_count2;
-
- assert(pool_ptr->min_preallocated < pool_ptr->max_preallocated);
-
- while (count < pool_ptr->min_preallocated)
- {
- node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size);
- if (node_ptr == NULL)
- {
- LOG_DEBUG("malloc() failed (%u)", (unsigned int)pool_ptr->used_size);
- break;
- }
-
- list_add_tail(node_ptr, &pool_ptr->pending);
-
- count++;
-
- pool_ptr->used_size += pool_ptr->data_size;
- }
-
- while (count > pool_ptr->max_preallocated && !list_empty(&pool_ptr->pending))
- {
- node_ptr = pool_ptr->pending.next;
-
- list_del(node_ptr);
-
- free(node_ptr);
-
- count--;
-
- pool_ptr->used_size -= pool_ptr->data_size;
- }
-
- pthread_mutex_unlock(&pool_ptr->mutex);
- }
- else
- {
- while (pool_ptr->unused_count < pool_ptr->min_preallocated)
- {
- node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size);
- if (node_ptr == NULL)
- {
- LOG_DEBUG("malloc() failed (%u)", (unsigned int)pool_ptr->used_size);
- return;
- }
-
- list_add_tail(node_ptr, &pool_ptr->unused);
- pool_ptr->unused_count++;
- pool_ptr->used_size += pool_ptr->data_size;
- }
-
- while (pool_ptr->unused_count > pool_ptr->max_preallocated)
- {
- assert(!list_empty(&pool_ptr->unused));
-
- node_ptr = pool_ptr->unused.next;
-
- list_del(node_ptr);
- pool_ptr->unused_count--;
-
- free(node_ptr);
- pool_ptr->used_size -= pool_ptr->data_size;
- }
- }
- }
-
- /* find entry in unused list, fail if it is empty */
- static
- void *
- rtsafe_memory_pool_allocate_atomic(
- lv2_rtsafe_memory_pool_handle pool_handle)
- {
- struct list_head * node_ptr;
-
- LOG_DEBUG("pool \"%s\", allocate (%u, %u)", pool_ptr->name, pool_ptr->used_count, pool_ptr->unused_count);
-
- if (list_empty(&pool_ptr->unused))
- {
- return NULL;
- }
-
- node_ptr = pool_ptr->unused.next;
- list_del(node_ptr);
- pool_ptr->unused_count--;
- pool_ptr->used_count++;
- list_add_tail(node_ptr, &pool_ptr->used);
-
- if (pool_ptr->enforce_thread_safety &&
- pthread_mutex_trylock(&pool_ptr->mutex) == 0)
- {
- while (pool_ptr->unused_count < pool_ptr->min_preallocated && !list_empty(&pool_ptr->pending))
- {
- node_ptr = pool_ptr->pending.next;
-
- list_del(node_ptr);
- list_add_tail(node_ptr, &pool_ptr->unused);
- pool_ptr->unused_count++;
- }
-
- pool_ptr->unused_count2 = pool_ptr->unused_count;
-
- pthread_mutex_unlock(&pool_ptr->mutex);
- }
-
- LOG_DEBUG("pool \"%s\", allocated %p (%u)", pool_ptr->name, node_ptr + 1, pool_ptr->used_count);
- return (node_ptr + 1);
- }
-
- /* move from used to unused list */
- static
- void
- rtsafe_memory_pool_deallocate(
- lv2_rtsafe_memory_pool_handle pool_handle,
- void * data)
- {
- struct list_head * node_ptr;
-
- LOG_DEBUG("pool \"%s\", deallocate %p (%u)", pool_ptr->name, (struct list_head *)data - 1, pool_ptr->used_count);
-
- list_del((struct list_head *)data - 1);
- list_add_tail((struct list_head *)data - 1, &pool_ptr->unused);
- pool_ptr->used_count--;
- pool_ptr->unused_count++;
-
- if (pool_ptr->enforce_thread_safety &&
- pthread_mutex_trylock(&pool_ptr->mutex) == 0)
- {
- while (pool_ptr->unused_count > pool_ptr->max_preallocated)
- {
- assert(!list_empty(&pool_ptr->unused));
-
- node_ptr = pool_ptr->unused.next;
-
- list_del(node_ptr);
- list_add_tail(node_ptr, &pool_ptr->pending);
- pool_ptr->unused_count--;
- }
-
- pool_ptr->unused_count2 = pool_ptr->unused_count;
-
- pthread_mutex_unlock(&pool_ptr->mutex);
- }
- }
-
- static
- void *
- rtsafe_memory_pool_allocate_sleepy(
- lv2_rtsafe_memory_pool_handle pool_handle)
- {
- void * data;
-
- LOG_DEBUG("pool \"%s\", allocate sleepy", pool_ptr->name);
-
- do
- {
- rtsafe_memory_pool_sleepy(pool_handle);
- data = rtsafe_memory_pool_allocate_atomic(pool_handle);
- }
- while (data == NULL);
-
- return data;
- }
-
- #undef pool_ptr
-
- static
- unsigned char
- rtsafe_memory_pool_create2(
- const char * pool_name,
- size_t data_size,
- size_t min_preallocated,
- size_t max_preallocated,
- lv2_rtsafe_memory_pool_handle * pool_ptr)
- {
- return rtsafe_memory_pool_create(pool_name, data_size, min_preallocated, max_preallocated, false, pool_ptr);
- }
-
- void
- rtmempool_allocator_init(
- lv2_rtsafe_memory_pool_provider * allocator_ptr)
- {
- allocator_ptr->create = rtsafe_memory_pool_create2;
- allocator_ptr->destroy = rtsafe_memory_pool_destroy;
- allocator_ptr->allocate_atomic = rtsafe_memory_pool_allocate_atomic;
- allocator_ptr->allocate_sleepy = rtsafe_memory_pool_allocate_sleepy;
- allocator_ptr->deallocate = rtsafe_memory_pool_deallocate;
- }
|