/* Duplicate a handle for usage in a child process, * and write the child process instance * of the handle to the parameter file. */ Bool getDuplicatedHandle( void* self, HANDLE* dest, HANDLE src, HANDLE childProcess) { IProcessManager _ = (IProcessManager)self; IErrorLogger elog = _->errorLogger; HANDLE hChild = INVALID_HANDLE_VALUE; if (!DuplicateHandle(GetCurrentProcess(), src, childProcess, &hChild, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { elog->log(LOG_LOG, ERROR_CODE_DUPLICATE_HANDLE_FAILED, "could not duplicate handle to be written to backend parameter file: error code %lu", GetLastError()); return False; } *dest = hChild; return True; }
TThread startThread( void* self, THREAD_FUNC func, void* param, TThreadId threadid) { IThreadHelper _ = (IThreadHelper)self; IErrorLogger elog = _->errorLogger; TThread threadHandle = CreateThread( NULL, 0, func, param, 0, &threadid); if (threadHandle == NULL) elog->log(LOG_ERROR, ERROR_CODE_CREATE_THREAD_FAILED, "could not create a thread: error code %lu", GetLastError()); return threadHandle; }
void waitForMultipleEvents( void* self, TEvent* eventsToWait, int eventsCount, Bool waitAll) { IThreadHelper _ = (IThreadHelper)self; IErrorLogger elog = _->errorLogger; DWORD result; result = WaitForMultipleObjects( eventsCount, eventsToWait, waitAll, INFINITE); switch (result) { /* In this case we have successfully waited for the event. */ case WAIT_OBJECT_0: elog->log(LOG_LOG, 0, "Successfully waited for the events"); break; /* When we are here, probably some error has happened. */ default: elog->log(LOG_ERROR, ERROR_CODE_WAIT_FOR_SINGLE_OBJECT_FAILED, "Wait for single object failed: error code %lu", GetLastError()); break; } }
Latch initLatch(void* self) { ILatchManager _ = (ILatchManager)self; IErrorLogger elog = _->errorLogger; IMemoryManager mm = _->memManager; SECURITY_ATTRIBUTES sa; Latch latch; latch = (Latch)mm->alloc(sizeof(SLatch)); ASSERT_VOID(elog, latch != NULL); latch->isset = False; latch->ownerid = 0; latch->shared = True; memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = True; latch->event = CreateEvent(&sa, TRUE, FALSE, NULL); if (latch->event == NULL) elog->log(LOG_ERROR, ERROR_CODE_GENERAL, "CreateEvent failed: error code %lu", GetLastError()); return latch; }
BackendParams restoreBackendParamsFromSharedMemory(void* self) { IProcessManager _ = (IProcessManager)self; IErrorLogger elog = _->errorLogger; IMemoryManager memMan = _->memManager; BackendParams paramSm; BackendParams paramSmCpy = (BackendParams)memMan->alloc(sizeof(SBackendParams)); HANDLE hMapFile; IMemoryManager memManager; hMapFile = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, sharedMemParamsName); if (hMapFile == NULL) elog->log(LOG_ERROR, ERROR_CODE_FILE_OPEN_MAPPING_FAILED, "could not open file mapping object: error code %lu", GetLastError()); /* Maps a view of a file mapping into the address space of a calling process. */ paramSm = (BackendParams) MapViewOfFile( hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SBackendParams)); if (!paramSm) { elog->log(LOG_ERROR, ERROR_CODE_MAP_MEMORY_TO_FILE, "could not map backend parameter memory: error code %lu", GetLastError()); CloseHandle(paramSm); return -1; } memcpy(paramSmCpy, paramSm, sizeof(SBackendParams)); if (!UnmapViewOfFile(paramSm)) elog->log(LOG_ERROR, ERROR_CODE_UNMAP_VIEW_OF_FILE, "could not unmap view of backend parameter file: error code %lu", GetLastError()); if (!CloseHandle(hMapFile)) elog->log(LOG_ERROR, ERROR_CODE_CLOSE_HANDLER_FAILED, "could not close handle to backend parameter file: error code %lu", GetLastError()); return paramSmCpy; }
/* This is an auxiliary function for allocateMemory method. * It allocates a whole new block, inserts it into * the head of the set's block list * and takes the whole memory from the entire block * for a chunk. */ void* allocateChunkBlock( void* self, size_t chunkSize, MemoryContainer container) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = (IErrorLogger)_->errorLogger; size_t blockSize = chunkSize + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; MemorySet set = (MemorySet)container; MemoryBlock block; MemoryChunk chunk; void* chunkPtr; size_t size = AlignDefault(chunkSize); ASSERT(elog, funcMalloc != NULL, NULL); block = (MemoryBlock)funcMalloc(blockSize); /* Report malloc error */ if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", chunkSize); } block->memset = set; block->freeStart = (char*)block + blockSize; block->freeEnd = block->freeStart; chunk = (MemoryChunk)((char*)block + MEM_BLOCK_SIZE); chunk->memsetorchunk = set; chunk->size = size; chunk->sizeRequested = chunkSize; chunkPtr = MemoryChunkGetPointer(chunk); /* Insert the block into the head of * the set's block list. */ if (set->blockList != NULL) { block->next = set->blockList->next; set->blockList->next = block; return chunkPtr; } block->next = NULL; set->blockList = block; return chunkPtr; }
/* Set a privilege to a token. */ BOOL SetPrivilege( void* self, HANDLE hToken, /* access token handle */ LPCTSTR lpszPrivilege, /* name of privilege to enable/disable */ BOOL bEnablePrivilege) /* to enable or disable privilege */ { IProcessManager _ = (IProcessManager)self; IErrorLogger elog = _->errorLogger; TOKEN_PRIVILEGES tp, prevstate; LUID luid; DWORD retlen; /* lookup privilege on local system */ if (!LookupPrivilegeValue( NULL, lpszPrivilege, &luid)) { elog->log(LOG_ERROR, ERROR_CODE_LOOKUP_PRIVILEGE_FAILED, "lookup privilege value error: error code %lu", GetLastError()); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; /* Enable or disable the privilege. */ if (!AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &prevstate, &retlen)) { int error = GetLastError(); elog->log(LOG_ERROR, ERROR_CODE_ADJUST_PRIVILEGE_FAILED, "adjust privilege value error: error code %lu", error); return FALSE; } return TRUE; }
void waitLatch( void* self, Latch latch) { ILatchManager _ = (ILatchManager)self; ISignalManager sm = _->signalManager; IErrorLogger elog = _->errorLogger; DWORD wait_result; HANDLE events[2]; HANDLE latchevent = latch->event; int numevents = 2; int result = 0; events[0] = signalEvent; events[1] = latchevent; sm->dispatchQueuedSignals(); do { wait_result = WaitForMultipleObjects(numevents, events, FALSE, INFINITE); if (wait_result == WAIT_FAILED) { int lastError = GetLastError(); elog->log(LOG_ERROR, ERROR_CODE_WAIT_FOR_MULTIPLE_OBJECTS_FAILED, "ResetEvent failed: error code %lu", lastError); } if (wait_result == WAIT_OBJECT_0) sm->dispatchQueuedSignals(); if (wait_result == WAIT_OBJECT_0 + 1) break; } while (result == 0); return result; }
void threadHelpCtor( void* self, sleepFunc slpFuncParam, Bool includeStartThreadEvent) { IThreadHelper _ = (IThreadHelper)self; IErrorLogger elog = _->errorLogger; slpFunc = slpFuncParam; ASSERT_VOID(elog, slpFunc != NULL); if (!includeStartThreadEvent) return; threadStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (threadStartEvent == NULL) elog->log(LOG_FATAL, ERROR_CODE_CREATE_EVENT_FAILED, "Could not create signal event: error code %lu", GetLastError()); }
TProcess startSubProcess(void* self, int argc, char* argv[]) { IProcessManager _ = (IProcessManager)self; IErrorLogger elog = _->errorLogger; STARTUPINFO si; PROCESS_INFORMATION pi; SECURITY_ATTRIBUTES sa; BackendParams paramSm; char paramSmStr[32]; char commandLine[MAX_PATH * 2]; int cmdCharCount; HANDLE paramMap; DeadChildInfo childInfo; HANDLE tokenHandle = NULL; int i, j; ASSERT(elog, argv != NULL, -1); ASSERT(elog, argv[0] != NULL, -1); ASSERT(elog, argv[1] != NULL, -1); ASSERT(elog, argv[2] == NULL, -1); /* Set up shared memory for parameter passing */ ZeroMemory(&sa, sizeof(sa)); sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; if (!OpenProcessToken( GetCurrentProcess(), TOKEN_ALL_ACCESS, &tokenHandle)) { elog->log(LOG_ERROR, ERROR_CODE_OPEN_PROCESS_TOKEN, "could not open process token: error code %lu", GetLastError()); return -1; } if (!SetPrivilege( _, tokenHandle, SE_CREATE_GLOBAL_NAME, True)) { elog->log(LOG_ERROR, ERROR_CODE_SET_PRIVILEDGE_FAILED, "could not open process token: error code %lu", GetLastError()); return -1; } /* If the first parameter is INVALID_HANDLE_VALUE, * the calling process must also specify a size * for the file mapping object. In this scenario, * CreateFileMapping creates a file mapping object * of a specified size that is backed by the system paging file * instead of by a file in the file system. */ paramMap = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, sizeof(SBackendParams), sharedMemParamsName); if (paramMap == NULL || paramMap == INVALID_HANDLE_VALUE) { int error = GetLastError(); elog->log(LOG_ERROR, ERROR_CODE_CREATE_FILE_MAP_FAILED, "could not create file mapping: error code %lu", error); return -1; } sharedMemID = paramMap; sharedMemSegmSize = sizeof(SBackendParams); /* Maps a view of a file mapping into the address space of a calling process. */ paramSm = MapViewOfFile(paramMap, FILE_MAP_WRITE, 0, 0, sizeof(SBackendParams)); if (!paramSm) { elog->log(LOG_ERROR, ERROR_CODE_MAP_MEMORY_TO_FILE, "could not map backend parameter memory: error code %lu", GetLastError()); CloseHandle(paramSm); return -1; } sprintf(paramSmStr, "%lu", (DWORD)paramSm); argv[2] = paramSmStr; cmdCharCount = sizeof(commandLine); commandLine[cmdCharCount - 1] = '\0'; commandLine[cmdCharCount - 2] = '\0'; snprintf(commandLine, cmdCharCount - 1, "\"%s\"", ExecPath); i = 0; while (argv[++i] != NULL) { j = strlen(commandLine); snprintf(commandLine + j, sizeof(commandLine) - 1 - j, " \"%s\"", argv[i]); } if (commandLine[sizeof(commandLine) - 2] != '\0') { elog->log(LOG_ERROR, ERROR_CODE_PROC_CMD_LINE_TO_LONG, "subprocess command line too long"); return -1; } memset(&pi, 0, sizeof(pi)); memset(&si, 0, sizeof(si)); si.cb = sizeof(si); /* Create the subprocess in a suspended state. * This will be resumed later, * once we have written out the parameter file. * If this parameter TRUE, each inheritable handle * in the calling process is inherited by the new process. * If the parameter is FALSE, the handles are not inherited. * Note that inherited handles have the same value * and access rights as the original handles. */ if (!CreateProcess(NULL, commandLine, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { elog->log(LOG_ERROR, ERROR_CODE_CREATE_PROCESS_FAILED, "CreateProcess call failed: (error code %lu)", GetLastError()); return -1; } if (!fillBackandParams( _, paramSm, pi.hProcess, pi.dwProcessId)) { /* Delete the process */ if (!TerminateProcess(pi.hProcess, 255)) elog->log(LOG_ERROR, ERROR_CODE_TERMINATE_PROCESS_FAILED, "Terminate process failed: (error code %lu)", GetLastError()); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return -1; } /* Drop the handler to the shared memory. * Now the shared memory has already been passed * along to the child process. */ if (!UnmapViewOfFile(paramSm)) elog->log(LOG_ERROR, ERROR_CODE_UNMAP_VIEW_OF_FILE, "could not unmap view of backend parameter file: error code %lu", GetLastError()); if (!CloseHandle(paramMap)) elog->log(LOG_ERROR, ERROR_CODE_CLOSE_HANDLER_FAILED, "could not close handle to backend parameter file: error code %lu", GetLastError()); /* All variables are written out, so we can resume the thread */ if (ResumeThread(pi.hThread) == -1) { /* Delete the process */ if (!TerminateProcess(pi.hProcess, 255)) { elog->log(LOG_ERROR, ERROR_CODE_TERMINATE_PROCESS_FAILED, "Terminate process failed: (error code %lu)", GetLastError()); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return -1; } CloseHandle(pi.hProcess); CloseHandle(pi.hThread); elog->log(LOG_ERROR, ERROR_CODE_RESUME_THREAD_FAILED, "Terminate process failed: (error code %lu)", GetLastError()); return -1; } childInfo = malloc(sizeof(SDeadChildInfo)); if (childInfo == NULL) elog->log(LOG_FATAL, ERROR_CODE_OUT_OF_MEMORY, "out of memory"); childInfo->procHandle = pi.hProcess; childInfo->procId = pi.dwProcessId; /* Directs a wait thread in the thread pool to wait on the object. * The wait thread queues the specified callback function to the thread pool * when one of the following occurs: * - The specified object is in the signaled state. * - The time-out interval elapses. * When a process terminates, the state of the process object * becomes signaled, releasing any threads * that had been waiting for the process to terminate. */ if (!RegisterWaitForSingleObject( &childInfo->waitHandle, pi.hProcess, deadChildProcCallBack, childInfo, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTEINWAITTHREAD)) elog->log(LOG_ERROR, ERROR_CODE_REGISTER_WAIT_HANDLER_FAILED, "Could not register process for wait: error code %lu", GetLastError()); /* Don't close pi.hProcess here - * the wait thread needs access to it. */ CloseHandle(pi.hThread); return pi.hProcess; }
Hashtable createHashtable( void* self, char* name, long maxItemsNum, HashtableSettings set, int setFlags) { IHashtableManager _ = (IHashtableManager)self; IErrorLogger elog = (IErrorLogger)_->errorLogger; ISpinLockManager slog = (ISpinLockManager)_->spinLockHelper; Hashtable tbl; int nHashLists; int nSegs; int segLLSize; uint elemSize; HashSegment* segP; ulong tblSegSize; /* Initialize the hash header, plus a copy of the table name. * First sizeof(Hashtable) bytes are allocated for a hash table. * The next strlen(Name) are allocated for the name. * In this way we unate one malloc call for struct SHashtable with another * malloc for char* name. */ tbl = (Hashtable)_->memManager->alloc(sizeof(SHashtable) + strlen(name) + 1); memset(tbl, 0, sizeof(SHashtable)); /* Adding one to a pointer means "produce a pointer to the object * that comes in memory right after this one," * which means that the compiler automatically scales up * whatever you're incrementing the pointer with * by the size of the object being pointed at. */ tbl->name = (char*)(tbl + 1); strcpy(tbl->name, name); setDefaults(tbl); if (setFlags & HASH_FUNC) tbl->hashFunc = set->hashFunc; if (tbl->hashFunc == hashFuncStr) tbl->hashCmp = hashCmpFuncStr; if (setFlags & HASH_CMP) tbl->hashCmp = set->hashCmp; if (tbl->hashFunc == hashFuncStr) tbl->hashCpy = (hashCpyFunc)strcpy; if (setFlags & HASH_KEYCPY) tbl->hashCpy = set->hashCpy; if (setFlags & HASH_ITEM) { tbl->keyLen = set->keyLen; tbl->valLen = set->valLen; } if (setFlags & HASH_SEG) { tbl->segmSize = set->segmSize; tbl->segmShift = set->segmShift; } if (setFlags & HASH_LIST_SIZE) { tbl->segmsAmount = set->segmsAmount; tbl->maxSegmsAmount = set->maxSegmsAmount; tbl->hashListSize = set->hashListSize; } if (setFlags & HASH_WITHOUT_EXTENTION) { tbl->isWithoutExtention = set->isWithoutExtention; } if (setFlags & HASH_PARTITION) { Bool isNumPowerOf2 = tbl->partNum == _->commonHelper->nextPowerOf2(tbl->partNum); /* First of all we need to check if the hashtable is located * in the shared memory. Applying partitions to a hashtable * which is located in the local memory is pointless because * no contention is applied. */ ASSERT(elog, setFlags & HASH_SHARED_MEMORY, NULL); /* We should check if the number of partiotions is really power of 2 */ ASSERT(elog, isNumPowerOf2, NULL); tbl->partNum = set->partNum; } if (IS_TABLE_PARTITIONED(tbl)) slog->spinLockInit(slog, &(tbl->mutex)); nHashLists = (maxItemsNum - 1) / tbl->hashListSize + 1; nHashLists = _->commonHelper->nextPowerOf2(nHashLists); tbl->lowMask = _->hashtableHelper->calcLowMask(nHashLists); tbl->highMask = _->hashtableHelper->calcHighMask(nHashLists); nSegs = _->hashtableHelper->calcSegmsNum(nHashLists, tbl->segmSize); tbl->numHashLists = nHashLists; tbl->segments = (AHashSegment)_->memManager->alloc(tbl->segmsAmount * sizeof(HashSegment)); tblSegSize = tbl->segmSize; /* Allocate initial segments */ for (segP = tbl->segments; tbl->nSegs < nSegs; tbl->nSegs++, segP++) { segLLSize = tblSegSize * sizeof(HashList); *segP = (HashSegment)(AHashList)_->memManager->alloc(segLLSize); memset(*segP, 0, segLLSize); } /* Our element consists of a header and data sp that the size * is sum of the header's size and data's size. * Header also includes aligned key. */ elemSize = HASH_ELEM_SIZE(tbl); tbl->numItemsToAlloc = itemsNumToAlloc(elemSize); if (!initHashtable(_, tbl)) { elog->log(LOG_ERROR, ERROR_CODE_FAILED_TO_INIT_HASHTABLE, "Spinlock exceeded max allowed sleep counts: %d"); return NULL; } if (setFlags & HASH_SEG || maxItemsNum < tbl->numItemsToAlloc) { if (!allocNewItems(_, tbl)) { elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory"); return NULL; } return NULL; } return tbl; }
/* Reallocate memory. When we need more memory * we allocate a piece of memory with an appropriate size, * copy an old memory and then free it. */ void* reallocateMemory( void* self, MemoryContainer container, void* old_mem, size_t new_size) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; MemorySet set = (MemorySet)container; MemoryChunk chunk = (MemoryChunk)((char*)old_mem - MEM_CHUNK_SIZE); size_t oldsize = chunk->size; void* chunkPtr; void* new_mem; /* Always return when a requested size is a decrease */ if (oldsize >= new_size) return old_mem; /* Check if for the chunk there was allocate a block */ if (oldsize > set->chunkMaxSize) { /* Try to find the corresponding block first */ MemoryBlock block = set->blockList; MemoryBlock prevblock = NULL; size_t chunk_size; size_t block_size; char* expected_block_end; while (block != NULL) { if (chunk == (MemoryChunk)((char*)block + MEM_BLOCK_SIZE)) break; prevblock = block; block = block->next; } /* Could not find the block. We should report an error. */ if (block == NULL) { elog->log(LOG_ERROR, ERROR_CODE_BLOCK_NOT_FOUND, "Could not find block containing chunk %p", chunk); return NULL; } /* We should check that the chunk is only one * on the block. */ expected_block_end = (char*)block + chunk->size + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; ASSERT(elog, block->freeEnd == expected_block_end, NULL); /* Do the realloc */ chunk_size = AlignDefault(new_size); block_size = chunk_size + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; ASSERT(elog, funcRealloc != NULL, NULL); block = (MemoryBlock)funcRealloc(block, block_size); if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", block_size); return NULL; } block->freeStart = block->freeEnd = (char*)block + block_size; chunk = (MemoryChunk)((char*)block + MEM_BLOCK_SIZE); /* Change block pointer to newly allocated block. */ if (prevblock == NULL) set->blockList = block; else prevblock->next = block; chunk->size = chunk_size; chunkPtr = MemoryChunkGetPointer(chunk); return chunkPtr; } /* If we are here that this small block * was taken from the free list. We allocate * a new free chunk and the old chunk add to * the free list. */ new_mem = allocateMemory(_, container, new_size); /* copy existing memory to a new memory. */ memcpy(new_mem, old_mem, oldsize); /* free old chunk */ freeChunk(self, old_mem); return new_mem; }
/* Frees a piece of memory that have been allocated * as a chunk. The function converts this memory address * to a chunk and checks how this chunks has been allocated. * If it was like a full block we should free this block. * If is was an ordinary chunk we give it back to the freelist * for recycling. */ void freeChunk( void* self, void* mem) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; MemoryChunk chunk; MemorySet set; int ind; /* Get a pointer to MemoryChunk header. */ chunk = (MemoryChunk)((char*)mem - MEM_CHUNK_SIZE); set = chunk->memsetorchunk; /* If the chunk's size is larger than chunk max size * we have allocated an entire block. So that we have * to find and free this block. */ if (chunk->size > set->chunkMaxSize) { /* Try to find the corresponding block first */ MemoryBlock block = set->blockList; MemoryBlock prevblock = NULL; while (block != NULL) { if (chunk == (MemoryChunk)((char*)block + MEM_BLOCK_SIZE)) break; prevblock = block; block = block->next; } /* Could not find the block. We should report an error. */ if (block == NULL) { elog->log(LOG_ERROR, ERROR_CODE_BLOCK_NOT_FOUND, "Could not find block containing chunk %p", chunk); return; } /* Remove the block from the block list */ if (prevblock == NULL) set->blockList = block->next; else prevblock->next = block->next; ASSERT_VOID(elog, funcFree != NULL); funcFree(block); return; } /* Now we have a normal case and the chunk is small * So we should return it to the free list. */ ind = calculateFreeListIndex(chunk->size); /* Insert into the head of the free list. */ chunk->memsetorchunk = (void*)set->freelist[ind]; set->freelist[ind] = chunk; }
/* Creates new memory set object. */ MemorySet memSetCreate( void* self, MemoryContainer container, MemoryContainer parent, char* name, size_t minContainerSize, size_t initBlockSize, size_t maxBlockSize) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; size_t blockMaxChunkSize; size_t blockSize; MemoryBlock block; /* MemorySet is derived from MemoryContainer. * First we create a base object. */ MemorySet set = (MemorySet)memContCreate( self, container, parent, MCT_MemorySet, sizeof(SMemorySet), name); ASSERT(elog, set != NULL, NULL); initBlockSize = AlignDefault(initBlockSize); /* Check if initBlockSize is less than * the minimum allowed value. */ if (initBlockSize < MEM_BLOCK_INIT_MIN_SIZE) initBlockSize = MEM_BLOCK_INIT_MIN_SIZE; maxBlockSize = AlignDefault(maxBlockSize); /* maxBlock should be bigger than initBlockSize. */ if (maxBlockSize < initBlockSize) maxBlockSize = initBlockSize; set->initBlockSize = initBlockSize; set->maxBlockSize = maxBlockSize; set->nextBlockSize = initBlockSize; /* chunkMaxSize can't be more than chunk_max_size * because the number of free lists is restricted. */ set->chunkMaxSize = MEMORY_CHUNK_MAX_SIZE; /* Calculate the max chunk size in comparison * to the max block size. * The chunk size limit is at most 1/8 of the max block size * In the case when all chunks have the maximum size * only 1/8 of the block space will be wasted. */ blockMaxChunkSize = (maxBlockSize - MEM_BLOCK_SIZE) / MAX_BLOCK_CHUNKS_NUM; /* There can be a situation when memory chunk max size is more than * the max chunk size to block. So we should syncronize this and * we divide it on 2 until chunk max size becomes less than maxChunkSizeToBlock. */ while (set->chunkMaxSize + MEM_CHUNK_SIZE > blockMaxChunkSize) set->chunkMaxSize >>= 1; if (minContainerSize <= MEM_BLOCK_SIZE + MEM_CHUNK_SIZE) return (MemoryContainer)set; /* Here minContextSize is more than the block size. * In this case we allocate the first block. */ blockSize = AlignDefault(minContainerSize); ASSERT(elog, funcMalloc != NULL, NULL); block = (MemoryBlock)funcMalloc(blockSize); /* An error has happened. We should report it. */ if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", blockSize); return NULL; } block->memset = set; block->freeStart = ((char*)block) + MEM_BLOCK_SIZE; block->freeEnd = ((char*)block) + blockSize; /* Insert this block into the head of the blocks list. */ block->next = set->blockList; set->blockList = block; //set->keeperBlock = block; return (MemoryContainer)set; }
void* allocateMemory( void* self, MemoryContainer container, size_t size) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; Bool isContValid = MemoryContainerIsValid(container); Bool isSizeValid = MemAllocSizeIsValid(size); MemorySet set = (MemorySet)container; MemoryBlock block; MemoryChunk chunk; void* chunkPtr; int freeListInd; size_t chunkSize; size_t blockSize; /* Assert if the container is valid. */ ASSERT_ARG(elog, isContValid, NULL); ASSERT(elog, set->chunkMaxSize > 0, NULL); if (!isSizeValid) elog->log(LOG_ERROR, -1, "Allocate memory request size: %lu is invalid", size); /* In this case the requested memory size * is more than the max chunk size. So that * we can't allocate a memory chunk. * In this case we allocate a new whole block. */ if (size > set->chunkMaxSize) return allocateChunkBlock(_, size, container); /* The size of the chunk is too small to be an entire block. * In this case we should treat it as a chunk. * First of all we should look at the free list if * there is a free chunk that can come up to our * memory request. */ freeListInd = calculateFreeListIndex(size); chunk = set->freelist[freeListInd]; if (chunk != NULL) { ASSERT(elog, chunk->size > size, NULL); /* We remove the chunk from the head of the free list. */ set->freelist[freeListInd] = (MemoryChunk)chunk->memsetorchunk; /* MemSetOrChunk now points to the parent memory set */ chunk->memsetorchunk = set; chunk->sizeRequested = size; chunkPtr = MemoryChunkGetPointer(chunk); return chunkPtr; } /* Now we should calculate the chunk size. * freeListInd = log2(chunksCount) * 2^(freeListInd) = 2^(log2(chunksCount)) * chunksCount = 2 ^ freeListInd; * 1 << 10 = 2 ^ 10. So we should shift left * on freeListInd positions * chunksCount = 1 << freeListInd; * And then we should multiple on 2 ^ MIN_CHUNK_POWER_OF_2 */ chunkSize = (1 << MIN_CHUNK_POWER_OF_2) << freeListInd; ASSERT(elog, chunkSize > size, NULL); /* Take the first block in the blocks list */ block = set->blockList; /* If the actual active block does not contain enough * free space for the chunk we should create a new block. */ if (block == NULL || !checkFreeBlockSpace(_, set, block, chunkSize)) block = allocateBlock(_, chunkSize, container); /* do the allocation */ chunk = (MemoryChunk)block->freeStart; /* Move the block's free start pointer */ block->freeStart += (chunkSize + MEM_CHUNK_SIZE); chunk->memsetorchunk = (void*)set; chunk->size = chunkSize; chunk->sizeRequested = size; chunkPtr = MemoryChunkGetPointer(chunk); return chunkPtr; }
/* This is also an auxiliary function for allocateMemory method. * It allocates a new block and the whole memory is free. * The difference between allocateBlock and allocateChunkBlock * is that allocateChunkBlock gives the whole memory to a chunk * and its free list is always empty. allocateBlock creates * a new block and reclaims whole memory to its free list. */ void* allocateBlock( void* self, size_t chunkSize, MemoryContainer container) { IMemContainerManager _ = (IMemContainerManager)self; IErrorLogger elog = _->errorLogger; MemorySet set = (MemorySet)container; MemoryBlock block; size_t requiredSize; size_t blockSize; ASSERT(elog, set->nextBlockSize > 0, NULL); ASSERT(elog, set->maxBlockSize > 0, NULL); /* Set blockSize. We keep track of all block sizes * And we allocate blocks in increase order of their size. * We start from initBlockSize and always double the blockSize * until we reach maxBlockSize. */ blockSize = set->nextBlockSize; set->nextBlockSize <<= 1; if (set->nextBlockSize > set->maxBlockSize) set->nextBlockSize = set->maxBlockSize; /* Block size should at least cover the chunk */ requiredSize = chunkSize + MEM_BLOCK_SIZE + MEM_CHUNK_SIZE; while (blockSize < requiredSize) blockSize <<= 1; /* Allocate a new block */ ASSERT(elog, funcMalloc != NULL, NULL); block = (MemoryBlock)funcMalloc(blockSize); /* Report malloc error */ if (block == NULL) { showMemStat(_, topMemCont, 0); elog->log(LOG_ERROR, ERROR_CODE_OUT_OF_MEMORY, "Out of memory. Failed request size: %lu", chunkSize); } block->memset = set; block->freeStart = ((char*)block) + MEM_BLOCK_SIZE; block->freeEnd = ((char*)block) + blockSize; /* If the block size is an initial block size, * we adjust a keeper block. */ // if (set->keeperBlock == NULL && blockSize == set->initBlockSize) // set->keeperBlock = block; /* Insert the new block into the head of the freelist */ block->next = set->blockList; set->blockList = block; return block; }