/* -*- Mode: C ; c-basic-offset: 2 -*- */ /***************************************************************************** * * This file is part of zynjacku * * Copyright (C) 2006,2007,2008,2009 Nedko Arnaudov * * 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 #include #include #include /* sprintf */ #include #include #include #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; }