diff --git a/library/stdlib_slab.c b/library/stdlib_slab.c old mode 100644 new mode 100755 index c2a3f04..b78ff1c --- a/library/stdlib_slab.c +++ b/library/stdlib_slab.c @@ -47,7 +47,7 @@ struct SlabData NOCOMMON __slab_data; /****************************************************************************/ -/* Size of a single slab chunk, with padding to make it a multiple of 8. */ +/* Header for a single slab chunk, with padding to make it a multiple of 8. */ struct SlabChunk { struct SlabNode * sc_ParentSlab; @@ -56,35 +56,70 @@ struct SlabChunk /****************************************************************************/ +/* Get the size of a slab entry, including management information + * and padding, in addition to the payload size. + */ +static ULONG +get_slab_entry_size(ULONG payload_size) +{ + ULONG slab_entry_size; + ULONG result = 0; + ULONG padding; + + /* Add room for a pointer back to the parent slab + * which the chunk belongs to. + */ + slab_entry_size = sizeof(struct SlabChunk) + payload_size; + + /* Pad the allocation size to a multiple of 8 bytes. */ + if ((slab_entry_size % MEM_BLOCKSIZE) > 0) + padding = MEM_BLOCKSIZE - (slab_entry_size % MEM_BLOCKSIZE); + else + padding = 0; + + /* Check if the sums will cause an integer overflow. */ + if (__addition_overflows(sizeof(struct SlabChunk), payload_size) || + __addition_overflows(slab_entry_size, padding)) + { + goto out; + } + + /* Looking good so far. */ + result = slab_entry_size + padding; + + out: + + return result; +} + +/****************************************************************************/ + void * __slab_allocate(size_t allocation_size) { struct SlabChunk * chunk; void * allocation = NULL; - size_t allocation_size_with_chunk_header; - BOOL overflow = FALSE; + ULONG slab_entry_size; ULONG padding; D(("allocating %lu bytes of memory", allocation_size)); assert( __slab_data.sd_StandardSlabSize > 0 ); - /* Check for integer overflow. */ - if (__addition_overflows(sizeof(*chunk), allocation_size)) + slab_entry_size = get_slab_entry_size((ULONG)allocation_size); + if (slab_entry_size == 0) { SHOWMSG("integer overflow"); return NULL; } - allocation_size_with_chunk_header = sizeof(*chunk) + allocation_size; - /* Number of bytes to allocate exceeds the slab size? * If so, allocate this memory chunk separately and * keep track of it. */ - if (allocation_size_with_chunk_header > __slab_data.sd_StandardSlabSize) + if (slab_entry_size > __slab_data.sd_StandardSlabSize) { - ULONG total_single_allocation_size; + size_t total_single_allocation_size; struct SlabSingleAllocation * ssa; total_single_allocation_size = sizeof(*ssa) + allocation_size; @@ -96,210 +131,351 @@ __slab_allocate(size_t allocation_size) padding = 0; D(("allocation size is > %ld; this will be stored separately", __slab_data.sd_StandardSlabSize)); - D(("allocating %ld (MinNode+Size+Padding) + %ld = %ld bytes", sizeof(*ssa), allocation_size, total_single_allocation_size)); /* Check if the sums will cause an integer overflow. */ if (__addition_overflows(sizeof(*ssa), allocation_size) || __addition_overflows(total_single_allocation_size, padding)) { - overflow = TRUE; + SHOWMSG("integer overflow"); + return NULL; } - else + + /* Looking good so far. */ + total_single_allocation_size += padding; + + D(("allocating %ld (MinNode+Size+Padding) + %ld = %ld bytes", sizeof(*ssa), allocation_size, total_single_allocation_size)); + + PROFILE_OFF(); + + #if defined(__amigaos4__) { - /* Looking good so far. */ - total_single_allocation_size += padding; - - overflow = FALSE; + ssa = AllocMem(total_single_allocation_size, MEMF_PRIVATE); } - - /* No integer overflow? */ - if (overflow == FALSE) + #else { - PROFILE_OFF(); - - #if defined(__amigaos4__) - { - ssa = AllocMem(total_single_allocation_size, MEMF_PRIVATE); - } - #else - { - ssa = AllocMem(total_single_allocation_size, MEMF_ANY); - } - #endif /* __amigaos4__ */ - - PROFILE_ON(); + ssa = AllocMem(total_single_allocation_size, MEMF_ANY); } - /* Integer overflow has occurred. */ - else + #endif /* __amigaos4__ */ + + PROFILE_ON(); + + if (ssa == NULL) { - ssa = NULL; + D(("single allocation failed")); + return NULL; } - if (ssa != NULL) - { - ssa->ssa_Size = total_single_allocation_size; + ssa->ssa_Size = total_single_allocation_size; - allocation = &ssa[1]; + allocation = &ssa[1]; - D(("single allocation = 0x%08lx", allocation)); + D(("single allocation = 0x%08lx", allocation)); - AddTail((struct List *)&__slab_data.sd_SingleAllocations, (struct Node *)ssa); + AddTail((struct List *)&__slab_data.sd_SingleAllocations, (struct Node *)ssa); - __slab_data.sd_NumSingleAllocations++; - __slab_data.sd_TotalSingleAllocationSize += total_single_allocation_size; + __slab_data.sd_NumSingleAllocations++; + __slab_data.sd_TotalSingleAllocationSize += total_single_allocation_size; - D(("single allocation succeeded at 0x%08lx (number of single allocations = %lu)", - allocation, __slab_data.sd_NumSingleAllocations)); - } - else - { - if (overflow) - SHOWMSG("integer overflow"); - else - D(("single allocation failed")); - } + D(("single allocation succeeded at 0x%08lx (number of single allocations = %lu)", + allocation, __slab_data.sd_NumSingleAllocations)); } /* Otherwise allocate a chunk from a slab. */ else { - struct MinList * slab_list = NULL; - BOOL slab_reused = FALSE; - ULONG entry_size; + struct MinList * slab_list; + struct SlabNode * sn; ULONG chunk_size = 0; int slab_index; D(("allocation size is <= %ld; this will be allocated from a slab", __slab_data.sd_StandardSlabSize)); - /* Add room for a pointer back to the parent slab - * which the chunk belongs to. + D(("final entry size prior to picking slab size = %ld bytes", + slab_entry_size)); + + /* Find a slab which keeps track of chunks that are no larger + * than the amount of memory which needs to be allocated. We end + * up picking the smallest chunk size that still works. + * + * Note that we start with a minimum size of 8 bytes because that + * is the exact minimum size of a memory allocation as performed + * by AllocMem() and the Allocate() function which it is built + * upon. Because the malloc/realloc/calloc functions already + * add their own management information in the form of the + * 'struct MemoryNode', the minimum allocation size will be + * larger than 8 bytes, though. */ - entry_size = sizeof(*chunk) + allocation_size; + slab_list = NULL; - /* Pad the allocation size to a multiple of 8 bytes. */ - if ((entry_size % MEM_BLOCKSIZE) > 0) - padding = MEM_BLOCKSIZE - (entry_size % MEM_BLOCKSIZE); - else - padding = 0; - - /* Check if the sums will cause an integer overflow. */ - if (__addition_overflows(sizeof(*chunk), allocation_size) || - __addition_overflows(entry_size, padding)) + for (slab_index = 3, chunk_size = (1UL << slab_index) ; + slab_index < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; + slab_index++, chunk_size *= 2) { - overflow = TRUE; - } - else - { - /* Looking good so far. */ - entry_size += padding; - - overflow = FALSE; - } - - if (overflow == FALSE) - { - D(("final entry size prior to picking slab size = %ld bytes", entry_size)); - - /* Find a slab which keeps track of chunks that are no - * larger than the amount of memory which needs to be - * allocated. We end up picking the smallest chunk - * size that still works. - * - * Note that we start with a minimum size of 8 bytes because that - * is the exact minimum size of a memory allocation as performed - * by AllocMem() and the Allocate() function which it is built - * upon. - */ - for (slab_index = 3, chunk_size = (1UL << slab_index) ; - slab_index < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; - slab_index++, chunk_size += chunk_size) + if (slab_entry_size <= chunk_size) { - if (entry_size <= chunk_size) + D(("using slab #%ld (%lu bytes per chunk)", + slab_index, chunk_size)); + + assert( (chunk_size % sizeof(LONG)) == 0 ); + + slab_list = &__slab_data.sd_Slabs[slab_index]; + break; + } + } + + if (slab_list == NULL) + { + D(("no matching slab found")); + goto out; + } + + SHOWVALUE(chunk_size); + + /* The slab list is organized in such a way that the first entry + * always has a free chunk ready for allocation. If there is no + * such free chunk, it means that no other slab nodes in this + * list have any free chunks. + */ + sn = (struct SlabNode *)slab_list->mlh_Head; + + /* Make sure that the slab list is not empty. */ + if (sn->sn_MinNode.mln_Succ != NULL) + { + D(("slab = 0x%08lx, chunk size = %ld", sn, sn->sn_ChunkSize)); + + assert( sn->sn_ChunkSize == chunk_size ); + + chunk = (struct SlabChunk *)RemHead((struct List *)&sn->sn_FreeList); + if (chunk != NULL) + { + /* Keep track of this chunk's parent slab. */ + chunk->sc_ParentSlab = sn; + + allocation = &chunk[1]; + + D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)", + allocation, sn, sn->sn_UseCount)); + + /* Was this slab empty before we began using it again? */ + if (sn->sn_UseCount == 0) { - D(("using slab #%ld (%lu bytes per chunk)", slab_index, chunk_size)); + D(("slab is no longer empty")); - assert( (chunk_size % sizeof(LONG)) == 0 ); + /* Pull it out of the list of slabs available + * for reuse. + */ + Remove((struct Node *)&sn->sn_EmptyLink); - slab_list = &__slab_data.sd_Slabs[slab_index]; + sn->sn_EmptyDecay = 0; + sn->sn_NumReused++; + } + + sn->sn_UseCount++; + + /* Is this slab now fully utilized? Move it to the end + * of the queue so that it will not be checked before + * other slabs of the same size have been tested. Those + * at the front of the queue should still have room + * left. + */ + if (sn->sn_UseCount == sn->sn_Count && + sn != (struct SlabNode *)slab_list->mlh_TailPred) + { + D(("slab is full, goes back to end of list")); + + Remove((struct Node *)sn); + AddTail((struct List *)slab_list, (struct Node *)sn); + } + } + } + + /* There is no slab with a free chunk? Then we might have to + * allocate a new one. + */ + if (allocation == NULL) + { + struct MinNode * free_node; + struct MinNode * free_node_next; + struct SlabNode * new_sn = NULL; + BOOL slab_reused = FALSE; + BOOL purge = FALSE; + + D(("no slab is available which still has free room")); + + /* Try to recycle an empty (unused) slab, if possible. */ + for (free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ; + free_node->mln_Succ != NULL ; + free_node = free_node_next) + { + free_node_next = (struct MinNode *)free_node->mln_Succ; + + /* free_node points to SlabNode.sn_EmptyLink, which + * directly follows the SlabNode.sn_MinNode. + */ + sn = (struct SlabNode *)&free_node[-1]; + + /* Is this empty slab ready to be reused? */ + if (sn->sn_EmptyDecay == 0) + { + /* Unlink from list of empty slabs. */ + Remove((struct Node *)free_node); + + /* If the chunk size of the reused slab matches + * exactly what we need, then we won't have to + * completely reinitialize it again. + */ + if (sn->sn_ChunkSize == chunk_size) + { + slab_reused = TRUE; + } + else + { + /* Unlink from list of slabs which keep chunks + * of the same size. It will be added there + * again, at a different position. + */ + Remove((struct Node *)sn); + } + + D(("reusing a slab")); + + sn->sn_NumReused++; + + new_sn = sn; break; } } - } - if (slab_list != NULL) - { - struct SlabNode * sn; - - SHOWVALUE(chunk_size); - - /* The slab list is organized in such a way that the first - * entry always has a free chunk ready for allocation. If - * there is no such free chunk, it means that no other - * slab nodes in this list have any free chunks. + /* We couldn't reuse an empty slab? Then we'll have to + * allocate memory for another one. + * + * Note that we allocate an extra MEM_BLOCKSIZE bytes so that + * we may chop up the slab into chunks which all start on an + * address which is a multiple of MEM_BLOCKSIZE bytes. This + * is useful for aligning allocations to a 64 bit boundary + * on the PowerPC when using floating point numbers embedded + * in data structures. */ - sn = (struct SlabNode *)slab_list->mlh_Head; - - /* Make sure that the slab list is not empty. */ - if (sn->sn_MinNode.mln_Succ != NULL) + if (new_sn == NULL) { - D(("slab = 0x%08lx, chunk size = %ld", sn, sn->sn_ChunkSize)); + D(("no slab is available for reuse; allocating a new slab (%lu bytes)", + sizeof(*new_sn) + __slab_data.sd_StandardSlabSize)); - assert( sn->sn_ChunkSize == chunk_size ); + PROFILE_OFF(); - chunk = (struct SlabChunk *)RemHead((struct List *)&sn->sn_FreeList); - if (chunk != NULL) + #if defined(__amigaos4__) { - /* Keep track of this chunk's parent slab. */ - chunk->sc_ParentSlab = sn; - - allocation = &chunk[1]; - - D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)", - allocation, sn, sn->sn_UseCount)); - - /* Was this slab empty before we began using it again? */ - if (sn->sn_UseCount == 0) - { - D(("slab is no longer empty")); - - /* Pull it out of the list of slabs available for reuse. */ - Remove((struct Node *)&sn->sn_EmptyLink); - - sn->sn_EmptyDecay = 0; - sn->sn_NumReused++; - } - - sn->sn_UseCount++; - - /* Is this slab now fully utilized? Move it to the - * end of the queue so that it will not be checked - * before other slabs of the same size have been - * tested. Those at the front of the queue should - * still have room left. - */ - if (sn->sn_UseCount == sn->sn_Count && sn != (struct SlabNode *)slab_list->mlh_TailPred) - { - D(("slab is full")); - - Remove((struct Node *)sn); - AddTail((struct List *)slab_list, (struct Node *)sn); - } + new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize + MEM_BLOCKSIZE, MEMF_PRIVATE); } + #else + { + new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize + MEM_BLOCKSIZE, MEMF_ANY); + } + #endif /* __amigaos4__ */ + + PROFILE_ON(); + + if (new_sn == NULL) + D(("slab allocation failed")); + + /* If this allocation went well, try to free all currently + * unused slabs which are ready for purging. This is done so + * that we don't keep allocating new memory all the time + * without cutting back on unused slabs. + */ + purge = TRUE; } - /* There is no slab with a free chunk? Then we might have to - * allocate a new one. - */ - if (allocation == NULL) + if (new_sn != NULL) { - struct MinNode * free_node; - struct MinNode * free_node_next; - struct SlabNode * new_sn = NULL; - BOOL purge = FALSE; + D(("setting up slab 0x%08lx", new_sn)); - D(("no slab is available which still has free room")); + assert( chunk_size <= __slab_data.sd_StandardSlabSize ); + + /* Do we have to completely initialize this slab from + * scratch? + */ + if (slab_reused == FALSE) + { + struct SlabChunk * free_chunk; + ULONG num_free_chunks = 0; + BYTE * first_byte; + BYTE * last_byte; + + memset(new_sn, 0, sizeof(*new_sn)); + + NewList((struct List *)&new_sn->sn_FreeList); + + /* This slab has room for new allocations, so make + * sure that it goes to the front of the slab list. + * It will be used by the next allocation request + * of this size. + */ + AddHead((struct List *)slab_list, (struct Node *)new_sn); + + /* Split up the slab memory into individual chunks of + * the same size and keep track of them in the free + * list. The memory managed by this slab follows the + * SlabNode header with some padding added to make + * the first allocatable slab start on a 64-bit + * boundary. + */ + first_byte = (BYTE *)((((ULONG)&new_sn[1]) + MEM_BLOCKMASK) & ~MEM_BLOCKMASK); + last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; + + for (free_chunk = (struct SlabChunk *)first_byte ; + free_chunk <= (struct SlabChunk *)last_byte; + free_chunk = (struct SlabChunk *)(((BYTE *)free_chunk) + chunk_size)) + { + AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk); + num_free_chunks++; + } + + new_sn->sn_Count = num_free_chunks; + new_sn->sn_ChunkSize = chunk_size; + + D(("new slab contains %lu chunks, %lu bytes each", + num_free_chunks, chunk_size)); + } + /* This slab was reused and need not be reinitialized + * from scratch. + */ + else + { + assert( new_sn->sn_FreeList.mlh_Head != NULL ); + assert( new_sn->sn_ChunkSize == chunk_size ); + assert( new_sn->sn_Count == 0 ); + } + + /* Grab the first free chunk (there has to be one). */ + chunk = (struct SlabChunk *)RemHead((struct List *)&new_sn->sn_FreeList); + + assert( chunk != NULL ); + + /* Keep track of this chunk's parent slab. */ + chunk->sc_ParentSlab = new_sn; + + assert( chunk != NULL ); + assert( chunk->sc_ParentSlab == new_sn ); + + allocation = &chunk[1]; + + /* This slab is now in use. */ + new_sn->sn_UseCount = 1; + + D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)", + allocation, new_sn, new_sn->sn_UseCount)); + } + + /* Mark unused slabs for purging, and purge those which + * are ready to be purged. + */ + if (purge) + { + size_t total_purged = 0; + + D(("purging empty slabs")); - /* Try to recycle an empty (unused) slab, if possible. */ for (free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ; free_node->mln_Succ != NULL ; free_node = free_node_next) @@ -311,226 +487,55 @@ __slab_allocate(size_t allocation_size) */ sn = (struct SlabNode *)&free_node[-1]; - /* Is this empty slab ready to be reused? */ + /* Is this empty slab ready to be purged? */ if (sn->sn_EmptyDecay == 0) { + D(("freeing empty slab")); + /* Unlink from list of empty slabs. */ Remove((struct Node *)free_node); - /* If the chunk size of the reused slab matches - * exactly what we need then we won't have to - * completely reinitialize it again. + /* Unlink from list of slabs of the same size. */ + Remove((struct Node *)sn); + + PROFILE_OFF(); + FreeVec(sn); + PROFILE_ON(); + + total_purged += sizeof(*sn) + __slab_data.sd_StandardSlabSize; + + /* Stop releasing memory if we reach the + * threshold. If no threshold has been set, + * we will free as much memory as possible. */ - if (sn->sn_ChunkSize == chunk_size) - { - slab_reused = TRUE; - } - else - { - /* Unlink from list of slabs which keep chunks - * of the same size. It will be added there - * again, at a different position. - */ - Remove((struct Node *)sn); - } - - D(("reusing a slab")); - - sn->sn_NumReused++; - - new_sn = sn; - break; + if (__slab_purge_threshold > 0 && total_purged >= __slab_purge_threshold) + break; } - } - - /* We couldn't reuse an empty slab? Then we'll have to allocate - * memory for another one. - * - * Note that we allocate an extra MEM_BLOCKSIZE bytes so that we - * may chop up the slab into chunks which all start on an address - * which is a multiple of MEM_BLOCKSIZE bytes. This is useful for - * aligning allocations to a 64 bit boundary on the PowerPC when - * using floating point numbers embedded in data structures. - */ - if (new_sn == NULL) - { - D(("no slab is available for reuse; allocating a new slab (%lu bytes)", - sizeof(*new_sn) + __slab_data.sd_StandardSlabSize)); - - PROFILE_OFF(); - - #if defined(__amigaos4__) - { - new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize + MEM_BLOCKSIZE, MEMF_PRIVATE); - } - #else - { - new_sn = (struct SlabNode *)AllocVec(sizeof(*new_sn) + __slab_data.sd_StandardSlabSize + MEM_BLOCKSIZE, MEMF_ANY); - } - #endif /* __amigaos4__ */ - - PROFILE_ON(); - - if (new_sn == NULL) - D(("slab allocation failed")); - - /* If this allocation went well, try to free all currently unused - * slabs which are ready for purging. This is done so that we don't - * keep allocating new memory all the time without cutting back on - * unused slabs. - */ - purge = TRUE; - } - - if (new_sn != NULL) - { - D(("setting up slab 0x%08lx", new_sn)); - - assert( chunk_size <= __slab_data.sd_StandardSlabSize ); - - /* Do we have to completely initialize this slab from scratch? */ - if (NOT slab_reused) - { - struct SlabChunk * free_chunk; - ULONG num_free_chunks = 0; - BYTE * first_byte; - BYTE * last_byte; - - memset(new_sn, 0, sizeof(*new_sn)); - - NewList((struct List *)&new_sn->sn_FreeList); - - /* This slab has room for new allocations, so make sure that - * it goes to the front of the slab list. It will be used - * by the next allocation request of this size. - */ - AddHead((struct List *)slab_list, (struct Node *)new_sn); - - /* Split up the slab memory into individual chunks of the same - * size and keep track of them in the free list. The memory - * managed by this slab follows the SlabNode header with some - * padding added to make the first allocatable slab start on - * a 64 bit boundary. - */ - first_byte = (BYTE *)((((ULONG)&new_sn[1]) + MEM_BLOCKMASK) & ~MEM_BLOCKMASK); - last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; - - for (free_chunk = (struct SlabChunk *)first_byte ; - free_chunk <= (struct SlabChunk *)last_byte; - free_chunk = (struct SlabChunk *)(((BYTE *)free_chunk) + chunk_size)) - { - AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk); - num_free_chunks++; - } - - new_sn->sn_Count = num_free_chunks; - new_sn->sn_ChunkSize = chunk_size; - - D(("new slab contains %lu chunks, %lu bytes each", num_free_chunks, chunk_size)); - } - /* This slab was reused and need not be reinitialized from scratch. */ + /* Give it another chance. */ else { - assert( new_sn->sn_FreeList.mlh_Head != NULL ); - assert( new_sn->sn_ChunkSize == chunk_size ); - assert( new_sn->sn_Count == 0 ); - } + sn->sn_EmptyDecay--; - /* Grab the first free chunk (there has to be one). */ - chunk = (struct SlabChunk *)RemHead((struct List *)&new_sn->sn_FreeList); - - assert( chunk != NULL ); - - /* Keep track of this chunk's parent slab. */ - chunk->sc_ParentSlab = new_sn; - - assert( chunk != NULL ); - assert( chunk->sc_ParentSlab == new_sn ); - - allocation = &chunk[1]; - - /* This slab is now in use. */ - new_sn->sn_UseCount = 1; - - D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)", - allocation, new_sn, new_sn->sn_UseCount)); - } - - /* Mark unused slabs for purging, and purge those which - * are ready to be purged. - */ - if (purge) - { - size_t total_purged = 0; - - D(("purging empty slabs")); - - for (free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ; - free_node->mln_Succ != NULL ; - free_node = free_node_next) - { - free_node_next = (struct MinNode *)free_node->mln_Succ; - - /* free_node points to SlabNode.sn_EmptyLink, which - * directly follows the SlabNode.sn_MinNode. - */ - sn = (struct SlabNode *)&free_node[-1]; - - /* Is this empty slab ready to be purged? */ + /* Is this slab ready for reuse now? */ if (sn->sn_EmptyDecay == 0) { - D(("freeing empty slab")); - - /* Unlink from list of empty slabs. */ - Remove((struct Node *)free_node); - - /* Unlink from list of slabs of the same size. */ - Remove((struct Node *)sn); - - PROFILE_OFF(); - FreeVec(sn); - PROFILE_ON(); - - total_purged += sizeof(*sn) + __slab_data.sd_StandardSlabSize; - - /* Stop releasing memory if we reach the threshold. If no - * threshold has been set, we will free as much memory - * as possible. + /* Move it to the front of the list, so that + * it will be collected as soon as possible. */ - if (__slab_purge_threshold > 0 && total_purged >= __slab_purge_threshold) - break; - } - /* Give it another chance. */ - else - { - sn->sn_EmptyDecay--; - - /* Is this slab ready for reuse now? */ - if (sn->sn_EmptyDecay == 0) + if (free_node != (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head) { - /* Move it to the front of the list, so that - * it will be collected as soon as possible. - */ - if (free_node != (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head) - { - Remove((struct Node *)free_node); - AddHead((struct List *)&__slab_data.sd_EmptySlabs, (struct Node *)free_node); - } + Remove((struct Node *)free_node); + AddHead((struct List *)&__slab_data.sd_EmptySlabs, (struct Node *)free_node); } } } } } } - else - { - if (overflow) - SHOWMSG("integer overflow"); - else - D(("no matching slab found")); - } } + out: + return allocation; } @@ -540,20 +545,29 @@ void __slab_free(void * address, size_t allocation_size) { struct SlabChunk * chunk; + ULONG slab_entry_size; D(("freeing allocation at 0x%08lx, %lu bytes", address, allocation_size)); assert( __slab_data.sd_StandardSlabSize > 0 ); - /* Number of bytes allocated exceeds the slab size? - * Then the chunk was allocated separately. + slab_entry_size = get_slab_entry_size((ULONG)allocation_size); + if (slab_entry_size == 0) + { + SHOWMSG("integer overflow"); + return; + } + + /* Number of bytes allocated exceeds the slab size? Then the chunk was + * allocated separately. */ - if (sizeof(*chunk) + allocation_size > __slab_data.sd_StandardSlabSize) + if (slab_entry_size > __slab_data.sd_StandardSlabSize) { struct SlabSingleAllocation * ssa = address; ULONG size; - D(("allocation size is > %ld; this was stored separately", __slab_data.sd_StandardSlabSize)); + D(("allocation size is > %ld; this was stored separately", + __slab_data.sd_StandardSlabSize)); assert( __slab_data.sd_NumSingleAllocations > 0 ); @@ -562,8 +576,8 @@ __slab_free(void * address, size_t allocation_size) */ ssa--; - /* Verify that the allocation is really on the list we - * will remove it from. + /* Verify that the allocation is really on the list we will remove + * it from. */ #if DEBUG { @@ -597,46 +611,40 @@ __slab_free(void * address, size_t allocation_size) FreeMem(ssa, size); PROFILE_ON(); + assert( __slab_data.sd_NumSingleAllocations >= 1 ); + assert( __slab_data.sd_TotalSingleAllocationSize >= size ); + __slab_data.sd_NumSingleAllocations--; __slab_data.sd_TotalSingleAllocationSize -= size; - D(("number of single allocations = %ld", __slab_data.sd_NumSingleAllocations)); + D(("number of single allocations = %ld", + __slab_data.sd_NumSingleAllocations)); } /* Otherwise the allocation should have come from a slab. */ else { - struct MinList * slab_list = NULL; - size_t entry_size; + struct MinList * slab_list; + struct SlabNode * sn; ULONG chunk_size; int slab_index; D(("allocation size is <= %ld; this was allocated from a slab", __slab_data.sd_StandardSlabSize)); - /* Add room for a pointer back to the slab which - * the chunk belongs to. + /* Find a slab which keeps track of chunks that are no larger than + * the amount of memory which needs to be released. We end up + * picking the smallest chunk size that still works. */ - entry_size = sizeof(*chunk) + allocation_size; + slab_list = NULL; - /* Chunks must be at least as small as a MinNode, because - * that's what we use for keeping track of the chunks which - * are available for allocation within each slab. - */ - if (entry_size < sizeof(struct MinNode)) - entry_size = sizeof(struct MinNode); - - /* Find a slab which keeps track of chunks that are no - * larger than the amount of memory which needs to be - * released. We end up picking the smallest chunk - * size that still works. - */ - for (slab_index = 2, chunk_size = (1UL << slab_index) ; + for (slab_index = 3, chunk_size = (1UL << slab_index) ; slab_index < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; - slab_index++, chunk_size += chunk_size) + slab_index++, chunk_size *= 2) { - if (entry_size <= chunk_size) + if (slab_entry_size <= chunk_size) { - D(("using slab #%ld (%ld bytes per chunk)", slab_index, chunk_size)); + D(("using slab #%ld (%ld bytes per chunk)", + slab_index, chunk_size)); assert( (chunk_size % sizeof(LONG)) == 0 ); @@ -646,125 +654,123 @@ __slab_free(void * address, size_t allocation_size) } /* Pick the slab which contains the memory chunk. */ - if (slab_list != NULL) - { - struct SlabNode * sn; - - assert( chunk_size <= __slab_data.sd_StandardSlabSize ); - - /* The pointer back to the slab which this chunk belongs - * to precedes the address which __slab_allocate() - * returned. - */ - chunk = address; - chunk--; - - sn = chunk->sc_ParentSlab; - - #if DEBUG - { - struct SlabNode * other_sn; - BOOL slab_found = FALSE; - BOOL chunk_found = FALSE; - - for (other_sn = (struct SlabNode *)slab_list->mlh_Head ; - other_sn->sn_MinNode.mln_Succ != NULL ; - other_sn = (struct SlabNode *)other_sn->sn_MinNode.mln_Succ) - { - if (other_sn == sn) - { - slab_found = TRUE; - break; - } - } - - assert( slab_found ); - - if (slab_found) - { - struct MinNode * free_chunk; - BYTE * first_byte; - BYTE * last_byte; - - first_byte = (BYTE *)((((ULONG)&sn[1]) + MEM_BLOCKMASK) & ~MEM_BLOCKMASK); - last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; - - for (free_chunk = (struct MinNode *)first_byte ; - free_chunk <= (struct MinNode *)last_byte; - free_chunk = (struct MinNode *)(((BYTE *)free_chunk) + chunk_size)) - { - if (free_chunk == (struct MinNode *)chunk) - { - chunk_found = TRUE; - break; - } - } - } - - assert( chunk_found ); - } - #endif /* DEBUG */ - - SHOWVALUE(sn->sn_ChunkSize); - - assert( sn->sn_ChunkSize != 0 ); - - assert( sn->sn_ChunkSize == chunk_size ); - - D(("allocation is part of slab 0x%08lx (slab use count = %ld)", sn, sn->sn_UseCount)); - - #if DEBUG - { - struct MinNode * mln; - BOOL chunk_already_free = FALSE; - - for (mln = sn->sn_FreeList.mlh_Head ; - mln->mln_Succ != NULL ; - mln = mln->mln_Succ) - { - if (mln == (struct MinNode *)chunk) - { - chunk_already_free = TRUE; - break; - } - } - - assert( NOT chunk_already_free ); - } - #endif /* DEBUG */ - - AddHead((struct List *)&sn->sn_FreeList, (struct Node *)chunk); - - assert( sn->sn_UseCount > 0 ); - - sn->sn_UseCount--; - - /* If this slab is empty, mark it as unused and - * allow it to be purged. - */ - if (sn->sn_UseCount == 0) - { - D(("slab is now empty")); - - AddTail((struct List *)&__slab_data.sd_EmptySlabs, (struct Node *)&sn->sn_EmptyLink); - sn->sn_EmptyDecay = 1; - } - - /* This slab now has room. Move it to front of the list - * so that searching for a free chunk will pick it - * first. - */ - if (sn != (struct SlabNode *)slab_list->mlh_Head) - { - D(("moving slab to the head of the list")); - - Remove((struct Node *)sn); - AddHead((struct List *)slab_list, (struct Node *)sn); - } - } - else + if (slab_list == NULL) { D(("no matching slab found")); + return; + } + + assert( chunk_size <= __slab_data.sd_StandardSlabSize ); + + /* The pointer back to the slab which this chunk belongs + * to precedes the address which __slab_allocate() + * returned. + */ + chunk = address; + chunk--; + + sn = chunk->sc_ParentSlab; + + #if DEBUG + { + struct SlabNode * other_sn; + BOOL slab_found = FALSE; + BOOL chunk_found = FALSE; + + for (other_sn = (struct SlabNode *)slab_list->mlh_Head ; + other_sn->sn_MinNode.mln_Succ != NULL ; + other_sn = (struct SlabNode *)other_sn->sn_MinNode.mln_Succ) + { + if (other_sn == sn) + { + slab_found = TRUE; + break; + } + } + + assert( slab_found ); + + if (slab_found) + { + struct MinNode * free_chunk; + BYTE * first_byte; + BYTE * last_byte; + + first_byte = (BYTE *)((((ULONG)&sn[1]) + MEM_BLOCKMASK) & ~MEM_BLOCKMASK); + last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; + + for (free_chunk = (struct MinNode *)first_byte ; + free_chunk <= (struct MinNode *)last_byte; + free_chunk = (struct MinNode *)(((BYTE *)free_chunk) + chunk_size)) + { + if (free_chunk == (struct MinNode *)chunk) + { + chunk_found = TRUE; + break; + } + } + } + + assert( chunk_found ); + } + #endif /* DEBUG */ + + SHOWVALUE(sn->sn_ChunkSize); + + assert( sn->sn_ChunkSize != 0 ); + + assert( sn->sn_ChunkSize == chunk_size ); + + D(("allocation is part of slab 0x%08lx (slab use count = %ld)", + sn, sn->sn_UseCount)); + + #if DEBUG + { + struct MinNode * mln; + BOOL chunk_already_free = FALSE; + + for (mln = sn->sn_FreeList.mlh_Head ; + mln->mln_Succ != NULL ; + mln = mln->mln_Succ) + { + if (mln == (struct MinNode *)chunk) + { + chunk_already_free = TRUE; + break; + } + } + + assert( NOT chunk_already_free ); + } + #endif /* DEBUG */ + + AddHead((struct List *)&sn->sn_FreeList, (struct Node *)chunk); + + assert( sn->sn_UseCount > 0 ); + + sn->sn_UseCount--; + + /* If this slab is empty, mark it as unused and + * allow it to be purged. + */ + if (sn->sn_UseCount == 0) + { + D(("slab is now empty")); + + AddTail((struct List *)&__slab_data.sd_EmptySlabs, (struct Node *)&sn->sn_EmptyLink); + sn->sn_EmptyDecay = 1; + } + + /* This slab now has room. Move it to front of the list + * so that searching for a free chunk will pick it + * first. + */ + if (sn != (struct SlabNode *)slab_list->mlh_Head) + { + D(("moving slab to the head of the list")); + + Remove((struct Node *)sn); + AddHead((struct List *)slab_list, (struct Node *)sn); } } } @@ -853,7 +859,7 @@ __slab_init(size_t slab_size) #if DEBUG -static int print_json(void * ignore, const char * buffer, size_t len) +static int print_json(void * ignore UNUSED, const char * buffer, size_t len UNUSED) { extern void kputs(const char * str); @@ -923,7 +929,10 @@ __slab_exit(void) } if (slab_count > 0) - D(("number of slabs = %ld, total slab size = %ld bytes", slab_count, total_slab_size)); + { + D(("number of slabs = %ld, total slab size = %ld bytes", + slab_count, total_slab_size)); + } if (__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL) D(("freeing single allocations")); @@ -939,7 +948,8 @@ __slab_exit(void) ssa = (struct SlabSingleAllocation *)mn; - D((" allocation #%ld at 0x%08lx, %lu bytes", ++j, ssa, ssa->ssa_Size)); + D((" allocation #%ld at 0x%08lx, %lu bytes", + ++j, ssa, ssa->ssa_Size)); total_single_allocation_size += ssa->ssa_Size; single_allocation_count++;