* Something was fishy with the previous diff_lock-based implementation;

while I couldn't put my finger on it, I rarely got crashes with it.
* I've greatly simplified that mechanism now, and got rid of the 
  diff_locks completely - at least I understand the code now :)
* Coding style cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24588 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-03-26 10:50:47 +00:00
parent fb84047190
commit 25ce50e1b9
@@ -1,27 +1,27 @@
/* /*
* Copyright 2008, Axel Dörfler, [email protected].
* Copyright 2002/03, Thomas Kurschel. All rights reserved. * Copyright 2002/03, Thomas Kurschel. All rights reserved.
*
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
/* /*! Deadlock-safe allocation of locked memory.
Deadlock-safe allocation of locked memory.
Allocation/freeing is optimized for speed. Count of <sem> Allocation/freeing is optimized for speed. Count of <sem>
is the number of free blocks and thus should be modified is the number of free blocks and thus should be modified
by each alloc() and free(). As this count is only crucial if by each alloc() and free(). As this count is only crucial if
an allocation is waiting for a free block, <sem> is only an allocation is waiting for a free block, <sem> is only
updated on demand - the correct number of free blocks is updated on demand - the correct number of free blocks is
stored in <free_blocks> and the difference between <free_blocks> stored in <free_blocks>. There are only three cases where
and the semaphore count is stored in <diff_locks>. There <sem> is updated:
are only three cases where <sem> is updated:
- if an allocation fails because there is no free block left; - if an allocation fails because there is no free block left;
in this case, <num_waiting> increased by one and then the in this case, <num_waiting> increased by one and then the
thread makes <sem> up-to-date and waits for a free block thread makes <sem> up-to-date and waits for a free block
via <sem> in one step; finally, <num_waiting> is decreased via <sem> in one step; finally, <num_waiting> is decreased
by one by one
- if a block is freed and <num_waiting> is non-zero; - if a block is freed and <num_waiting> is non-zero;
here, count of <sem> is updated to release threads waiting here, count of <sem> is updated to release threads waiting
for allocation for allocation
- if a new chunk of blocks is allocated; - if a new chunk of blocks is allocated;
same as previous case same as previous case
@@ -51,7 +51,6 @@ typedef struct locked_pool {
benaphore mutex; // to be used whenever some variable of the first benaphore mutex; // to be used whenever some variable of the first
// block of this structure is read or modified // block of this structure is read or modified
int free_blocks; // # free blocks int free_blocks; // # free blocks
int diff_locks; // # how often <sem> must be acquired
int num_waiting; // # waiting allocations int num_waiting; // # waiting allocations
void *free_list; // list of free blocks void *free_list; // list of free blocks
int next_ofs; // offset of next-pointer in block int next_ofs; // offset of next-pointer in block
@@ -77,265 +76,141 @@ typedef struct locked_pool {
// header of memory chunk // header of memory chunk
typedef struct chunk_header { typedef struct chunk_header {
struct chunk_header *next; // free-list struct chunk_header *next; // free-list
area_id region; // underlying region area_id area; // underlying area
int num_blocks; // size in blocks int num_blocks; // size in blocks
} chunk_header; } chunk_header;
// global list of pools // global list of pools
static locked_pool *locked_pools; static locked_pool *sLockedPools;
// benaphore to protect locked_pools // benaphore to protect sLockedPools
static benaphore locked_pool_ben; static benaphore sLockedPoolsLock;
// true, if thread should shut down // true, if thread should shut down
static bool locked_pool_shutting_down; static bool sShuttingDown;
// background thread to enlarge pools // background thread to enlarge pools
static thread_id locked_pool_enlarger_thread; static thread_id sEnlargerThread;
// semaphore to wake up enlarger thread // semaphore to wake up enlarger thread
static sem_id locked_pool_enlarger_sem; static sem_id sEnlargerSemaphore;
// macro to access next-pointer in free block // macro to access next-pointer in free block
#define NEXT_PTR(pool, a) ((void **)(((char *)a) + pool->next_ofs)) #define NEXT_PTR(pool, a) ((void **)(((char *)a) + pool->next_ofs))
/** public: alloc memory from pool */ /*! Enlarge memory pool by <num_block> blocks */
static void *
pool_alloc(locked_pool *pool)
{
void *block;
int num_to_lock;
TRACE(("pool_alloc()\n"));
benaphore_lock(&pool->mutex);
--pool->free_blocks;
if (pool->free_blocks > 0) {
// there are free blocks - grab one
// increase difference between allocations executed and those
// reflected in pool semaphore
++pool->diff_locks;
TRACE(("freeblocks=%d, diff_locks=%d, free_list=%p\n",
pool->free_blocks, pool->diff_locks, pool->free_list));
block = pool->free_list;
pool->free_list = *NEXT_PTR(pool, block);
TRACE(("new free_list=%p\n", pool->free_list));
benaphore_unlock(&pool->mutex);
return block;
}
// entire pool is in use
// we should do a ++free_blocks here, but this can lead to race
// condition: when we wait for <sem> and a block gets released
// and another thread calls alloc() before we grab the freshly freed
// block, the other thread could overtake us and grab the free block
// instead! by leaving free_block at a negative value, the other
// thread cannot see the free block and thus will leave it for us
// tell them we are waiting on semaphore
++pool->num_waiting;
// make pool semaphore up-to-date;
// add one as we want to allocate a block
num_to_lock = pool->diff_locks+1;
pool->diff_locks = 0;
TRACE(("locking %d times (%d waiting allocs)\n", num_to_lock, pool->num_waiting));
benaphore_unlock(&pool->mutex);
// awake background thread
release_sem_etc(locked_pool_enlarger_sem, 1, B_DO_NOT_RESCHEDULE);
// make samphore up-to-date and wait until a block is available
acquire_sem_etc(pool->sem, num_to_lock, 0, 0);
benaphore_lock(&pool->mutex);
// tell them that we don't wait on semaphore anymore
--pool->num_waiting;
TRACE(("continuing alloc (%d free blocks)\n", pool->free_blocks));
block = pool->free_list;
pool->free_list = *NEXT_PTR(pool, block);
benaphore_unlock(&pool->mutex);
return block;
}
/** public: free block */
static void
pool_free(locked_pool *pool, void *block)
{
int num_to_unlock;
TRACE(("pool_free()\n"));
benaphore_lock(&pool->mutex);
// add to free list
*NEXT_PTR(pool, block) = pool->free_list;
pool->free_list = block;
++pool->free_blocks;
// increase difference between allocation executed and those
// reflected in pool semaphore
--pool->diff_locks;
TRACE(("freeblocks=%d, diff_locks=%d, free_list=%p\n",
pool->free_blocks, pool->diff_locks, pool->free_list));
if (pool->num_waiting == 0) {
// if noone is waiting, this is it
//SHOW_FLOW0( 3, "leaving" );
benaphore_unlock(&pool->mutex);
//SHOW_FLOW0( 3, "done" );
return;
}
// someone is waiting on semaphore, so we have to make it up-to-date
num_to_unlock = -pool->diff_locks;
pool->diff_locks = 0;
TRACE(("unlocking %d times (%d waiting allocs)\n",
num_to_unlock, pool->num_waiting));
benaphore_unlock(&pool->mutex);
// now it is up-to-date and waiting allocations can be continued
release_sem_etc(pool->sem, num_to_unlock, 0);
return;
}
/** enlarge memory pool by <num_block> blocks */
static status_t static status_t
enlarge_pool(locked_pool *pool, int num_blocks) enlarge_pool(locked_pool *pool, int numBlocks)
{ {
void **next; void **next;
int i; int i;
status_t res; int numWaiting;
area_id region; status_t status;
area_id area;
chunk_header *chunk; chunk_header *chunk;
size_t chunk_size; size_t chunkSize;
int num_to_unlock; void *block, *lastBlock;
void *block, *last_block;
TRACE(("enlarge_pool()\n")); TRACE(("enlarge_pool()\n"));
// get memory directly from VM; we don't let user code access memory // get memory directly from VM; we don't let user code access memory
chunk_size = num_blocks * pool->block_size + pool->header_size; chunkSize = numBlocks * pool->block_size + pool->header_size;
chunk_size = (chunk_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); chunkSize = (chunkSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
res = region = create_area(pool->name, status = area = create_area(pool->name,
(void **)&chunk, B_ANY_KERNEL_ADDRESS, chunk_size, (void **)&chunk, B_ANY_KERNEL_ADDRESS, chunkSize,
pool->lock_flags, 0); pool->lock_flags, 0);
if (res < 0) { if (status < B_OK) {
dprintf("cannot enlarge pool (%s)\n", strerror(res)); dprintf("cannot enlarge pool (%s)\n", strerror(status));
return res; // TODO: we should wait a bit and try again!
return status;
} }
chunk->region = region; chunk->area = area;
chunk->num_blocks = num_blocks; chunk->num_blocks = numBlocks;
// create free_list and call add-hook // create free_list and call add-hook
// very important: we first create a freelist within the chunk, // very important: we first create a freelist within the chunk,
// going from lower to higher addresses; at the end of the loop, // going from lower to higher addresses; at the end of the loop,
// "next" points to the head of the list and "last_block" to the // "next" points to the head of the list and "lastBlock" to the
// last list node! // last list node!
next = NULL; next = NULL;
last_block = (char *)chunk + pool->header_size + lastBlock = (char *)chunk + pool->header_size +
(num_blocks-1) * pool->block_size; (numBlocks-1) * pool->block_size;
for (i = 0, block = last_block; i < num_blocks; for (i = 0, block = lastBlock; i < numBlocks;
++i, block = (char *)block - pool->block_size) ++i, block = (char *)block - pool->block_size)
{ {
if (pool->add_hook) { if (pool->add_hook) {
if ((res = pool->add_hook(block, pool->hook_arg)) < 0) if ((status = pool->add_hook(block, pool->hook_arg)) < B_OK)
break; break;
} }
*NEXT_PTR(pool, block) = next; *NEXT_PTR(pool, block) = next;
next = block; next = block;
} }
if (i < num_blocks) { if (i < numBlocks) {
// ups - pre-init failed somehow // ups - pre-init failed somehow
// call remove-hook for blocks that we called add-hook for // call remove-hook for blocks that we called add-hook for
int j; int j;
for (block = last_block, j = 0; j < i; ++j, block = (char *)block - pool->block_size) { for (block = lastBlock, j = 0; j < i; ++j,
block = (char *)block - pool->block_size) {
if (pool->remove_hook) if (pool->remove_hook)
pool->remove_hook(block, pool->hook_arg); pool->remove_hook(block, pool->hook_arg);
} }
// destroy area and give up // destroy area and give up
delete_area(chunk->region); delete_area(chunk->area);
return res; return status;
} }
// add new blocks to pool // add new blocks to pool
benaphore_lock(&pool->mutex); benaphore_lock(&pool->mutex);
// see remarks about initialising list within chunk // see remarks about initialising list within chunk
*NEXT_PTR(pool, last_block) = pool->free_list; *NEXT_PTR(pool, lastBlock) = pool->free_list;
pool->free_list = next; pool->free_list = next;
chunk->next = pool->chunks; chunk->next = pool->chunks;
pool->chunks = chunk; pool->chunks = chunk;
pool->num_blocks += num_blocks; pool->num_blocks += numBlocks;
pool->free_blocks += numBlocks;
pool->free_blocks += num_blocks; TRACE(("done - num_blocks=%d, free_blocks=%d, num_waiting=%d\n",
pool->diff_locks -= num_blocks; pool->num_blocks, pool->free_blocks, pool->num_waiting));
// update sem instantly (we could check waiting flag whether updating numWaiting = min_c(pool->num_waiting, numBlocks);
// is really necessary, but we don't go for speed here) pool->num_waiting -= numWaiting;
num_to_unlock = -pool->diff_locks;
pool->diff_locks = 0;
TRACE(("done - num_blocks=%d, free_blocks=%d, num_waiting=%d, num_to_unlock=%d\n",
pool->num_blocks, pool->free_blocks, pool->num_waiting, num_to_unlock));
benaphore_unlock(&pool->mutex); benaphore_unlock(&pool->mutex);
// bring sem up-to-date, releasing threads that wait for empty blocks // release threads that wait for empty blocks
release_sem_etc(pool->sem, num_to_unlock, 0); release_sem_etc(pool->sem, numWaiting, 0);
return B_OK; return B_OK;
} }
/** background thread that adjusts pool size */ /*! Background thread that adjusts pool size */
static int32 static int32
enlarger_threadproc(void *arg) enlarger_thread(void *arg)
{ {
while (1) { while (1) {
locked_pool *pool; locked_pool *pool;
acquire_sem(locked_pool_enlarger_sem); acquire_sem(sEnlargerSemaphore);
if (locked_pool_shutting_down) if (sShuttingDown)
break; break;
// protect traversing of global list and // protect traversing of global list and
// block destroy_pool() to not clean up a pool we are enlarging // block destroy_pool() to not clean up a pool we are enlarging
benaphore_lock(&locked_pool_ben); benaphore_lock(&sLockedPoolsLock);
for (pool = locked_pools; pool; pool = pool->next) { for (pool = sLockedPools; pool; pool = pool->next) {
int num_free; int num_free;
// this mutex is probably not necessary (at least on 80x86) // this mutex is probably not necessary (at least on 80x86)
@@ -353,20 +228,19 @@ enlarger_threadproc(void *arg)
// never create more blocks then defined - the caller may have // never create more blocks then defined - the caller may have
// a good reason for choosing the limit // a good reason for choosing the limit
if (pool->num_blocks < pool->max_blocks) { if (pool->num_blocks < pool->max_blocks) {
enlarge_pool(pool, enlarge_pool(pool,
min(pool->enlarge_by, pool->max_blocks - pool->num_blocks)); min(pool->enlarge_by, pool->max_blocks - pool->num_blocks));
} }
} }
benaphore_unlock(&locked_pool_ben); benaphore_unlock(&sLockedPoolsLock);
} }
return 0; return 0;
} }
/** free all chunks belonging to pool */ /*! Free all chunks belonging to pool */
static void static void
free_chunks(locked_pool *pool) free_chunks(locked_pool *pool)
{ {
@@ -374,37 +248,182 @@ free_chunks(locked_pool *pool)
for (chunk = pool->chunks; chunk; chunk = next) { for (chunk = pool->chunks; chunk; chunk = next) {
int i; int i;
void *block, *last_block; void *block, *lastBlock;
next = chunk->next; next = chunk->next;
last_block = (char *)chunk + pool->header_size + lastBlock = (char *)chunk + pool->header_size +
(chunk->num_blocks-1) * pool->block_size; (chunk->num_blocks-1) * pool->block_size;
// don't forget to call remove-hook // don't forget to call remove-hook
for (i = 0, block = last_block; i < pool->num_blocks; ++i, block = (char *)block - pool->block_size) { for (i = 0, block = lastBlock; i < pool->num_blocks; ++i, block = (char *)block - pool->block_size) {
if (pool->remove_hook) if (pool->remove_hook)
pool->remove_hook(block, pool->hook_arg); pool->remove_hook(block, pool->hook_arg);
} }
delete_area(chunk->region); delete_area(chunk->area);
} }
pool->chunks = NULL; pool->chunks = NULL;
} }
/** public: create pool */ /*! Global init, executed when module is loaded */
static status_t
init_locked_pool(void)
{
status_t status = benaphore_init(&sLockedPoolsLock,
"locked_pool_global_list");
if (status < B_OK)
goto err;
status = sEnlargerSemaphore = create_sem(0, "locked_pool_enlarger");
if (status < B_OK)
goto err2;
sLockedPools = NULL;
sShuttingDown = false;
status = sEnlargerThread = spawn_kernel_thread(enlarger_thread,
"locked_pool_enlarger", B_NORMAL_PRIORITY, NULL);
if (status < B_OK)
goto err3;
resume_thread(sEnlargerThread);
return B_OK;
err3:
delete_sem(sEnlargerSemaphore);
err2:
benaphore_destroy(&sLockedPoolsLock);
err:
return status;
}
/*! Global uninit, executed before module is unloaded */
static status_t
uninit_locked_pool(void)
{
sShuttingDown = true;
release_sem(sEnlargerSemaphore);
wait_for_thread(sEnlargerThread, NULL);
delete_sem(sEnlargerSemaphore);
benaphore_destroy(&sLockedPoolsLock);
return B_OK;
}
// #pragma mark - Module API
/*! Alloc memory from pool */
static void *
pool_alloc(locked_pool *pool)
{
void *block;
TRACE(("pool_alloc()\n"));
benaphore_lock(&pool->mutex);
--pool->free_blocks;
if (pool->free_blocks > 0) {
// there are free blocks - grab one
TRACE(("freeblocks=%d, free_list=%p\n",
pool->free_blocks, pool->free_list));
block = pool->free_list;
pool->free_list = *NEXT_PTR(pool, block);
TRACE(("new free_list=%p\n", pool->free_list));
benaphore_unlock(&pool->mutex);
return block;
}
// entire pool is in use
// we should do a ++free_blocks here, but this can lead to race
// condition: when we wait for <sem> and a block gets released
// and another thread calls alloc() before we grab the freshly freed
// block, the other thread could overtake us and grab the free block
// instead! by leaving free_block at a negative value, the other
// thread cannot see the free block and thus will leave it for us
// tell them we are waiting on semaphore
++pool->num_waiting;
TRACE(("%d waiting allocs\n", pool->num_waiting));
benaphore_unlock(&pool->mutex);
// awake background thread
release_sem_etc(sEnlargerSemaphore, 1, B_DO_NOT_RESCHEDULE);
// make samphore up-to-date and wait until a block is available
acquire_sem(pool->sem);
benaphore_lock(&pool->mutex);
TRACE(("continuing alloc (%d free blocks)\n", pool->free_blocks));
block = pool->free_list;
pool->free_list = *NEXT_PTR(pool, block);
benaphore_unlock(&pool->mutex);
return block;
}
static void
pool_free(locked_pool *pool, void *block)
{
TRACE(("pool_free()\n"));
benaphore_lock(&pool->mutex);
// add to free list
*NEXT_PTR(pool, block) = pool->free_list;
pool->free_list = block;
++pool->free_blocks;
TRACE(("freeblocks=%d, free_list=%p\n", pool->free_blocks,
pool->free_list));
if (pool->num_waiting == 0) {
// if no one is waiting, this is it
benaphore_unlock(&pool->mutex);
return;
}
// someone is waiting on the semaphore
TRACE(("%d waiting allocs\n", pool->num_waiting));
pool->num_waiting--;
benaphore_unlock(&pool->mutex);
// now it is up-to-date and waiting allocations can be continued
release_sem(pool->sem);
return;
}
static locked_pool * static locked_pool *
create_pool(int block_size, int alignment, int next_ofs, create_pool(int block_size, int alignment, int next_ofs,
int chunk_size, int max_blocks, int min_free_blocks, int chunkSize, int max_blocks, int min_free_blocks,
const char *name, uint32 lock_flags, const char *name, uint32 lock_flags,
locked_pool_add_hook add_hook, locked_pool_add_hook add_hook,
locked_pool_remove_hook remove_hook, void *hook_arg) locked_pool_remove_hook remove_hook, void *hook_arg)
{ {
locked_pool *pool; locked_pool *pool;
status_t res; status_t status;
TRACE(("create_pool()\n")); TRACE(("create_pool()\n"));
@@ -414,36 +433,35 @@ create_pool(int block_size, int alignment, int next_ofs,
memset(pool, sizeof(*pool), 0); memset(pool, sizeof(*pool), 0);
if ((res = benaphore_init(&pool->mutex, "locked_pool")) < 0) if ((status = benaphore_init(&pool->mutex, "locked_pool")) < 0)
goto err; goto err;
if ((res = pool->sem = create_sem(0, "locked_pool")) < 0) if ((status = pool->sem = create_sem(0, "locked_pool")) < 0)
goto err1; goto err1;
if ((pool->name = strdup(name)) == NULL) { if ((pool->name = strdup(name)) == NULL) {
res = B_NO_MEMORY; status = B_NO_MEMORY;
goto err3; goto err3;
} }
pool->alignment = alignment; pool->alignment = alignment;
// take care that there is always enough space to fulfill alignment // take care that there is always enough space to fulfill alignment
pool->block_size = (block_size + pool->alignment) & ~pool->alignment; pool->block_size = (block_size + pool->alignment) & ~pool->alignment;
pool->next_ofs = next_ofs; pool->next_ofs = next_ofs;
pool->lock_flags = lock_flags; pool->lock_flags = lock_flags;
pool->header_size = max((sizeof( chunk_header ) + pool->alignment) & ~pool->alignment, pool->header_size = max((sizeof( chunk_header ) + pool->alignment) & ~pool->alignment,
pool->alignment + 1); pool->alignment + 1);
pool->enlarge_by = (((chunk_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1)) - pool->header_size) pool->enlarge_by = (((chunkSize + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1)) - pool->header_size)
/ pool->block_size; / pool->block_size;
pool->max_blocks = max_blocks; pool->max_blocks = max_blocks;
pool->min_free_blocks = min_free_blocks; pool->min_free_blocks = min_free_blocks;
pool->free_blocks = 0; pool->free_blocks = 0;
pool->num_blocks = 0; pool->num_blocks = 0;
pool->diff_locks = 0;
pool->num_waiting = 0; pool->num_waiting = 0;
pool->free_list = NULL; pool->free_list = NULL;
pool->add_hook = add_hook; pool->add_hook = add_hook;
@@ -457,14 +475,14 @@ create_pool(int block_size, int alignment, int next_ofs,
// if there is a minimum size, enlarge pool right now // if there is a minimum size, enlarge pool right now
if (min_free_blocks > 0) { if (min_free_blocks > 0) {
if ((res = enlarge_pool(pool, min(pool->enlarge_by, pool->max_blocks))) < 0) if ((status = enlarge_pool(pool, min(pool->enlarge_by, pool->max_blocks))) < 0)
goto err4; goto err4;
} }
// add to global list, so enlarger thread takes care of pool // add to global list, so enlarger thread takes care of pool
benaphore_lock(&locked_pool_ben); benaphore_lock(&sLockedPoolsLock);
ADD_DL_LIST_HEAD(pool, locked_pools, ); ADD_DL_LIST_HEAD(pool, sLockedPools, );
benaphore_unlock(&locked_pool_ben); benaphore_unlock(&sLockedPoolsLock);
return pool; return pool;
@@ -480,8 +498,6 @@ err:
} }
/** public: destroy pool */
static void static void
destroy_pool(locked_pool *pool) destroy_pool(locked_pool *pool)
{ {
@@ -489,11 +505,11 @@ destroy_pool(locked_pool *pool)
// first, remove from global list, so enlarger thread // first, remove from global list, so enlarger thread
// won't touch this pool anymore // won't touch this pool anymore
benaphore_lock(&locked_pool_ben); benaphore_lock(&sLockedPoolsLock);
REMOVE_DL_LIST(pool, locked_pools, ); REMOVE_DL_LIST(pool, sLockedPools, );
benaphore_unlock(&locked_pool_ben); benaphore_unlock(&sLockedPoolsLock);
// then cleanup pool // then cleanup pool
free_chunks(pool); free_chunks(pool);
free(pool->name); free(pool->name);
@@ -504,58 +520,6 @@ destroy_pool(locked_pool *pool)
} }
/** global init, executed when module is loaded */
static status_t
init_locked_pool(void)
{
status_t res;
if ((res = benaphore_init(&locked_pool_ben, "locked_pool_global_list")) < B_OK)
goto err;
if ((res = locked_pool_enlarger_sem = create_sem(0, "locked_pool_enlarger")) < B_OK)
goto err2;
locked_pools = NULL;
locked_pool_shutting_down = false;
if ((res = locked_pool_enlarger_thread = spawn_kernel_thread(enlarger_threadproc,
"locked_pool_enlarger", B_NORMAL_PRIORITY, NULL)) < B_OK)
goto err3;
resume_thread(locked_pool_enlarger_thread);
return B_OK;
err3:
delete_sem(locked_pool_enlarger_sem);
err2:
benaphore_destroy(&locked_pool_ben);
err:
return res;
}
/** global uninit, executed before module is unloaded */
static status_t
uninit_locked_pool(void)
{
int32 retcode;
locked_pool_shutting_down = true;
release_sem(locked_pool_enlarger_sem);
wait_for_thread(locked_pool_enlarger_thread, &retcode);
delete_sem(locked_pool_enlarger_sem);
benaphore_destroy(&locked_pool_ben);
return B_OK;
}
static status_t static status_t
std_ops(int32 op, ...) std_ops(int32 op, ...)
{ {
@@ -578,16 +542,15 @@ locked_pool_interface interface = {
std_ops std_ops
}, },
pool_alloc, pool_alloc,
pool_free, pool_free,
create_pool, create_pool,
destroy_pool destroy_pool
}; };
#if !_BUILDING_kernel && !BOOT
module_info *modules[] = { module_info *modules[] = {
&interface.minfo, &interface.minfo,
NULL NULL
}; };
#endif