mirror of
https://github.com/php/pecl-caching-wincache.git
synced 2026-03-23 23:02:17 +01:00
1553 lines
45 KiB
C
1553 lines
45 KiB
C
/*
|
|
+----------------------------------------------------------------------------------------------+
|
|
| Windows Cache for PHP |
|
|
+----------------------------------------------------------------------------------------------+
|
|
| Copyright (c) 2009, Microsoft Corporation. All rights reserved. |
|
|
| |
|
|
| Redistribution and use in source and binary forms, with or without modification, are |
|
|
| permitted provided that the following conditions are met: |
|
|
| - Redistributions of source code must retain the above copyright notice, this list of |
|
|
| conditions and the following disclaimer. |
|
|
| - Redistributions in binary form must reproduce the above copyright notice, this list of |
|
|
| conditions and the following disclaimer in the documentation and/or other materials provided |
|
|
| with the distribution. |
|
|
| - Neither the name of the Microsoft Corporation nor the names of its contributors may be |
|
|
| used to endorse or promote products derived from this software without specific prior written|
|
|
| permission. |
|
|
| |
|
|
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS |
|
|
| OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
|
| MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
|
| COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
|
| EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE|
|
|
| GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
|
| AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
|
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
|
|
| OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
+----------------------------------------------------------------------------------------------+
|
|
| Module: wincache_alloc.c |
|
|
+----------------------------------------------------------------------------------------------+
|
|
| Author: Kanwaljeet Singla <ksingla@microsoft.com> |
|
|
| Updated: Eric Stenson <ericsten@microsoft.com> |
|
|
+----------------------------------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
#define BLOCK_ISFREE_FIRST (unsigned int)'iiii'
|
|
#define BLOCK_ISFREE_FREE (unsigned int)'ffff'
|
|
#define BLOCK_ISFREE_USED (unsigned int)'uuuu'
|
|
#define BLOCK_ISFREE_LAST (unsigned int)'llll'
|
|
|
|
#define ALLOC_SEGMENT_HEADER_SIZE (sizeof(alloc_segment_header))
|
|
#define ALLOC_FREE_BLOCK_OVERHEAD (sizeof(alloc_free_header) + sizeof(size_t))
|
|
#define ALLOC_USED_BLOCK_OVERHEAD (sizeof(alloc_used_header) + sizeof(size_t))
|
|
|
|
#define POINTER_OFFSET(baseaddr, p) ((char *)p - (char *)baseaddr)
|
|
#define FREE_HEADER(baseaddr, offset) (alloc_free_header *)((char *)baseaddr + offset)
|
|
#define USED_HEADER(baseaddr, offset) (alloc_used_header *)((char *)baseaddr + offset)
|
|
#define MIN_FREE_BLOCK_SIZE (ALLOC_FREE_BLOCK_OVERHEAD + 4)
|
|
|
|
#define POOL_ALLOCATION_TYPE_UNKNOWN 0
|
|
#define POOL_ALLOCATION_TYPE_SMALL 1
|
|
#define POOL_ALLOCATION_TYPE_MEDIUM 2
|
|
#define POOL_ALLOCATION_TYPE_LARGE 3
|
|
#define POOL_ALLOCATION_TYPE_HUGE 4
|
|
|
|
#define POOL_ALLOCATION_SMALL_MAX 128
|
|
#define POOL_ALLOCATION_MEDIUM_MAX 256
|
|
#define POOL_ALLOCATION_LARGE_MAX 1024
|
|
|
|
#define POOL_ALLOCATION_SMALL_BSIZE 512
|
|
#define POOL_ALLOCATION_MEDIUM_BSIZE 1024
|
|
#define POOL_ALLOCATION_LARGE_BSIZE 4096
|
|
|
|
/* Globals */
|
|
unsigned short gallocid = 1;
|
|
|
|
/* Private methods */
|
|
static int allocate_memory(alloc_context * palloc, size_t size, void ** ppaddr);
|
|
static void free_memory(alloc_context * palloc, void * paddr);
|
|
static size_t get_memory_size(alloc_context * palloc, void * addr);
|
|
|
|
static void * alloc_malloc(alloc_context * palloc, unsigned int type, size_t size);
|
|
static void * alloc_realloc(alloc_context * palloc, unsigned int type, void * addr, size_t size);
|
|
static char * alloc_strdup(alloc_context * palloc, unsigned int type, const char * str);
|
|
static void alloc_free(alloc_context * palloc, unsigned int type, void * addr);
|
|
|
|
static int allocate_memory(alloc_context * palloc, size_t size, void ** ppaddr)
|
|
{
|
|
int result = NONFATAL;
|
|
void * paddr = NULL;
|
|
void * segaddr = NULL;
|
|
unsigned char flock = 0;
|
|
|
|
size_t oldsize = 0;
|
|
size_t oldprev = 0;
|
|
size_t oldnext = 0;
|
|
|
|
alloc_segment_header * header = NULL;
|
|
alloc_free_header * freeh = NULL;
|
|
alloc_free_header * pfree = NULL;
|
|
alloc_used_header * usedh = NULL;
|
|
|
|
const char * filename;
|
|
uint32_t lineno;
|
|
|
|
dprintdecorate("start allocate_memory");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(palloc->lock != NULL);
|
|
_ASSERT(palloc->header != NULL);
|
|
_ASSERT(size >= 0);
|
|
|
|
/* If localheap should be used, use HeapAlloc */
|
|
if(palloc->localheap == 1)
|
|
{
|
|
*ppaddr = HeapAlloc(GetProcessHeap(), 0, size);
|
|
goto Finished;
|
|
}
|
|
|
|
/* Allocate memory from memory segment shared with other processes */
|
|
segaddr = palloc->memaddr;
|
|
header = palloc->header;
|
|
|
|
/* Allocate in sizes which are multiples of size_t */
|
|
#ifdef _WIN64
|
|
size = ALIGNQWORD(size);
|
|
#else /* _WIN64 */
|
|
size = ALIGNDWORD(size);
|
|
#endif /* _WIN64 */
|
|
|
|
/* Acquire lock to segment before doing anything */
|
|
lock_lock(palloc->lock);
|
|
flock = 1;
|
|
|
|
/* If available size is less than requested size, return NULL */
|
|
if(header->free_size < size)
|
|
{
|
|
result = FATAL_ALLOC_NO_MEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
/* Get pointer to start block and move to next block */
|
|
freeh = (alloc_free_header *)(header + 1);
|
|
_ASSERT(freeh->is_free == BLOCK_ISFREE_FIRST);
|
|
if (freeh->is_free != BLOCK_ISFREE_FIRST)
|
|
{
|
|
dprintcritical("allocate_memory: First block is not BLOCK_ISFREE_FIRST");
|
|
result = FATAL_ALLOC_SEGMENT_CORRUPT;
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemFreeListCorrupt(freeh, palloc, filename, lineno);
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
freeh = FREE_HEADER(segaddr, freeh->next_free);
|
|
_ASSERT(freeh->is_free == BLOCK_ISFREE_FREE || freeh->is_free == BLOCK_ISFREE_LAST);
|
|
if (freeh->is_free != BLOCK_ISFREE_FREE && freeh->is_free != BLOCK_ISFREE_LAST)
|
|
{
|
|
dprintcritical("allocate_memory: Free block is not BLOCK_ISFREE_FREE or BLOCK_ISFREE_LAST");
|
|
result = FATAL_ALLOC_SEGMENT_CORRUPT;
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemFreeListCorrupt(freeh, palloc, filename, lineno);
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
/* Find a free block which is large enough to satisfy the request */
|
|
while(freeh->is_free != BLOCK_ISFREE_LAST)
|
|
{
|
|
_ASSERT(freeh->is_free == BLOCK_ISFREE_FREE);
|
|
if (freeh->is_free != BLOCK_ISFREE_FREE)
|
|
{
|
|
dprintcritical("allocate_memory: Free block is not BLOCK_ISFREE_FREE");
|
|
result = FATAL_ALLOC_SEGMENT_CORRUPT;
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemFreeListCorrupt(freeh, palloc, filename, lineno);
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
if(freeh->size >= size)
|
|
{
|
|
break;
|
|
}
|
|
|
|
freeh = FREE_HEADER(segaddr, freeh->next_free);
|
|
}
|
|
|
|
/* If we didn't find a block big enough, return error */
|
|
if(freeh->is_free == BLOCK_ISFREE_LAST)
|
|
{
|
|
result = FATAL_ALLOC_NO_MEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
_ASSERT(freeh->is_free == BLOCK_ISFREE_FREE);
|
|
|
|
/* Got a free block with sufficient free memory. Now */
|
|
/* see if block size is big enough to be broken into two */
|
|
if(freeh->size >= size + MIN_FREE_BLOCK_SIZE)
|
|
{
|
|
oldsize = freeh->size;
|
|
oldprev = freeh->prev_free;
|
|
oldnext = freeh->next_free;
|
|
|
|
usedh = (alloc_used_header *)freeh;
|
|
usedh->size = size;
|
|
usedh->is_free = BLOCK_ISFREE_USED;
|
|
usedh->dummy1 = 0;
|
|
usedh->dummy2 = 0;
|
|
|
|
/* Return pointer right after the used header */
|
|
paddr = (void *)(usedh + 1);
|
|
|
|
/* Put the size of allocated block in the end */
|
|
*((size_t *)((char *)paddr + size)) = size;
|
|
|
|
/* Create a new free header after the allocated block */
|
|
freeh = (alloc_free_header *)((char *)usedh + size + ALLOC_USED_BLOCK_OVERHEAD);
|
|
freeh->size = oldsize - size - ALLOC_USED_BLOCK_OVERHEAD;
|
|
freeh->is_free = BLOCK_ISFREE_FREE;
|
|
freeh->prev_free = oldprev;
|
|
freeh->next_free = oldnext;
|
|
*((size_t *)(((char *)(freeh + 1))+ freeh->size)) = freeh->size;
|
|
|
|
pfree = FREE_HEADER(segaddr, freeh->prev_free);
|
|
pfree->next_free = POINTER_OFFSET(segaddr, freeh);
|
|
pfree = FREE_HEADER(segaddr, freeh->next_free);
|
|
pfree->prev_free = POINTER_OFFSET(segaddr, freeh);
|
|
|
|
/* Update free_size in segment header */
|
|
header->free_size -= (size + ALLOC_USED_BLOCK_OVERHEAD);
|
|
|
|
/* Number of free blocks are not decreasing in this case */
|
|
header->usedcount++;
|
|
}
|
|
else
|
|
{
|
|
/* Block is very small and should be used completely */
|
|
pfree = FREE_HEADER(segaddr, freeh->prev_free);
|
|
pfree->next_free = freeh->next_free;
|
|
|
|
pfree = FREE_HEADER(segaddr, freeh->next_free);
|
|
pfree->prev_free = freeh->prev_free;
|
|
|
|
/* Update size to indicate number of bytes we are actually allocating */
|
|
size = freeh->size;
|
|
|
|
/* Set used header and last two size_t bytes */
|
|
usedh = (alloc_used_header *)freeh;
|
|
usedh->size = size;
|
|
usedh->is_free = BLOCK_ISFREE_USED;
|
|
usedh->dummy1 = 0;
|
|
usedh->dummy2 = 0;
|
|
|
|
/* Return pointer right after the used header */
|
|
paddr = (void *)(usedh + 1);
|
|
|
|
/* Put the size of allocated block in the end */
|
|
*((size_t *)((char *)paddr + size)) = size;
|
|
|
|
header->free_size -= size;
|
|
|
|
/* One free block got converted to used block */
|
|
header->freecount--;
|
|
header->usedcount++;
|
|
}
|
|
|
|
*ppaddr = paddr;
|
|
|
|
Finished:
|
|
|
|
if(flock)
|
|
{
|
|
lock_unlock(palloc->lock);
|
|
flock = 0;
|
|
}
|
|
|
|
if(FAILED(result))
|
|
{
|
|
_ASSERT(*ppaddr == NULL);
|
|
|
|
dprintverbose("failure %d in allocate_memory", result);
|
|
}
|
|
|
|
dprintdecorate("end allocate_memory");
|
|
|
|
return result;
|
|
}
|
|
|
|
static void free_memory(alloc_context * palloc, void * paddr)
|
|
{
|
|
int combined = 0;
|
|
size_t blocksiz = 0;
|
|
size_t lastsize = 0;
|
|
size_t coffset = 0;
|
|
void * segaddr = NULL;
|
|
unsigned char flock = 0;
|
|
|
|
alloc_segment_header * header = NULL;
|
|
alloc_used_header * usedh = NULL;
|
|
alloc_free_header * freeh = NULL;
|
|
alloc_free_header * pfree = NULL;
|
|
|
|
const char * filename;
|
|
uint32_t lineno;
|
|
|
|
dprintdecorate("start free_memory");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(paddr != NULL);
|
|
|
|
/* If localheap is used, use HeapFree to free memory */
|
|
if(palloc->localheap == 1)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, paddr);
|
|
goto Finished;
|
|
}
|
|
|
|
/* Free the block from shared segment */
|
|
lock_lock(palloc->lock);
|
|
flock = 1;
|
|
|
|
header = palloc->header;
|
|
segaddr = palloc->memaddr;
|
|
|
|
/* Get pointer to used_header for the block which is getting freed */
|
|
usedh = (alloc_used_header *)((char *)paddr - sizeof(alloc_used_header));
|
|
|
|
/* Validate the memory being freed is in the heap */
|
|
if ((void *)usedh < segaddr || (char *)usedh > ((char *)segaddr + palloc->size))
|
|
{
|
|
_ASSERT(0);
|
|
dprintcritical("free_memory: Address %p not in segment %p (size %d)",
|
|
paddr, segaddr, palloc->size);
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemFreeAddrNotInSegment(paddr, palloc, filename, lineno);
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
/* Block is in our heap. Validate it is in use */
|
|
_ASSERT(usedh->is_free == BLOCK_ISFREE_USED);
|
|
if (usedh->is_free != BLOCK_ISFREE_USED)
|
|
{
|
|
dprintcritical("free_memory: Address %p not in use in segment %p (size %d)",
|
|
paddr, segaddr, palloc->size);
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemBlockNotInUse(paddr, palloc, filename, lineno);
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
/* Increment freecount and decrement usedcount right away */
|
|
header->usedcount--;
|
|
header->freecount++;
|
|
|
|
blocksiz = usedh->size;
|
|
header->free_size += blocksiz;
|
|
|
|
/* Check if next block is free */
|
|
freeh = (alloc_free_header *)((char *)usedh + blocksiz + ALLOC_USED_BLOCK_OVERHEAD);
|
|
if(freeh->is_free == BLOCK_ISFREE_FREE)
|
|
{
|
|
/* Block after this block is free. Need to combine */
|
|
pfree = (alloc_free_header *)usedh;
|
|
|
|
pfree->size = freeh->size + ALLOC_FREE_BLOCK_OVERHEAD + blocksiz;
|
|
pfree->is_free = BLOCK_ISFREE_FREE;
|
|
pfree->prev_free = freeh->prev_free;
|
|
pfree->next_free = freeh->next_free;
|
|
|
|
coffset = POINTER_OFFSET(segaddr, pfree);
|
|
(FREE_HEADER(segaddr, pfree->prev_free))->next_free = coffset;
|
|
(FREE_HEADER(segaddr, pfree->next_free))->prev_free = coffset;
|
|
*((size_t *)(((char *)(pfree + 1)) + pfree->size)) = pfree->size;
|
|
|
|
header->freecount--;
|
|
header->free_size += ALLOC_FREE_BLOCK_OVERHEAD;
|
|
|
|
combined = 1;
|
|
}
|
|
|
|
/* Check if previous block is free */
|
|
lastsize = *((size_t *)((char *)paddr - sizeof(alloc_used_header) - sizeof(size_t)));
|
|
freeh = (alloc_free_header *)((char *)paddr - sizeof(alloc_used_header) - ALLOC_FREE_BLOCK_OVERHEAD - lastsize);
|
|
|
|
_ASSERT(freeh->size == lastsize);
|
|
if(freeh->is_free == BLOCK_ISFREE_FREE)
|
|
{
|
|
/* Block before block getting freed is free. Combine with that */
|
|
if(combined == 0)
|
|
{
|
|
/* Just need to expand the size of this block */
|
|
freeh->size += usedh->size + ALLOC_USED_BLOCK_OVERHEAD;
|
|
}
|
|
else
|
|
{
|
|
/* We already combined next block with block getting freed */
|
|
pfree = (alloc_free_header *)usedh;
|
|
freeh->size += pfree->size + ALLOC_FREE_BLOCK_OVERHEAD;
|
|
|
|
freeh->next_free = pfree->next_free;
|
|
pfree = FREE_HEADER(segaddr, pfree->next_free);
|
|
pfree->prev_free = POINTER_OFFSET(segaddr, freeh);
|
|
}
|
|
|
|
*((size_t *)(((char *)(freeh + 1)) + freeh->size)) = freeh->size;
|
|
|
|
header->freecount--;
|
|
header->free_size += ALLOC_FREE_BLOCK_OVERHEAD;
|
|
|
|
combined = 1;
|
|
}
|
|
|
|
if(combined == 0)
|
|
{
|
|
/* New free block around two used blocks or around */
|
|
/* used and first block or around used and last block */
|
|
/* Find closest free block or first/last if closest */
|
|
usedh = (alloc_used_header *)((char *)paddr - sizeof(alloc_used_header));
|
|
freeh = (alloc_free_header *)((char *)usedh + usedh->size + ALLOC_USED_BLOCK_OVERHEAD);
|
|
while(freeh->is_free == BLOCK_ISFREE_USED)
|
|
{
|
|
/* Keep moving ahead unless free or end block is not found */
|
|
freeh = (alloc_free_header *)((char *)freeh + freeh->size + ALLOC_USED_BLOCK_OVERHEAD);
|
|
}
|
|
|
|
_ASSERT(freeh->is_free == BLOCK_ISFREE_FREE || freeh->is_free == BLOCK_ISFREE_LAST);
|
|
if (freeh->is_free != BLOCK_ISFREE_FREE && freeh->is_free != BLOCK_ISFREE_LAST)
|
|
{
|
|
dprintcritical("free_memory: Free block %p is not free in segment %p (size %d) - CORRUPTION IMMINENT",
|
|
freeh, segaddr, palloc->size);
|
|
|
|
utils_get_filename_and_line(&filename, &lineno);
|
|
EventWriteMemCombineNonFreeBlock(freeh, palloc, filename, lineno);
|
|
}
|
|
|
|
pfree = (alloc_free_header *)usedh;
|
|
pfree->is_free = BLOCK_ISFREE_FREE;
|
|
pfree->next_free = POINTER_OFFSET(segaddr, freeh);
|
|
pfree->prev_free = freeh->prev_free;
|
|
|
|
freeh = FREE_HEADER(segaddr, pfree->prev_free);
|
|
freeh->next_free = POINTER_OFFSET(segaddr, pfree);
|
|
|
|
freeh = FREE_HEADER(segaddr, pfree->next_free);
|
|
freeh->prev_free = POINTER_OFFSET(segaddr, pfree);
|
|
}
|
|
|
|
Finished:
|
|
|
|
if(flock)
|
|
{
|
|
lock_unlock(palloc->lock);
|
|
flock = 0;
|
|
}
|
|
|
|
dprintdecorate("end free_memory");
|
|
|
|
return;
|
|
}
|
|
|
|
static size_t get_memory_size(alloc_context * palloc, void * addr)
|
|
{
|
|
size_t size = 0;
|
|
alloc_used_header * usedh = NULL;
|
|
|
|
dprintverbose("start get_memory_size");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(addr != NULL);
|
|
|
|
lock_lock(palloc->lock);
|
|
|
|
usedh = (alloc_used_header *)((char *)addr - sizeof(alloc_used_header));
|
|
_ASSERT(usedh->is_free == BLOCK_ISFREE_USED);
|
|
|
|
size = usedh->size;
|
|
lock_unlock(palloc->lock);
|
|
|
|
dprintverbose("end get_memory_size");
|
|
return size;
|
|
}
|
|
|
|
static void * alloc_malloc(alloc_context * palloc, unsigned int type, size_t size)
|
|
{
|
|
void * result = NULL;
|
|
|
|
_ASSERT(size >= 0);
|
|
|
|
switch(type)
|
|
{
|
|
case ALLOC_TYPE_SHAREDMEM:
|
|
if(FAILED(allocate_memory(palloc, size, &result)))
|
|
{
|
|
return NULL;
|
|
}
|
|
break;
|
|
case ALLOC_TYPE_PROCESS:
|
|
result = pemalloc(size, 0);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS_PERSISTENT:
|
|
result = pemalloc(size, 1);
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void * alloc_realloc(alloc_context * palloc, unsigned int type, void * addr, size_t size)
|
|
{
|
|
void * result = NULL;
|
|
size_t osize = 0;
|
|
|
|
_ASSERT(addr != NULL);
|
|
_ASSERT(size >= 0);
|
|
|
|
switch(type)
|
|
{
|
|
case ALLOC_TYPE_SHAREDMEM:
|
|
if(FAILED(allocate_memory(palloc, size, &result)))
|
|
{
|
|
return NULL;
|
|
}
|
|
osize = get_memory_size(palloc, addr);
|
|
if(size < osize)
|
|
{
|
|
osize = size;
|
|
}
|
|
memcpy_s(result, osize, addr, osize);
|
|
free_memory(palloc, addr);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS:
|
|
result = perealloc(addr, size, 0);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS_PERSISTENT:
|
|
result = perealloc(addr, size, 1);
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static char * alloc_strdup(alloc_context * palloc, unsigned int type, const char * str)
|
|
{
|
|
char * result = NULL;
|
|
size_t strl = 0;
|
|
|
|
_ASSERT(str != NULL);
|
|
strl = strlen(str) + 1;
|
|
|
|
switch(type)
|
|
{
|
|
case ALLOC_TYPE_SHAREDMEM:
|
|
if(FAILED(allocate_memory(palloc, strl, &result)))
|
|
{
|
|
return NULL;
|
|
}
|
|
memcpy_s(result, strl, str, strl);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS:
|
|
result = pestrdup(str, 0);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS_PERSISTENT:
|
|
result = pestrdup(str, 1);
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void alloc_free(alloc_context * palloc, unsigned int type, void * addr)
|
|
{
|
|
_ASSERT(addr != NULL);
|
|
|
|
switch(type)
|
|
{
|
|
case ALLOC_TYPE_SHAREDMEM:
|
|
free_memory(palloc, addr);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS:
|
|
pefree(addr, 0);
|
|
break;
|
|
case ALLOC_TYPE_PROCESS_PERSISTENT:
|
|
pefree(addr, 1);
|
|
break;
|
|
default:
|
|
_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int alloc_create(alloc_context ** ppalloc)
|
|
{
|
|
int result = NONFATAL;
|
|
alloc_context * palloc = NULL;
|
|
|
|
dprintverbose("start alloc_create");
|
|
_ASSERT(ppalloc != NULL);
|
|
|
|
*ppalloc = NULL;
|
|
|
|
/* Allocate memory for context */
|
|
palloc = (alloc_context *)alloc_pemalloc(sizeof(alloc_context));
|
|
if(palloc == NULL)
|
|
{
|
|
result = FATAL_OUT_OF_LMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
/* Initialize member variables */
|
|
palloc->id = gallocid++;
|
|
palloc->islocal = 0;
|
|
palloc->hinitdone = NULL;
|
|
palloc->memaddr = NULL;
|
|
palloc->size = 0;
|
|
palloc->lock = NULL;
|
|
palloc->header = NULL;
|
|
palloc->localheap = 0;
|
|
|
|
*ppalloc = palloc;
|
|
|
|
Finished:
|
|
|
|
if(FAILED(result))
|
|
{
|
|
dprintimportant("failure %d in alloc_create", result);
|
|
}
|
|
|
|
dprintverbose("end alloc_create");
|
|
return result;
|
|
}
|
|
|
|
void alloc_destroy(alloc_context * palloc)
|
|
{
|
|
dprintverbose("start alloc_destroy");
|
|
|
|
if(palloc != NULL)
|
|
{
|
|
alloc_pefree(palloc);
|
|
palloc = NULL;
|
|
}
|
|
|
|
dprintverbose("end alloc_destroy");
|
|
|
|
return;
|
|
}
|
|
|
|
/* initmemory should be 1 for all non file backed shared memory allocators and 0 */
|
|
/* for file backed shared memory allocators when filemap->existing is set to 1 */
|
|
int alloc_initialize(alloc_context * palloc, unsigned short islocal, char * name, unsigned short cachekey, void * staddr, size_t size, unsigned char initmemory)
|
|
{
|
|
int result = NONFATAL;
|
|
unsigned short locktype = LOCK_TYPE_SHARED;
|
|
alloc_segment_header * header = NULL;
|
|
alloc_free_header * frestart = NULL;
|
|
alloc_free_header * fremid = NULL;
|
|
alloc_free_header * freend = NULL;
|
|
unsigned char isfirst = 1;
|
|
DWORD ret = 0;
|
|
|
|
dprintverbose("start alloc_initialize");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(cachekey != 0);
|
|
_ASSERT(staddr != NULL);
|
|
_ASSERT(size > 0);
|
|
|
|
palloc->localheap = WCG(localheap);
|
|
palloc->memaddr = staddr;
|
|
palloc->size = size;
|
|
palloc->header = (alloc_segment_header *)staddr;
|
|
header = palloc->header;
|
|
|
|
result = lock_create(&palloc->lock);
|
|
if(FAILED(result))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
if(islocal)
|
|
{
|
|
locktype = LOCK_TYPE_LOCAL;
|
|
palloc->islocal = islocal;
|
|
}
|
|
|
|
result = lock_initialize(palloc->lock, name, cachekey, locktype, &header->last_owner);
|
|
if(FAILED(result))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
result = utils_create_init_event(palloc->lock->nameprefix, "ALLOC_INIT", &palloc->hinitdone, &isfirst);
|
|
if (FAILED(result))
|
|
{
|
|
result = FATAL_ALLOC_INIT_EVENT;
|
|
goto Finished;
|
|
}
|
|
|
|
/* If the segment is not initialized, set */
|
|
/* header on the shared memory segment start */
|
|
if(islocal || isfirst)
|
|
{
|
|
/* No need to get a lock as other processes */
|
|
/* are blocked waiting for hinitdone event */
|
|
|
|
if(initmemory)
|
|
{
|
|
/* Put the start header of size 0 */
|
|
/* followed by the first free block of size free_size */
|
|
frestart = (alloc_free_header *)(header + 1);
|
|
fremid = (alloc_free_header *)((char *)frestart + ALLOC_FREE_BLOCK_OVERHEAD);
|
|
|
|
/* Set start of doubly circular linked list */
|
|
frestart->size = 0;
|
|
frestart->is_free = BLOCK_ISFREE_FIRST;
|
|
frestart->prev_free = 0;
|
|
frestart->next_free = POINTER_OFFSET(staddr, fremid);
|
|
*((size_t *)((char *)(frestart + 1))) = frestart->size;
|
|
|
|
/* Set mid of doubly linked list */
|
|
fremid->size = size - ALLOC_SEGMENT_HEADER_SIZE - ALLOC_FREE_BLOCK_OVERHEAD - ALLOC_FREE_BLOCK_OVERHEAD - ALLOC_FREE_BLOCK_OVERHEAD;
|
|
freend = (alloc_free_header *)((char *)fremid + ALLOC_FREE_BLOCK_OVERHEAD + fremid->size);
|
|
|
|
fremid->is_free = BLOCK_ISFREE_FREE;
|
|
fremid->prev_free = POINTER_OFFSET(staddr, frestart);
|
|
fremid->next_free = POINTER_OFFSET(staddr, freend);
|
|
*((size_t *)(((char *)(fremid + 1)) + fremid->size)) = fremid->size;
|
|
|
|
freend->size = 0;
|
|
freend->is_free = BLOCK_ISFREE_LAST;
|
|
freend->next_free = 0;
|
|
freend->prev_free = POINTER_OFFSET(staddr, fremid);
|
|
*((size_t *)((char *)(freend + 1))) = freend->size;
|
|
|
|
/* Set segment header */
|
|
header->total_size = size;
|
|
header->free_size = fremid->size;
|
|
|
|
header->cacheheader1 = 0;
|
|
header->cacheheader2 = 0;
|
|
header->usedcount = 0;
|
|
header->freecount = 1;
|
|
}
|
|
|
|
header->mapcount = 1;
|
|
}
|
|
else
|
|
{
|
|
/* TODO: perform integrity check of segment. If it fails, re-init. */
|
|
InterlockedIncrement(&header->mapcount);
|
|
}
|
|
|
|
_ASSERT(SUCCEEDED(result));
|
|
|
|
Finished:
|
|
|
|
if (palloc->hinitdone)
|
|
{
|
|
ReleaseMutex(palloc->hinitdone);
|
|
}
|
|
|
|
if(FAILED(result))
|
|
{
|
|
dprintimportant("failure %d in alloc_initialize", result);
|
|
|
|
if(palloc != NULL)
|
|
{
|
|
if(palloc->lock != NULL)
|
|
{
|
|
lock_terminate(palloc->lock);
|
|
lock_destroy(palloc->lock);
|
|
|
|
palloc->lock = NULL;
|
|
}
|
|
|
|
if(palloc->hinitdone != NULL)
|
|
{
|
|
CloseHandle(palloc->hinitdone);
|
|
palloc->hinitdone = NULL;
|
|
}
|
|
|
|
palloc->memaddr = NULL;
|
|
palloc->size = 0;
|
|
palloc->header = NULL;
|
|
}
|
|
}
|
|
|
|
dprintverbose("end alloc_initialize");
|
|
|
|
return result;
|
|
}
|
|
|
|
void alloc_terminate(alloc_context * palloc)
|
|
{
|
|
dprintverbose("start alloc_terminate");
|
|
|
|
if(palloc != NULL)
|
|
{
|
|
if(palloc->header != NULL)
|
|
{
|
|
InterlockedDecrement(&palloc->header->mapcount);
|
|
palloc->header = NULL;
|
|
}
|
|
|
|
if(palloc->lock != NULL)
|
|
{
|
|
lock_terminate(palloc->lock);
|
|
lock_destroy(palloc->lock);
|
|
|
|
palloc->lock = NULL;
|
|
}
|
|
|
|
if(palloc->hinitdone != NULL)
|
|
{
|
|
CloseHandle(palloc->hinitdone);
|
|
palloc->hinitdone = NULL;
|
|
}
|
|
|
|
palloc->memaddr = NULL;
|
|
palloc->size = 0;
|
|
}
|
|
|
|
dprintverbose("end alloc_terminate");
|
|
|
|
return;
|
|
}
|
|
|
|
int alloc_create_mpool(alloc_context * palloc, size_t * phoffset)
|
|
{
|
|
int result = NONFATAL;
|
|
alloc_mpool_header * header = NULL;
|
|
|
|
dprintverbose("start alloc_create_mpool");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(phoffset != NULL);
|
|
|
|
*phoffset = 0;
|
|
|
|
header = (alloc_mpool_header *)alloc_smalloc(palloc, sizeof(alloc_mpool_header));
|
|
if(header == NULL)
|
|
{
|
|
result = FATAL_OUT_OF_MEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
header->foffset = 0;
|
|
header->loffset = 0;
|
|
header->socurr = 0;
|
|
header->mocurr = 0;
|
|
header->locurr = 0;
|
|
|
|
*phoffset = POINTER_OFFSET(palloc->memaddr, header);
|
|
|
|
Finished:
|
|
|
|
if(FAILED(result))
|
|
{
|
|
dprintimportant("failure %d in alloc_create_mpool", result);
|
|
}
|
|
|
|
dprintverbose("end alloc_create_mpool");
|
|
|
|
return result;
|
|
}
|
|
|
|
void alloc_free_mpool(alloc_context * palloc, size_t hoffset)
|
|
{
|
|
void * pvoid = NULL;
|
|
alloc_mpool_header * header = NULL;
|
|
alloc_mpool_segment * pchunk = NULL;
|
|
alloc_mpool_segment * ptemp = NULL;
|
|
|
|
dprintverbose("start alloc_free_mpool");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset > 0);
|
|
|
|
header = (alloc_mpool_header *)((char *)palloc->memaddr + hoffset);
|
|
if(header->foffset > 0)
|
|
{
|
|
pchunk = (alloc_mpool_segment *)((char *)palloc->memaddr + header->foffset);
|
|
while(pchunk != NULL)
|
|
{
|
|
ptemp = pchunk;
|
|
pchunk = ((pchunk->next == 0) ? NULL : (alloc_mpool_segment *)((char *)palloc->memaddr + pchunk->next));
|
|
|
|
if(ptemp->aoffset != 0)
|
|
{
|
|
pvoid = (void *)((char *)palloc->memaddr + ptemp->aoffset);
|
|
|
|
alloc_sfree(palloc, pvoid);
|
|
ptemp->aoffset = 0;
|
|
}
|
|
|
|
alloc_sfree(palloc, ptemp);
|
|
ptemp = NULL;
|
|
}
|
|
}
|
|
|
|
alloc_sfree(palloc, header);
|
|
header = NULL;
|
|
|
|
dprintverbose("end alloc_free_mpool");
|
|
return;
|
|
}
|
|
|
|
void * alloc_get_cacheheader(alloc_context * palloc, unsigned int msize, unsigned int type)
|
|
{
|
|
void * pvoid = NULL;
|
|
alloc_segment_header * header = NULL;
|
|
size_t * pcvalue = NULL;
|
|
|
|
dprintverbose("start alloc_get_cacheheader");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(msize > 0);
|
|
|
|
header = palloc->header;
|
|
|
|
switch(type)
|
|
{
|
|
case CACHE_TYPE_FILELIST:
|
|
case CACHE_TYPE_RESPATHS:
|
|
case CACHE_TYPE_FILECONTENT:
|
|
case CACHE_TYPE_BYTECODES:
|
|
case CACHE_TYPE_USERZVALS:
|
|
case CACHE_TYPE_SESSZVALS:
|
|
pcvalue = &header->cacheheader1;
|
|
break;
|
|
|
|
case CACHE_TYPE_FCNOTIFY:
|
|
pcvalue = &header->cacheheader2;
|
|
break;
|
|
|
|
default:
|
|
_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
if(*pcvalue != 0)
|
|
{
|
|
pvoid = (void *)((char *)palloc->memaddr + (*pcvalue));
|
|
goto Finished;
|
|
}
|
|
|
|
if(FAILED(allocate_memory(palloc, msize, &pvoid)))
|
|
{
|
|
pvoid = NULL;
|
|
goto Finished;
|
|
}
|
|
|
|
lock_lock(palloc->lock);
|
|
if(*pcvalue != 0)
|
|
{
|
|
/* Some other process allocated before this process could do */
|
|
lock_unlock(palloc->lock);
|
|
free_memory(palloc, pvoid);
|
|
pvoid = (void *)((char *)palloc->memaddr + (*pcvalue));
|
|
}
|
|
else
|
|
{
|
|
*pcvalue = POINTER_OFFSET(palloc->memaddr, pvoid);
|
|
lock_unlock(palloc->lock);
|
|
}
|
|
|
|
Finished:
|
|
|
|
dprintverbose("end alloc_get_cacheheader");
|
|
return pvoid;
|
|
}
|
|
|
|
__inline
|
|
void * alloc_get_cachevalue(alloc_context * palloc, size_t offset)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(palloc->memaddr != NULL);
|
|
|
|
if(offset != 0 && offset < palloc->size)
|
|
{
|
|
return (void *)((char *)palloc->memaddr + offset);
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
__inline
|
|
size_t alloc_get_valueoffset(alloc_context * palloc, void * pvalue)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(palloc->memaddr != NULL);
|
|
|
|
if(pvalue != NULL && pvalue >= palloc->memaddr)
|
|
{
|
|
return POINTER_OFFSET(palloc->memaddr, pvalue);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int alloc_getinfo(alloc_context * palloc, alloc_info ** ppinfo)
|
|
{
|
|
int result = NONFATAL;
|
|
alloc_info * pinfo = NULL;
|
|
|
|
dprintverbose("start alloc_getinfo");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(ppinfo != NULL);
|
|
|
|
*ppinfo = NULL;
|
|
|
|
pinfo = (alloc_info *)alloc_emalloc(sizeof(alloc_info));
|
|
if(pinfo == NULL)
|
|
{
|
|
result = FATAL_OUT_OF_LMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
lock_lock(palloc->lock);
|
|
|
|
pinfo->total_size = palloc->header->total_size;
|
|
pinfo->free_size = palloc->header->free_size;
|
|
pinfo->usedcount = palloc->header->usedcount;
|
|
pinfo->freecount = palloc->header->freecount;
|
|
pinfo->mem_overhead = (palloc->header->usedcount + palloc->header->freecount + 2) * sizeof(alloc_free_header);
|
|
|
|
lock_unlock(palloc->lock);
|
|
|
|
*ppinfo = pinfo;
|
|
_ASSERT(SUCCEEDED(result));
|
|
|
|
Finished:
|
|
|
|
if(FAILED(result))
|
|
{
|
|
dprintimportant("failure %d in alloc_getinfo", result);
|
|
|
|
if(pinfo != NULL)
|
|
{
|
|
alloc_efree(pinfo);
|
|
pinfo = NULL;
|
|
}
|
|
}
|
|
|
|
dprintverbose("end alloc_getinfo");
|
|
|
|
return result;
|
|
}
|
|
|
|
void alloc_freeinfo(alloc_info * pinfo)
|
|
{
|
|
if(pinfo != NULL)
|
|
{
|
|
alloc_efree(pinfo);
|
|
pinfo = NULL;
|
|
}
|
|
}
|
|
|
|
__inline
|
|
void * alloc_emalloc(size_t size)
|
|
{
|
|
return alloc_malloc(NULL, ALLOC_TYPE_PROCESS, size);
|
|
}
|
|
|
|
__inline
|
|
void * alloc_pemalloc(size_t size)
|
|
{
|
|
return alloc_malloc(NULL, ALLOC_TYPE_PROCESS_PERSISTENT, size);
|
|
}
|
|
|
|
__inline
|
|
void * alloc_smalloc(alloc_context * palloc, size_t size)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
return alloc_malloc(palloc, ALLOC_TYPE_SHAREDMEM, size);
|
|
}
|
|
|
|
__inline
|
|
void * alloc_erealloc(void * addr, size_t size)
|
|
{
|
|
return alloc_realloc(NULL, ALLOC_TYPE_PROCESS, addr, size);
|
|
}
|
|
|
|
__inline
|
|
void * alloc_perealloc(void * addr, size_t size)
|
|
{
|
|
return alloc_realloc(NULL, ALLOC_TYPE_PROCESS_PERSISTENT, addr, size);
|
|
}
|
|
|
|
__inline
|
|
void * alloc_srealloc(alloc_context * palloc, void * addr, size_t size)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
return alloc_realloc(palloc, ALLOC_TYPE_SHAREDMEM, addr, size);
|
|
}
|
|
|
|
__inline
|
|
char * alloc_estrdup(const char * str)
|
|
{
|
|
return alloc_strdup(NULL, ALLOC_TYPE_PROCESS, str);
|
|
}
|
|
|
|
__inline
|
|
char * alloc_pestrdup(const char * str)
|
|
{
|
|
return alloc_strdup(NULL, ALLOC_TYPE_PROCESS_PERSISTENT, str);
|
|
}
|
|
|
|
__inline
|
|
char * alloc_sstrdup(alloc_context * palloc, const char * str)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
return alloc_strdup(palloc, ALLOC_TYPE_SHAREDMEM, str);
|
|
}
|
|
|
|
__inline
|
|
void alloc_efree(void * addr)
|
|
{
|
|
alloc_free(NULL, ALLOC_TYPE_PROCESS, addr);
|
|
return;
|
|
}
|
|
|
|
__inline
|
|
void alloc_pefree(void * addr)
|
|
{
|
|
alloc_free(NULL, ALLOC_TYPE_PROCESS_PERSISTENT, addr);
|
|
return;
|
|
}
|
|
|
|
__inline
|
|
void alloc_sfree(alloc_context * palloc, void * addr)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
alloc_free(palloc, ALLOC_TYPE_SHAREDMEM, addr);
|
|
return;
|
|
}
|
|
|
|
void * alloc_oemalloc(alloc_context * palloc, size_t hoffset, size_t size)
|
|
{
|
|
_ASSERT(palloc == NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_emalloc(size);
|
|
}
|
|
|
|
void * alloc_osmalloc(alloc_context * palloc, size_t hoffset, size_t size)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_smalloc(palloc, size);
|
|
}
|
|
|
|
void * alloc_ommalloc(alloc_context * palloc, size_t hoffset, size_t size)
|
|
{
|
|
alloc_mpool_header * header = NULL;
|
|
alloc_mpool_segment * pchunk = NULL;
|
|
alloc_mpool_segment * ptemp = NULL;
|
|
|
|
void * addr = NULL;
|
|
void * pvoid = NULL;
|
|
size_t offset = 0;
|
|
size_t rsize = 0;
|
|
size_t bsize = 0;
|
|
unsigned char ctype = 0;
|
|
|
|
dprintdecorate("start alloc_ommalloc");
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset > 0);
|
|
_ASSERT(size > 0);
|
|
|
|
#ifdef _WIN64
|
|
rsize = ALIGNQWORD(size);
|
|
#else /* _WIN64 */
|
|
rsize = ALIGNDWORD(size);
|
|
#endif /* _WIN64 */
|
|
header = (alloc_mpool_header *)((char *)palloc->memaddr + hoffset);
|
|
|
|
if(rsize <= POOL_ALLOCATION_SMALL_MAX)
|
|
{
|
|
offset = header->socurr;
|
|
bsize = POOL_ALLOCATION_SMALL_BSIZE;
|
|
ctype = POOL_ALLOCATION_TYPE_SMALL;
|
|
}
|
|
else if(rsize <= POOL_ALLOCATION_MEDIUM_MAX)
|
|
{
|
|
offset = header->mocurr;
|
|
bsize = POOL_ALLOCATION_MEDIUM_BSIZE;
|
|
ctype = POOL_ALLOCATION_TYPE_MEDIUM;
|
|
}
|
|
else if(rsize <= POOL_ALLOCATION_LARGE_MAX)
|
|
{
|
|
offset = header->locurr;
|
|
bsize = POOL_ALLOCATION_LARGE_BSIZE;
|
|
ctype = POOL_ALLOCATION_TYPE_LARGE;
|
|
}
|
|
else
|
|
{
|
|
offset = 0;
|
|
bsize = rsize;
|
|
ctype = POOL_ALLOCATION_TYPE_HUGE;
|
|
}
|
|
|
|
/* Check if chunk has enough space for this allocation */
|
|
/* Else we need to allocate a new chunk and use that */
|
|
if(offset > 0)
|
|
{
|
|
pchunk = (alloc_mpool_segment *)((char *)palloc->memaddr + offset);
|
|
if(pchunk->size - pchunk->position < rsize)
|
|
{
|
|
offset = 0;
|
|
pchunk = NULL;
|
|
}
|
|
}
|
|
|
|
if(offset == 0)
|
|
{
|
|
pchunk = (alloc_mpool_segment *)alloc_smalloc(palloc, sizeof(alloc_mpool_segment));
|
|
if(pchunk == NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
/* Initialize pchunk */
|
|
pchunk->aoffset = 0;
|
|
pchunk->size = 0;
|
|
pchunk->position = 0;
|
|
pchunk->next = 0;
|
|
|
|
pvoid = alloc_smalloc(palloc, bsize);
|
|
if(pvoid == NULL)
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
/* Update aoffset to offset of allocated memory */
|
|
/* Also update size after allocating block */
|
|
pchunk->aoffset = POINTER_OFFSET(palloc->memaddr, pvoid);
|
|
pchunk->size = bsize;
|
|
|
|
offset = POINTER_OFFSET(palloc->memaddr, pchunk);
|
|
if(header->foffset == 0)
|
|
{
|
|
_ASSERT(header->loffset == 0);
|
|
|
|
header->foffset = offset;
|
|
header->loffset = offset;
|
|
}
|
|
else
|
|
{
|
|
ptemp = (alloc_mpool_segment *)((char *)palloc->memaddr + header->loffset);
|
|
ptemp->next = offset;
|
|
header->loffset = offset;
|
|
}
|
|
|
|
switch(ctype)
|
|
{
|
|
case POOL_ALLOCATION_TYPE_SMALL:
|
|
header->socurr = offset;
|
|
break;
|
|
case POOL_ALLOCATION_TYPE_MEDIUM:
|
|
header->mocurr = offset;
|
|
break;
|
|
case POOL_ALLOCATION_TYPE_LARGE:
|
|
header->locurr = offset;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pchunk = (alloc_mpool_segment *)((char *)palloc->memaddr + offset);
|
|
}
|
|
|
|
_ASSERT(pchunk != NULL);
|
|
_ASSERT(pchunk->size - pchunk->position >= rsize);
|
|
|
|
addr = (void *)((char *)palloc->memaddr + pchunk->aoffset + pchunk->position);
|
|
pchunk->position += rsize;
|
|
|
|
pchunk = NULL;
|
|
|
|
Finished:
|
|
|
|
if(pchunk != NULL)
|
|
{
|
|
if(pchunk->aoffset != 0)
|
|
{
|
|
ptemp = (alloc_mpool_segment *)((char *)palloc->memaddr + pchunk->aoffset);
|
|
alloc_sfree(palloc, ptemp);
|
|
pchunk->aoffset = 0;
|
|
}
|
|
|
|
alloc_sfree(palloc, pchunk);
|
|
pchunk = NULL;
|
|
}
|
|
|
|
dprintdecorate("end alloc_ommalloc");
|
|
return addr;
|
|
}
|
|
|
|
void * alloc_oerealloc(alloc_context * palloc, size_t hoffset, void * addr, size_t size)
|
|
{
|
|
_ASSERT(palloc == NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_erealloc(addr, size);
|
|
}
|
|
|
|
void * alloc_osrealloc(alloc_context * palloc, size_t hoffset, void * addr, size_t size)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_srealloc(palloc, addr, size);
|
|
}
|
|
|
|
void * alloc_omrealloc(alloc_context * palloc, size_t hoffset, void * addr, size_t size)
|
|
{
|
|
/* When using pool memory, we don't know the size of a */
|
|
/* particular block allocation. So we can't really do a realloc */
|
|
_ASSERT(FALSE);
|
|
return NULL;
|
|
}
|
|
|
|
char * alloc_oestrdup(alloc_context * palloc, size_t hoffset, const char * str)
|
|
{
|
|
_ASSERT(palloc == NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_estrdup(str);
|
|
}
|
|
|
|
char * alloc_osstrdup(alloc_context * palloc, size_t hoffset, const char * str)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
return alloc_sstrdup(palloc, str);
|
|
}
|
|
|
|
char * alloc_omstrdup(alloc_context * palloc, size_t hoffset, const char * str)
|
|
{
|
|
char * memaddr = NULL;
|
|
size_t strl = 0;
|
|
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset > 0);
|
|
_ASSERT(str != NULL);
|
|
|
|
strl = strlen(str) + 1;
|
|
memaddr = (char *)alloc_ommalloc(palloc, hoffset, strl);
|
|
if(memaddr == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memcpy_s(memaddr, strl, str, strl);
|
|
return memaddr;
|
|
}
|
|
|
|
void alloc_oefree(alloc_context * palloc, size_t hoffset, void * addr)
|
|
{
|
|
_ASSERT(palloc == NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
alloc_efree(addr);
|
|
return;
|
|
}
|
|
|
|
void alloc_osfree(alloc_context * palloc, size_t hoffset, void * addr)
|
|
{
|
|
_ASSERT(palloc != NULL);
|
|
_ASSERT(hoffset == 0);
|
|
|
|
alloc_sfree(palloc, addr);
|
|
return;
|
|
}
|
|
|
|
void alloc_omfree(alloc_context * palloc, size_t hoffset, void * addr)
|
|
{
|
|
/* Nothing to do. We will free memory in the pool in one go */
|
|
return;
|
|
}
|
|
|
|
void alloc_runtest()
|
|
{
|
|
int result = NONFATAL;
|
|
void * memaddr = NULL;
|
|
unsigned char islocal = 0;
|
|
|
|
void * mem1 = NULL;
|
|
void * mem2 = NULL;
|
|
void * mem3 = NULL;
|
|
void * mem4 = NULL;
|
|
|
|
size_t freesiz = 0;
|
|
|
|
alloc_context * palloc = NULL;
|
|
alloc_segment_header * header = NULL;
|
|
alloc_free_header * freeh = NULL;
|
|
alloc_used_header * usedh = NULL;
|
|
|
|
dprintverbose("*** STARTING ALLOC TESTS ***");
|
|
|
|
memaddr = malloc(4096);
|
|
if(memaddr == NULL)
|
|
{
|
|
result = FATAL_OUT_OF_LMEMORY;
|
|
goto Finished;
|
|
}
|
|
|
|
result = alloc_create(&palloc);
|
|
if(FAILED(result))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
result = alloc_initialize(palloc, islocal, "ALLOC_TEST", 1, memaddr, 4096, 1);
|
|
if(FAILED(result))
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
/* Verify alloc_context, alloc_segment_header and free blocks */
|
|
_ASSERT(palloc->lock != NULL);
|
|
_ASSERT(palloc->size == 4096);
|
|
_ASSERT(palloc->header != NULL);
|
|
_ASSERT(palloc->memaddr != NULL);
|
|
|
|
freesiz = palloc->header->free_size;
|
|
|
|
/* Allocate 128 bytes 4 times and verify segment header, */
|
|
/* free blocks and used blocks */
|
|
mem1 = alloc_smalloc(palloc, 128);
|
|
freesiz = freesiz - ALLOC_USED_BLOCK_OVERHEAD - 128;
|
|
|
|
_ASSERT(mem1 != NULL);
|
|
_ASSERT(POINTER_OFFSET(memaddr, mem1) < 4096);
|
|
_ASSERT(palloc->header->usedcount == 1);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
_ASSERT(palloc->header->free_size == freesiz);
|
|
|
|
mem2 = alloc_smalloc(palloc, 128);
|
|
freesiz = freesiz - ALLOC_USED_BLOCK_OVERHEAD - 128;
|
|
|
|
_ASSERT(mem2 != NULL);
|
|
_ASSERT(POINTER_OFFSET(memaddr, mem2) < 4096);
|
|
_ASSERT(palloc->header->usedcount == 2);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
_ASSERT(palloc->header->free_size == freesiz);
|
|
|
|
mem3 = alloc_smalloc(palloc, 128);
|
|
freesiz = freesiz - ALLOC_USED_BLOCK_OVERHEAD - 128;
|
|
|
|
_ASSERT(mem3 != NULL);
|
|
_ASSERT(POINTER_OFFSET(memaddr, mem3) < 4096);
|
|
_ASSERT(palloc->header->usedcount == 3);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
_ASSERT(palloc->header->free_size == freesiz);
|
|
|
|
mem4 = alloc_smalloc(palloc, 128);
|
|
freesiz = freesiz - ALLOC_USED_BLOCK_OVERHEAD - 128;
|
|
|
|
_ASSERT(mem4 != NULL);
|
|
_ASSERT(POINTER_OFFSET(memaddr, mem4) < 4096);
|
|
_ASSERT(palloc->header->usedcount == 4);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
_ASSERT(palloc->header->free_size == freesiz);
|
|
|
|
/* Verify that free and allocating again reuse the same address */
|
|
alloc_sfree(palloc, mem4);
|
|
_ASSERT(mem4 == alloc_smalloc(palloc, 128));
|
|
|
|
/* Free 3rd memory block and verify everything */
|
|
/* Free 4th memory block and verify everything */
|
|
/* Free 1st memory block and verify everything */
|
|
/* Free 2nd memory block and verify everything */
|
|
alloc_sfree(palloc, mem3);
|
|
_ASSERT(palloc->header->usedcount == 3);
|
|
_ASSERT(palloc->header->freecount == 2);
|
|
|
|
alloc_sfree(palloc, mem4);
|
|
_ASSERT(palloc->header->usedcount == 2);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
|
|
alloc_sfree(palloc, mem1);
|
|
_ASSERT(palloc->header->usedcount == 1);
|
|
_ASSERT(palloc->header->freecount == 2);
|
|
|
|
alloc_sfree(palloc, mem2);
|
|
_ASSERT(palloc->header->usedcount == 0);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
|
|
/* Allocate free_size number of bytes so that memory is full */
|
|
/* Try allocating another block and verify error */
|
|
/* Free block and allocate 1024 bytes twice and verify */
|
|
/* Free one block. Verify. Free other block and verify */
|
|
mem1 = alloc_smalloc(palloc, palloc->header->free_size);
|
|
mem2 = alloc_smalloc(palloc, 128);
|
|
|
|
_ASSERT(mem2 == NULL);
|
|
_ASSERT(palloc->header->usedcount == 1);
|
|
|
|
alloc_sfree(palloc, mem1);
|
|
mem2 = alloc_smalloc(palloc, 1024);
|
|
mem3 = alloc_smalloc(palloc, 1024);
|
|
|
|
alloc_sfree(palloc, mem2);
|
|
_ASSERT(palloc->header->usedcount == 1);
|
|
|
|
alloc_sfree(palloc, mem3);
|
|
|
|
_ASSERT(palloc->header->usedcount == 0);
|
|
_ASSERT(palloc->header->freecount == 1);
|
|
|
|
_ASSERT(SUCCEEDED(result));
|
|
|
|
Finished:
|
|
|
|
if(palloc != NULL)
|
|
{
|
|
alloc_terminate(palloc);
|
|
alloc_destroy(palloc);
|
|
|
|
palloc = NULL;
|
|
}
|
|
|
|
if(memaddr != NULL)
|
|
{
|
|
free(memaddr);
|
|
memaddr = NULL;
|
|
}
|
|
|
|
dprintverbose("*** ENDING ALLOC TESTS ***");
|
|
|
|
return;
|
|
}
|