//-------------------------------------------------------------------------------------------------- static Service_t* GetService ( le_msg_ProtocolRef_t protocolRef, const char* serviceName ) //-------------------------------------------------------------------------------------------------- { ServiceId_t id; id.protocolRef = protocolRef; LE_FATAL_IF(le_utf8_Copy(id.name, serviceName, sizeof(id.name), NULL) == LE_OVERFLOW, "Service ID '%s' too long (should only be %zu bytes total).", serviceName, sizeof(id.name)); Service_t* servicePtr = le_hashmap_Get(ServiceMapRef, &id); if (servicePtr == NULL) { servicePtr = CreateService(protocolRef, serviceName); } else { le_mem_AddRef(servicePtr); } return servicePtr; }
//-------------------------------------------------------------------------------------------------- le_mcc_CallRef_t le_mcc_Create ( const char* phoneNumPtr ///< [IN] ///< The target number we are going to ///< call. ) { if (phoneNumPtr == NULL) { LE_KILL_CLIENT("phoneNumPtr is NULL !"); return NULL; } if(strlen(phoneNumPtr) > (LE_MDMDEFS_PHONE_NUM_MAX_BYTES-1)) { LE_KILL_CLIENT("strlen(phoneNumPtr) > %d", (LE_MDMDEFS_PHONE_NUM_MAX_BYTES-1)); return NULL; } // Create the Call object. le_mcc_Call_t* mccCallPtr = GetCallObject(phoneNumPtr, -1, false); if (mccCallPtr != NULL) { le_mem_AddRef(mccCallPtr); mccCallPtr->refCount++; } else { mccCallPtr = CreateCallObject (phoneNumPtr, -1, LE_MCC_EVENT_TERMINATED, LE_MCC_TERM_UNDEFINED, -1); } // Manage client session SessionRefNode_t* newSessionRefPtr = le_mem_ForceAlloc(SessionRefPool); newSessionRefPtr->sessionRef = le_mcc_GetClientSessionRef(); newSessionRefPtr->link = LE_DLS_LINK_INIT; // Add the new sessionRef into the the sessionRef list le_dls_Queue(&mccCallPtr->sessionRefList, &(newSessionRefPtr->link)); if (mccCallPtr==NULL) { LE_ERROR("mccCallPtr null!!!!"); return NULL; } LE_DEBUG("Create Call ref.%p", mccCallPtr->callRef); // Return a Safe Reference for this Call object. return (mccCallPtr->callRef); }
//-------------------------------------------------------------------------------------------------- static void UpdateReferenceCount ( le_mcc_Call_t* callPtr ) { while (callPtr->refCount < CallHandlerCount) { callPtr->refCount++; le_mem_AddRef(callPtr); } }
//-------------------------------------------------------------------------------------------------- void msgService_AddSession ( le_msg_ServiceRef_t serviceRef, le_msg_SessionRef_t sessionRef ) //-------------------------------------------------------------------------------------------------- { // The Session object holds a reference to the Service object. le_mem_AddRef(serviceRef); le_dls_Queue(&serviceRef->sessionList, msgSession_GetListLink(sessionRef)); }
//-------------------------------------------------------------------------------------------------- void tu_SessionConnected ( le_msg_SessionRef_t sessionRef ///< [IN] The session just connected. ) //-------------------------------------------------------------------------------------------------- { bool wasCreated; tu_UserRef_t userRef = GetUserInfo(sessionRef, &wasCreated); if ( (wasCreated == false) && (tu_GetUserId(userRef) != 0)) { le_mem_AddRef(userRef); } }
//-------------------------------------------------------------------------------------------------- static void FdEventHandler ( int fd, short events ) //-------------------------------------------------------------------------------------------------- { Parser_t* parserPtr = le_fdMonitor_GetContextPtr(); // Increment the reference count on the Parser object so it won't go away until we are done // with it, even if the client calls le_json_Cleanup() for this parser. le_mem_AddRef(parserPtr); if (events & POLLIN) // Data available to read? { ReadData(parserPtr, fd); } // Error or hang-up? if (NotStopped(parserPtr) && ((events & POLLERR) || (events & POLLHUP) || (events & POLLRDHUP))) { char msg[256]; size_t bytesPrinted = snprintf(msg, sizeof(msg), "Read error flags set:"); if (events & POLLERR) { bytesPrinted += snprintf(msg + bytesPrinted, sizeof(msg) - bytesPrinted, " POLLERR"); } if (events & POLLHUP) { bytesPrinted += snprintf(msg + bytesPrinted, sizeof(msg) - bytesPrinted, " POLLHUP"); } if (events & POLLRDHUP) { bytesPrinted += snprintf(msg + bytesPrinted, sizeof(msg) - bytesPrinted, " POLLRDHUP"); } Error(parserPtr, LE_JSON_READ_ERROR, msg); } // We are finished with the parser object now. le_mem_Release(parserPtr); }
//-------------------------------------------------------------------------------------------------- static pthread_t StartThread ( pthread_attr_t* attrPtr, void* completionObjPtr ///< [in] Pointer to the object whose reference count is used to signal /// the completion of the test. ) { // Increment the reference count on the "completion object". This will be released by // ThreadMain() when the thread completes and has cleaned up its Legato thread-specific data. le_mem_AddRef(completionObjPtr); pthread_t handle; int result = pthread_create(&handle, attrPtr, ThreadMain, completionObjPtr); LE_FATAL_IF(result != 0, "pthread_create() failed with errno %m."); return handle; }
//-------------------------------------------------------------------------------------------------- static void StringEventHandler ( Parser_t *parserPtr, void *unused ) { char c; LE_UNUSED(unused); // Increment the reference count on the Parser object so it won't go away until we are done // with it, even if the client calls le_json_Cleanup() for this parser. le_mem_AddRef(parserPtr); while (NotStopped(parserPtr)) { c = parserPtr->jsonString[parserPtr->bytesRead]; if (c == '\0') // End of string? { // The document has been truncated. Error(parserPtr, LE_JSON_READ_ERROR, "Unexpected end of JSON string"); break; } else { parserPtr->bytesRead++; if (c == '\n') { parserPtr->line++; } ProcessChar(parserPtr, c); } } // We are finished with the parser object now. le_mem_Release(parserPtr); }
//-------------------------------------------------------------------------------------------------- static void DispatchToHandler ( void* param1Ptr, ///< FD Monitor safe reference. void* param2Ptr ///< epoll() event flags. ) //-------------------------------------------------------------------------------------------------- { uint32_t epollEventFlags = (uint32_t)(size_t)param2Ptr; LOCK // Get a pointer to the FD Monitor object for this fd. FdMonitor_t* fdMonitorPtr = le_ref_Lookup(FdMonitorRefMap, param1Ptr); UNLOCK // If the FD Monitor object has been deleted, we can just ignore this. if (fdMonitorPtr == NULL) { TRACE("Discarding events for non-existent FD Monitor %p.", param1Ptr); return; } // Sanity check: The FD monitor must belong to the current thread. LE_ASSERT(thread_GetEventRecPtr() == fdMonitorPtr->threadRecPtr); // Mask out any events that have been disabled since epoll_wait() reported these events to us. epollEventFlags &= (fdMonitorPtr->epollEvents | EPOLLERR | EPOLLHUP | EPOLLRDHUP); // If there's nothing left to report to the handler, don't call it. if (epollEventFlags == 0) { // Note: if the fd is always ready to read or write (is not supported by epoll()), then // we will only end up in here if both POLLIN and POLLOUT are disabled, in which case // returning now will prevent re-queuing of DispatchToHandler(), which is what we // want. When either POLLIN or POLLOUT are re-enabled, le_fdMonitor_Enable() will // call fdMon_Report() to get things going again. return; } // Translate the epoll() events into poll() events. short pollEvents = EPollToPoll(epollEventFlags); if (IS_TRACE_ENABLED()) { char eventsTextBuff[128]; TRACE("Calling event handler for FD Monitor %s (fd %d, events %s).", fdMonitorPtr->name, fdMonitorPtr->fd, GetPollEventsText(eventsTextBuff, sizeof(eventsTextBuff), pollEvents)); } // Increment the reference count on the Monitor object in case the handler deletes it. le_mem_AddRef(fdMonitorPtr); // Store a pointer to the FD Monitor as thread-specific data so le_fdMonitor_GetMonitor() // and le_fdMonitor_GetContextPtr() can find it. LE_ASSERT(pthread_setspecific(FDMonitorPtrKey, fdMonitorPtr) == 0); // Set the thread's event loop Context Pointer. event_SetCurrentContextPtr(fdMonitorPtr->contextPtr); // Call the handler function. fdMonitorPtr->handlerFunc(fdMonitorPtr->fd, pollEvents); // Clear the thread-specific pointer to the FD Monitor. LE_ASSERT(pthread_setspecific(FDMonitorPtrKey, NULL) == 0); // If this fd is always ready (is not supported by epoll) and either POLLIN or POLLOUT // are enabled, then queue up another dispatcher for this FD Monitor. // If neither are enabled, then le_fdMonitor_Enable() will queue the dispatcher // when one of them is re-enabled. if ((fdMonitorPtr->isAlwaysReady) && (fdMonitorPtr->epollEvents & (EPOLLIN | EPOLLOUT))) { fdMon_Report(fdMonitorPtr->safeRef, fdMonitorPtr->epollEvents & (EPOLLIN | EPOLLOUT)); } // Release our reference. We don't need the Monitor object anymore. le_mem_Release(fdMonitorPtr); }
int main(int argc, char *argv[]) { le_mem_PoolRef_t idPool, colourPool; idObj_t* idsPtr[ID_POOL_SIZE + NUM_EXTRA_ID] = {NULL}; colourObj_t* coloursPtr[COLOUR_POOL_SIZE] = {NULL}; unsigned int i = 0; unsigned int numRelease = 0; printf("\n"); printf("*** Unit Test for le_mem module. ***\n"); // // Create multiple pools. // idPool = le_mem_CreatePool("ID Pool", sizeof(idObj_t)); colourPool = le_mem_CreatePool("Colour Pool", sizeof(colourObj_t)); printf("Created two memory pools.\n"); // // Expand the pools. // idPool = le_mem_ExpandPool(idPool, ID_POOL_SIZE); colourPool = le_mem_ExpandPool(colourPool, COLOUR_POOL_SIZE); printf("Expanded all pools.\n"); // // Set destructors. // le_mem_SetDestructor(idPool, IdDestructor); // // Spawn child process and perform Assert allocation until failure. // pid_t pID = fork(); if (pID == 0) { // This is the child. // Allocate more than the available objects so the assert will kill the process. for (i = 0; i < ID_POOL_SIZE + 1; i++) { idsPtr[i] = le_mem_AssertAlloc(idPool); } return LE_OK; } else { int status; // wait for the child to terminate. wait(&status); if ( WEXITSTATUS(status) == EXIT_FAILURE ) // Child terminated with an error. { printf("Assert allocation performed correctly.\n"); } else { printf("Assert allocation incorrect: %d", __LINE__); return LE_FAULT; } } // // Allocate all objects. // for (i = 0; i < ID_POOL_SIZE; i++) { idsPtr[i] = le_mem_TryAlloc(idPool); if (idsPtr[i] == NULL) { printf("Allocation error: %d", __LINE__); return LE_FAULT; } idsPtr[i]->id = i; } for (i = 0; i < COLOUR_POOL_SIZE; i++) { coloursPtr[i] = le_mem_TryAlloc(colourPool); if (coloursPtr[i] == NULL) { printf("Allocation error: %d", __LINE__); return LE_FAULT; } coloursPtr[i]->r = i; coloursPtr[i]->g = i + 1; coloursPtr[i]->b = i + 2; } printf("Allocated all objects from all pools.\n"); // // Check objects // for (i = 0; i < ID_POOL_SIZE; i++) { if (idsPtr[i]->id != i) { printf("Object error: %d", __LINE__); return LE_FAULT; } } for (i = 0; i < COLOUR_POOL_SIZE; i++) { if ( (coloursPtr[i]->r != i) || (coloursPtr[i]->g != i+1) || (coloursPtr[i]->b != i+2) ) { printf("Object error: %d", __LINE__); return LE_FAULT; } } printf("Checked all objects in pools.\n"); // // Randomly release some objects. // { //seed the random number generator with the clock srand((unsigned int)clock()); idObj_t* objPtr; size_t numNotReleased = 0; NumRelease = 0; for (i = 0; i < ID_POOL_SIZE; i++) { objPtr = idsPtr[i]; if (rand() < REMOVE_THRESHOLD) { // Increase the ref number for these objects. le_mem_AddRef(objPtr); // These objects should not get freed. numNotReleased++; } else { idsPtr[i] = NULL; } // Release all objects but only objects that did not have their ref count increased will be released. le_mem_Release(objPtr); } if (NumRelease != ID_POOL_SIZE - numNotReleased) { printf("Released objects incorrectly: %d", __LINE__); return LE_FAULT; } // Release the rest of the objects. for (i = 0; i < ID_POOL_SIZE; i++) { if (idsPtr[i] != NULL) { le_mem_Release(idsPtr[i]); idsPtr[i] = NULL; } } // Check the number of free objects. le_mem_PoolStats_t stats; le_mem_GetStats(idPool, &stats); if (stats.numFree != ID_POOL_SIZE) { printf("Released objects incorrectly: %d", __LINE__); return LE_FAULT; } // Spawn child process and try to release an object that is already released. pid_t pID = fork(); if (pID == 0) { // This is the child. // This allocation should fail and kill the process. le_mem_Release(objPtr); return LE_OK; } else { int status; // wait for the child to terminate. wait(&status); if ( WEXITSTATUS(status) == EXIT_FAILURE ) // Child terminated with an error. { printf("Ref count correct.\n"); } else { printf("Ref count incorrect: %d", __LINE__); return LE_FAULT; } } } printf("Released objects according to ref counts correctly.\n"); printf("Checked that destructors were called correctly.\n"); // // Try allocate until full. // for (i = 0; i < ID_POOL_SIZE; i++) { if (idsPtr[i] == NULL) { idsPtr[i] = le_mem_TryAlloc(idPool); if (idsPtr[i] == NULL) { printf("Allocation error: %d.", __LINE__); return LE_FAULT; } } } // The pool should now be empty. if (le_mem_TryAlloc(idPool) != NULL) { printf("Allocation error: %d.", __LINE__); return LE_FAULT; } printf("Tried allocating from empty pool.\n"); // // Force allocate. // le_mem_SetNumObjsToForce(idPool, FORCE_SIZE); for (i = ID_POOL_SIZE; i < ID_POOL_SIZE + NUM_EXTRA_ID; i++) { idsPtr[i] = le_mem_ForceAlloc(idPool); if (idsPtr[i] == NULL) { printf("Allocation error: %d.", __LINE__); return LE_FAULT; } } printf("Forced allocated objects.\n"); // // Get stats. // le_mem_PoolStats_t stats; le_mem_GetStats(idPool, &stats); if ( (stats.numAllocs != ID_POOL_SIZE + NUM_EXTRA_ID + NumRelease) || (stats.numOverflows != (unsigned int)ceil(((1.0*NUM_EXTRA_ID / FORCE_SIZE)))) || (stats.numFree != (stats.numOverflows*FORCE_SIZE) % NUM_EXTRA_ID) ) { printf("Stats are incorrect: %d", __LINE__); return LE_FAULT; } printf("Stats are correct.\n"); // // Get pool size. // if (le_mem_GetTotalNumObjs(idPool) != ID_POOL_SIZE + (stats.numOverflows * FORCE_SIZE)) { printf("Pool size incorrect: %d", __LINE__); return LE_FAULT; } printf("Checked pool size.\n"); // // Get object size. // if (le_mem_GetObjectSize(idPool) != sizeof(idObj_t)) { printf("Object size incorrect: %d", __LINE__); return LE_FAULT; } printf("Checked object size.\n"); // // Reset stats. // { size_t numFree = stats.numFree; le_mem_ResetStats(idPool); le_mem_GetStats(idPool, &stats); if ( (stats.numAllocs != 0) || (stats.numOverflows != 0) || (stats.numFree != numFree) ) { printf("Stats are incorrect: %d", __LINE__); return LE_FAULT; } } printf("Reset stats correctly.\n"); // // Create sub-pool. // // Release some objs from the super-pool in a random manner. numRelease = 0; for (i = 0; i < COLOUR_POOL_SIZE; i++) { if (rand() < REMOVE_THRESHOLD) { le_mem_Release(coloursPtr[i]); coloursPtr[i] = NULL; numRelease++; } } // Create the sub-pool. le_mem_PoolRef_t colourSubPool1 = le_mem_CreateSubPool(colourPool, "Colour sub-pool", numRelease); // // Check sub-pools and super-pool. // if ( (le_mem_GetTotalNumObjs(colourSubPool1) != numRelease) || (le_mem_GetTotalNumObjs(colourPool) != COLOUR_POOL_SIZE) ) { printf("Sub-pool incorrect: %d", __LINE__); return LE_FAULT; } printf("Sub-pool created correctly.\n"); // // Create second sub-pool. // // Release the rest of the objects from the super-pool. for (i = 0; i < COLOUR_POOL_SIZE; i++) { if (coloursPtr[i] != NULL) { le_mem_Release(coloursPtr[i]); coloursPtr[i] = NULL; } } // Create another sub-pool. le_mem_PoolRef_t colourSubPool2 = le_mem_CreateSubPool(colourPool, "Second sub-pool", COLOUR_POOL_SIZE - numRelease); printf("Created second sub-pool.\n"); // // Expand the sub-pool causing the super-pool to expand. // le_mem_ExpandPool(colourSubPool2, NUM_EXPAND_SUB_POOL); // // Allocate from sub-pool. // for (i = 0; i < COLOUR_POOL_SIZE - numRelease; i++) { coloursPtr[i] = le_mem_TryAlloc(colourSubPool2); if (coloursPtr[i] == NULL) { printf("Error allocating from sub-pool: %d", __LINE__); return LE_FAULT; } } // // Check pools. // le_mem_GetStats(colourPool, &stats); if ( (le_mem_GetTotalNumObjs(colourPool) != COLOUR_POOL_SIZE + NUM_EXPAND_SUB_POOL) || (stats.numFree != 0) ) { printf("Error in super-pool: %d", __LINE__); return LE_FAULT; } le_mem_GetStats(colourSubPool1, &stats); if ( (le_mem_GetTotalNumObjs(colourSubPool1) != numRelease) || (stats.numFree != numRelease) ) { printf("Error in sub-pool: %d", __LINE__); return LE_FAULT; } le_mem_GetStats(colourSubPool2, &stats); if ( (le_mem_GetTotalNumObjs(colourSubPool2) != COLOUR_POOL_SIZE - numRelease + NUM_EXPAND_SUB_POOL) || (stats.numFree != NUM_EXPAND_SUB_POOL) ) { printf("Error in sub-pool: %d", __LINE__); return LE_FAULT; } printf("Expanded sub-pool correctly.\n"); printf("Allocated from sub-pools correctly.\n"); // Try Allocating from empty super-pool. if (le_mem_TryAlloc(colourPool) != NULL) { printf("Error in super-pool: %d", __LINE__); return LE_FAULT; } // // Delete sub-pool. // le_mem_DeleteSubPool(colourSubPool1); // Allocate from the super-pool. for (i = 0; i < NUM_ALLOC_SUPER_POOL; i++) { coloursPtr[numRelease] = le_mem_AssertAlloc(colourPool); } // // Check pools. // le_mem_GetStats(colourPool, &stats); if ( (stats.numFree != numRelease - NUM_ALLOC_SUPER_POOL) ) { printf("Error in super-pool: %d", __LINE__); return LE_FAULT; } le_mem_GetStats(colourSubPool2, &stats); if ( (le_mem_GetTotalNumObjs(colourSubPool2) != COLOUR_POOL_SIZE - numRelease + NUM_EXPAND_SUB_POOL) || (stats.numFree != NUM_EXPAND_SUB_POOL) ) { printf("Error in sub-pool: %d", __LINE__); return LE_FAULT; } printf("Deleted sub-pool correctly.\n"); // // Re-create sub-pool causing super pool to expand. // colourSubPool1 = le_mem_CreateSubPool(colourPool, "First sub-pool", numRelease + NUM_EXPAND_SUB_POOL); if ( (le_mem_GetTotalNumObjs(colourSubPool1) != numRelease + NUM_EXPAND_SUB_POOL) || (le_mem_GetTotalNumObjs(colourPool) != COLOUR_POOL_SIZE + 2*NUM_EXPAND_SUB_POOL + NUM_ALLOC_SUPER_POOL) ) { printf("Error re-creating sub-pool: %d", __LINE__); return LE_FAULT; } printf("Successfully recreated sub-pool.\n"); // // Search for pools by name. // if ( (idPool != le_mem_FindPool("ID Pool")) || (colourSubPool1 != le_mem_FindPool("First sub-pool")) ) { printf("Error finding pools by name: %d", __LINE__); return LE_FAULT; } printf("Successfully searched for pools by name.\n"); printf("*** Unit Test for le_mem module passed. ***\n"); printf("\n"); return LE_OK; }
// ------------------------------------------------------------------------------------------------- static void SpawnChildren ( size_t depth, // Indicates what nesting level the thread is at. // 1 = children of the process main thread. void* completionObjPtr // Ptr to the object whose ref count is used to terminate the test. ) // ------------------------------------------------------------------------------------------------- { int i; char childName[32]; le_thread_Ref_t children[FAN_OUT]; // Create and start all the children. for (i = 0; i < FAN_OUT; i++) { le_thread_Ref_t threadRef; Context_t* contextPtr = le_mem_ForceAlloc(ContextPoolRef); contextPtr->depth = depth; contextPtr->completionObjPtr = completionObjPtr; le_mem_AddRef(completionObjPtr); snprintf(childName, sizeof(childName), "%s-%d", le_thread_GetMyName(), i + 1); LE_INFO("Spawning thread '%s'.", childName); threadRef = le_thread_Create(childName, ThreadMainFunction, contextPtr); LE_INFO("Thread '%s' created.", childName); // Create a thread destructor that will release the Context object and the // Test Completion Object that we are going to pass to the child. le_thread_AddChildDestructor(threadRef, ThreadDestructor, contextPtr); LE_INFO("Thread '%s' destructor added.", childName); // Make every third child thread non-joinable and the rest joinable. if (((i + 1) % 3) != 0) { le_thread_SetJoinable(threadRef); } LE_INFO("Thread '%s' joinability set.", childName); // Start the child thread. le_thread_Start(threadRef); LE_INFO("Thread '%s' started.", childName); // Remember the child's thread reference for later join attempt. children[i] = threadRef; } // Join with all the children. for (i = 0; i < FAN_OUT; i++) { void* threadReturnValue; snprintf(childName, sizeof(childName), "%s-%d", le_thread_GetMyName(), i + 1); LE_INFO("Joining with thread '%s'.", childName); le_result_t result = le_thread_Join(children[i], &threadReturnValue); if (result != LE_OK) { LE_INFO("Failed to join with thread '%s'.", childName); LE_FATAL_IF(((i + 1) % 3) != 0, "Failed to join with joinable thread '%s'!", childName); } else { LE_INFO("Successfully joined with thread '%s', which returned %p.", childName, threadReturnValue); LE_FATAL_IF(((i + 1) % 3) == 0, "Joined with non-joinable thread '%s'!", childName); LE_FATAL_IF(threadReturnValue != completionObjPtr, "Thread returned strange value %p. Expected %p.", threadReturnValue, completionObjPtr); } } }
static void TestPools ( le_mem_PoolRef_t idPool, ///< [IN] ID pool le_mem_PoolRef_t colourPool, ///< [IN] Colour pool le_mem_PoolRef_t stringPool ///< [IN] String pool ) { idObj_t* idsPtr[ID_POOL_SIZE + NUM_EXTRA_ID] = {NULL}; colourObj_t* coloursPtr[COLOUR_POOL_SIZE] = {NULL}; char* stringsPtr[4*STRING_POOL_SIZE] = {NULL}; unsigned int i = 0; unsigned int numRelease = 0; LE_TEST_BEGIN_SKIP(!LE_CONFIG_IS_ENABLED(LE_CONFIG_LINUX), 1); #if LE_CONFIG_LINUX // // Spawn child process and perform Assert allocation until failure. // pid_t pID = fork(); if (pID == 0) { // This is the child. // Allocate more than the available objects so the assert will kill the process. for (i = 0; i < ID_POOL_SIZE + 1; i++) { idsPtr[i] = le_mem_AssertAlloc(idPool); } exit(EXIT_SUCCESS); } else { int status; // wait for the child to terminate. wait(&status); LE_TEST_OK(WEXITSTATUS(status) == EXIT_FAILURE, "Assert allocation"); } #endif /* end LE_CONFIG_LINUX */ LE_TEST_END_SKIP(); // // Allocate all objects. // for (i = 0; i < ID_POOL_SIZE; i++) { idsPtr[i] = le_mem_TryAlloc(idPool); LE_TEST_OK(NULL != idsPtr[i], "Allocate id %d", i); if (NULL != idsPtr[i]) { idsPtr[i]->id = i; } } for (i = 0; i < COLOUR_POOL_SIZE; i++) { coloursPtr[i] = le_mem_TryAlloc(colourPool); LE_TEST_OK(NULL != coloursPtr[i], "Allocate color %d", i); if (NULL != idsPtr[i]) { coloursPtr[i]->r = i; coloursPtr[i]->g = i + 1; coloursPtr[i]->b = i + 2; } } // // Check objects // for (i = 0; i < ID_POOL_SIZE; i++) { LE_TEST_OK(idsPtr[i] && (idsPtr[i]->id == i), "Check id %d", i); } for (i = 0; i < COLOUR_POOL_SIZE; i++) { LE_TEST_OK(coloursPtr[i] && ((coloursPtr[i]->r == i) && (coloursPtr[i]->g == i+1) && (coloursPtr[i]->b == i+2)), "Check color %d", i); } // // Randomly release some objects. // { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); //seed the random number generator with the clock srand((unsigned int)(ts.tv_nsec/1000)^ts.tv_sec); idObj_t* objPtr; unsigned int numNotReleased = 0; NumRelease = 0; for (i = 0; i < ID_POOL_SIZE; i++) { objPtr = idsPtr[i]; if (rand() < REMOVE_THRESHOLD) { // Increase the ref number for these objects. le_mem_AddRef(objPtr); // These objects should not get freed. numNotReleased++; } else { idsPtr[i] = NULL; } // Release all objects but only objects that did not have their ref count increased will be released. le_mem_Release(objPtr); } LE_TEST_OK((NumRelease == ID_POOL_SIZE - numNotReleased), "Released %u/%d objects (%u remaining)", NumRelease, ID_POOL_SIZE, numNotReleased); // Release the rest of the objects. for (i = 0; i < ID_POOL_SIZE; i++) { if (idsPtr[i] != NULL) { le_mem_Release(idsPtr[i]); idsPtr[i] = NULL; } } // Check the number of free objects. LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND || !LE_CONFIG_IS_ENABLED(LE_CONFIG_MEM_POOL_STATS), 1); le_mem_PoolStats_t stats; le_mem_GetStats(idPool, &stats); LE_TEST_OK((stats.numFree == ID_POOL_SIZE), "Released all objects correctly"); LE_TEST_END_SKIP(); LE_TEST_BEGIN_SKIP(!LE_CONFIG_IS_ENABLED(LE_CONFIG_LINUX), 2); #if LE_CONFIG_LINUX // Spawn child process and try to release an object that is already released. pid_t pID = fork(); if (pID == 0) { // This is the child. // This allocation should fail and kill the process. le_mem_Release(objPtr); exit(EXIT_SUCCESS); } else { int status; // wait for the child to terminate. wait(&status); LE_TEST_OK(( WEXITSTATUS(status) == EXIT_FAILURE ), "Double free terminates process"); // Child terminated with an error. } #endif /* end LE_CONFIG_LINUX */ LE_TEST_END_SKIP(); } // // Try allocate until full. // for (i = 0; i < ID_POOL_SIZE; i++) { if (NULL == idsPtr[i]) { idsPtr[i] = le_mem_TryAlloc(idPool); LE_TEST_OK(NULL != idsPtr[i], "Allocate id %d", i); } } // The pool should now be empty. LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND, 1); LE_TEST_OK (le_mem_TryAlloc(idPool) == NULL, "Allocate from empty pool"); LE_TEST_END_SKIP(); // // Force allocate. // le_mem_SetNumObjsToForce(idPool, FORCE_SIZE); for (i = ID_POOL_SIZE; i < ID_POOL_SIZE + NUM_EXTRA_ID; i++) { idsPtr[i] = le_mem_ForceAlloc(idPool); LE_TEST_OK((NULL != idsPtr[i]), "Force alloc id %d", i); } // // Get stats. // le_mem_PoolStats_t stats; LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND || !LE_CONFIG_IS_ENABLED(LE_CONFIG_MEM_POOL_STATS), 2); le_mem_GetStats(idPool, &stats); LE_TEST_OK(((stats.numAllocs == ID_POOL_SIZE + NUM_EXTRA_ID + NumRelease) && (stats.numOverflows == (unsigned int)ceil(((1.0*NUM_EXTRA_ID / FORCE_SIZE)))) && (stats.numFree == (stats.numOverflows*FORCE_SIZE) % NUM_EXTRA_ID)), "Check stats"); // // Get pool size. // LE_TEST_OK((le_mem_GetObjectCount(idPool) == ID_POOL_SIZE + (stats.numOverflows * FORCE_SIZE)), "Check pool size"); LE_TEST_END_SKIP(); // // Get object size. // LE_TEST_OK((le_mem_GetObjectSize(idPool) == sizeof(idObj_t)), "Check object size"); // // Reset stats. // LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND || !LE_CONFIG_IS_ENABLED(LE_CONFIG_MEM_POOL_STATS), 1); { size_t numFree = stats.numFree; le_mem_ResetStats(idPool); le_mem_GetStats(idPool, &stats); LE_TEST_OK(((stats.numAllocs == 0) && (stats.numOverflows == 0) && (stats.numFree == numFree) ), "Check reset stats"); } LE_TEST_END_SKIP(); // // Create sub-pool. // // Release some objs from the super-pool in a random manner. numRelease = 0; for (i = 0; i < COLOUR_POOL_SIZE; i++) { if (rand() < REMOVE_THRESHOLD) { le_mem_Release(coloursPtr[i]); coloursPtr[i] = NULL; numRelease++; } } // Create the sub-pool. le_mem_PoolRef_t colourSubPool1 = le_mem_CreateSubPool(colourPool, "Colour sub-pool", numRelease); // // Check sub-pools and super-pool. // LE_TEST_OK(NULL != colourSubPool1, "Create sub-pool"); LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND, 1); LE_TEST_OK(( (le_mem_GetObjectCount(colourSubPool1) == numRelease) && (le_mem_GetObjectCount(colourPool) == COLOUR_POOL_SIZE) ), "Check sub-pool size"); LE_TEST_END_SKIP(); // // Create second sub-pool. // // Release the rest of the objects from the super-pool. for (i = 0; i < COLOUR_POOL_SIZE; i++) { if (coloursPtr[i] != NULL) { le_mem_Release(coloursPtr[i]); coloursPtr[i] = NULL; } } // Create another sub-pool. le_mem_PoolRef_t colourSubPool2 = le_mem_CreateSubPool(colourPool, "Second sub-pool", COLOUR_POOL_SIZE - numRelease); LE_TEST_OK(NULL != colourSubPool2, "Create second sub-pool"); // // Expand the sub-pool causing the super-pool to expand. // le_mem_ExpandPool(colourSubPool2, NUM_EXPAND_SUB_POOL); // // Allocate from sub-pool. // for (i = 0; i < COLOUR_POOL_SIZE - numRelease; i++) { coloursPtr[i] = le_mem_TryAlloc(colourSubPool2); LE_TEST_OK(NULL != coloursPtr[i], "Allocate color %d from sub-pool", i); } // // Check pools. // LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND || !LE_CONFIG_IS_ENABLED(LE_CONFIG_MEM_POOL_STATS), 4); le_mem_GetStats(colourPool, &stats); LE_TEST_OK(( (le_mem_GetObjectCount(colourPool) == COLOUR_POOL_SIZE + NUM_EXPAND_SUB_POOL) && (stats.numFree == 0) ), "Check super-pool stats"); le_mem_GetStats(colourSubPool1, &stats); LE_TEST_OK(( (le_mem_GetObjectCount(colourSubPool1) == numRelease) && (stats.numFree == numRelease) ), "Check sub-pool stats"); le_mem_GetStats(colourSubPool2, &stats); LE_TEST_OK(( (le_mem_GetObjectCount(colourSubPool2) == COLOUR_POOL_SIZE - numRelease + NUM_EXPAND_SUB_POOL) && (stats.numFree == NUM_EXPAND_SUB_POOL) ), "Check second sub-pool stats"); // Try Allocating from empty super-pool. LE_TEST_OK(le_mem_TryAlloc(colourPool) == NULL, "Allocate from empty super-pool"); LE_TEST_END_SKIP(); // // Delete sub-pool. // le_mem_DeleteSubPool(colourSubPool1); // Allocate from the super-pool. for (i = 0; i < NUM_ALLOC_SUPER_POOL; i++) { if (COLOUR_POOL_SIZE > numRelease) { coloursPtr[numRelease] = le_mem_TryAlloc(colourPool); LE_TEST_OK(NULL != coloursPtr[numRelease], "Allocate item %d from super-pool", numRelease); } } // // Check pools. // LE_TEST_BEGIN_SKIP(TEST_MEM_VALGRIND || !LE_CONFIG_IS_ENABLED(LE_CONFIG_MEM_POOL_STATS), 3); le_mem_GetStats(colourPool, &stats); LE_TEST_OK((stats.numFree == numRelease - NUM_ALLOC_SUPER_POOL), "checking super-pool stats after releasing sub-pool"); le_mem_GetStats(colourSubPool2, &stats); LE_TEST_OK(( (le_mem_GetObjectCount(colourSubPool2) == COLOUR_POOL_SIZE - numRelease + NUM_EXPAND_SUB_POOL) && (stats.numFree == NUM_EXPAND_SUB_POOL) ), "checking second sub-pool stats after releasing sub-pool"); // // Re-create sub-pool causing super pool to expand. // colourSubPool1 = le_mem_CreateSubPool(colourPool, "First sub-pool", numRelease + NUM_EXPAND_SUB_POOL); LE_TEST_OK(((le_mem_GetObjectCount(colourSubPool1) == numRelease + NUM_EXPAND_SUB_POOL) && (le_mem_GetObjectCount(colourPool) == COLOUR_POOL_SIZE + 2*NUM_EXPAND_SUB_POOL + NUM_ALLOC_SUPER_POOL)), "recreated sub-pool"); LE_TEST_END_SKIP(); // Create some reduced sub-pools le_mem_PoolRef_t tieredStrPoolMed = le_mem_CreateReducedPool(stringPool, "stringPoolMed", 0, STRING_POOL_MED_BYTES); le_mem_PoolRef_t tieredStrPoolSmall = le_mem_CreateReducedPool(tieredStrPoolMed, "stringPoolSmall", 4, STRING_POOL_SMALL_BYTES); size_t medObjectSize = le_mem_GetObjectSize(tieredStrPoolMed); size_t smallObjectSize = le_mem_GetObjectSize(tieredStrPoolSmall); LE_TEST_OK(STRING_POOL_MED_BYTES <= medObjectSize && medObjectSize < STRING_POOL_BYTES/2, "Check medium pool size (%"PRIuS") is reasonable", medObjectSize); LE_TEST_OK(STRING_POOL_SMALL_BYTES <= smallObjectSize && smallObjectSize < medObjectSize/2, "Check small pool size (%"PRIuS") is reasonable", smallObjectSize); // Try allocating random sized strings int points = STRING_POOL_SIZE*4; for (i = 0; i < 4*STRING_POOL_SIZE && points >= 4; i++) { // Always allocate at least one byte size_t allocSize = (rand() % (STRING_POOL_BYTES-1)) + 1; stringsPtr[i] = le_mem_ForceVarAlloc(tieredStrPoolSmall, allocSize); LE_TEST_OK(stringsPtr[i], "allocate buffer %d (size %"PRIuS")", i, allocSize); memset(stringsPtr[i], 'a', allocSize); if (allocSize <= smallObjectSize) { points -= 1; LE_TEST_OK(le_mem_GetBlockSize(stringsPtr[i]) == smallObjectSize, "got a small object"); } else if (allocSize <= medObjectSize) { points -= 2; LE_TEST_OK(le_mem_GetBlockSize(stringsPtr[i]) == medObjectSize, "got a medium object"); } else { points -= 4; LE_TEST_OK(le_mem_GetBlockSize(stringsPtr[i]) == STRING_POOL_BYTES, "got a large object"); } } // Now try hibernating -- first disable interrupts LE_TEST_BEGIN_SKIP(!LE_CONFIG_IS_ENABLED(LE_CONFIG_RTOS), 3); #if LE_CONFIG_RTOS taskENTER_CRITICAL(); void *beginFree, *endFree; le_mem_Hibernate(&beginFree, &endFree); LE_TEST_OK(endFree - beginFree > 0, "Free %" PRIuS " bytes of memory by hibernating", endFree - beginFree); le_mem_Resume(); taskEXIT_CRITICAL(); #endif /* end LE_CONFIG_RTOS */ LE_TEST_END_SKIP(); // Now finish up by allocating some small strings for(; i < 4*STRING_POOL_SIZE && points >= 0; ++i) { stringsPtr[i] = le_mem_ForceVarAlloc(tieredStrPoolSmall, 1); LE_TEST_OK(stringsPtr[i], "allocate buffer %d (size 1)", i); memset(stringsPtr[i], 'b', 1); LE_TEST_OK(le_mem_GetBlockSize(stringsPtr[i]) == smallObjectSize, "got a small object"); points -= 1; } LE_TEST_INFO("Releasing some buffers"); for (i = 0; i < 4*STRING_POOL_SIZE && stringsPtr[i]; i++) { if (rand() < REMOVE_THRESHOLD) { le_mem_Release(stringsPtr[i]); stringsPtr[i] = NULL; numRelease++; } } LE_TEST_INFO("Re-allocate as small buffers"); for (; i-- > 0; ) { if (!stringsPtr[i]) { stringsPtr[i] = le_mem_ForceVarAlloc(tieredStrPoolSmall, 1); LE_TEST_OK(stringsPtr[i], "allocated a small buffer"); memset(stringsPtr[i], 'c', 1); } } LE_TEST_INFO("Free everything"); for (i = 0; i < 4*STRING_POOL_SIZE && stringsPtr[i]; ++i) { le_mem_Release(stringsPtr[i]); stringsPtr[i] = NULL; } // And delete the sub-pools LE_TEST_INFO("Delete sub-pools"); le_mem_DeleteSubPool(tieredStrPoolSmall); le_mem_DeleteSubPool(tieredStrPoolMed); }