/** * \brief Open the specified file. * * This function is an interface to open a file on disk. * It sets the handle which can later be used to read, seek etc. * \param[in] pzPath character string specifying the file to open. * \param[in] flags character string specifying the file mode to be used. * \param[out] pHandle the passed address will point to the file handle on success. * \return Returns TRUE if file was opened successfully, FALSE otherwise. */ int File_Open( char * pzPath, char * flags, void ** pHandle ) { if ( (*pHandle = fopen(pzPath, flags) ) == NULL ) { PMS_SHOW_ERROR(" ***ASSERT*** File_Open: Failed to open file %s \n", pzPath); return FALSE; } return TRUE; }
/*! \brief Start a new thread. * * UNIX/Linux * * \param start_address Funtion pointer to the threads entry point. * \param stack_size Size of stack to use, in bytes. * \param arglist Pointer to argument list. * \return Pointer to thread (casted to void * for platform portability) * */ void *PMS_BeginThread(void( *start_address )( void * ), unsigned int stack_size, void *arglist ) { pthread_attr_t attr; unsigned int result; #ifdef PMS_OIL_MERGE_DISABLE_MEM PMS_TyThreadFunction *ptThreadInfo = OSMalloc(sizeof(PMS_TyThreadFunction), PMS_MemoryPoolPMS); #else PMS_TyThreadFunction *ptThreadInfo = mmalloc(sizeof(PMS_TyThreadFunction)); #endif if(!ptThreadInfo) { return (NULL); } ptThreadInfo->bCondBroadcasted = 0; if ( pthread_mutex_init(&ptThreadInfo->mutexThread, NULL) != 0 ) { goto thread_info_destroy; } if ( pthread_cond_init(&ptThreadInfo->condThread, NULL) != 0 ) { goto thread_mutex_destroy; } if (pthread_attr_init(&attr)) { goto thread_cond_destroy; } ptThreadInfo->start_routine = start_address; ptThreadInfo->arg = arglist; result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) || pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) || pthread_create(&ptThreadInfo->tid, &attr, PMSThreadWrapper, (void*)ptThreadInfo); (void)pthread_attr_destroy(&attr) ; if (result != 0) { PMS_SHOW_ERROR("Failed to create thread: \n"); goto thread_cond_destroy; } return (void*)ptThreadInfo; thread_cond_destroy: (void)pthread_cond_destroy(&ptThreadInfo->condThread) ; thread_mutex_destroy: (void)pthread_mutex_destroy(&ptThreadInfo->mutexThread) ; thread_info_destroy: #ifdef PMS_OIL_MERGE_DISABLE_MEM OSFree(ptThreadInfo, PMS_MemoryPoolPMS); #else mfree(ptThreadInfo); #endif return (NULL); }
/** * \brief Read the file associated with the specified handle. * * This function is an interface to read a file on disk. * \param[out] buffer the requested bytes are read into the buffer pointed to by this address * \param[in] nBytesToRead number of bytes to read * \param[in] pHandle address pointing to the file handle. * \return Returns number of bytes read on success, negative value otherwise. */ int File_Read(unsigned char * buffer, int nBytesToRead, void * handle) { int nbytes_read; if(handle==NULL) return (0); nbytes_read = (int)fread(buffer, 1, nBytesToRead, handle); if(nbytes_read < 0) { PMS_SHOW_ERROR("Failed to read from file"); fclose(handle); handle = NULL; } return (nbytes_read); }
/*! \brief Wait for a semaphore for a specified time. * * UNIX/Linux * * \param[in] semaHandle Pointer returned by \c PMS_CreateSemaphore. * \param[in] nMilliSeconds Timeout in milliseconds. * \return 1 if successful, 0 otherwise. */ int PMS_WaitOnSemaphore(void * semaHandle, unsigned int nMilliSeconds) { int val; sem_t *sem = (sem_t*)semaHandle; #ifndef MACOSX struct timespec ts; /* CLOCK_REALTIME can go up forward in leaps, and occasionally backwards. CLOCK_MONOTONIC does neither, it just keeps going forwards. */ val = clock_gettime( CLOCK_MONOTONIC, &ts ); if (val != 0) { val = clock_gettime( CLOCK_REALTIME, &ts ); if (val != 0) { PMS_SHOW_ERROR("PMS_WaitOnSemaphore: clock_gettime failed\n"); } } ts.tv_sec += (nMilliSeconds/1000); ts.tv_nsec += (nMilliSeconds%1000)*1000000; if( ts.tv_nsec > 1000000000L ) { ts.tv_sec++; ts.tv_nsec = ts.tv_nsec % 1000000000L; } #endif do { #ifdef MACOSX /* TODO: no sem_timedwait() on Mac OS X */ val = sem_wait(sem); #else #ifdef __NetBSD__ /* TODO: no sem_timedwait() on NetBSD 6.0 */ val = sem_wait(sem); #else val = sem_timedwait(sem,&ts); #endif #endif if(errno == ETIMEDOUT) return 0; } while (val != 0 && errno == EINTR); return 1; }
/** * \brief Read from File Data Stream, but move the file position back to the start of the read. * * This routine reads the requested number of bytes from the file and puts them * in the buffer and return the file position back to the start of the read.\n * It is an implementation of PMS_PeekDataStream callback. \n */ int File_PeekDataStream(unsigned char * buffer, int nBytesToRead) { int nbytes_read; /* PMS_SHOW("File_PeekDataStream(0x%p, %d)\n", buffer, nBytesToRead); */ if(g_tSystemInfo.nStoreJobBeforeRip) { if((l_nStoreBufferPos + nBytesToRead) < l_nStoreBufferUsed) { nbytes_read = nBytesToRead; } else { nbytes_read = l_nStoreBufferUsed - l_nStoreBufferPos; } memcpy(buffer, l_pStoreBuffer + l_nStoreBufferPos, nbytes_read); } else { if(l_fileCurrent==NULL) return (0); nbytes_read = (int)fread(buffer, 1, nBytesToRead, l_fileCurrent); if(nbytes_read < 0) { PMS_SHOW_ERROR("Failed to read from file"); fclose(l_fileCurrent); l_fileCurrent = NULL; } else if (nbytes_read == 0) { fclose(l_fileCurrent); l_fileCurrent = NULL; } else { fseek(l_fileCurrent, -nbytes_read, SEEK_CUR); } } return (nbytes_read); }
/** * \brief Wrapper for closing hot folder function * */ int HotFolder_CloseDataStream(void) { int nResult = 0; if(bActiveFile && !bVirtualFile) { File_CloseDataStream(); nResult = remove(aszHotFolderFiles[0]); bActiveFile = FALSE; } else if(bVirtualFile) { nResult = remove(aszHotFolderFiles[0]); bActiveFile = FALSE; bVirtualFile = FALSE; } if(nResult != 0) { PMS_SHOW_ERROR("Failed to remove file \"%s\" from hot folder.\n", aszHotFolderFiles[0]); } return 1; }
/*! \brief Create and initialize a critical section * * UNIX/Linux * * \return Pointer to critical section created and initialized (casted to \c void *). */ void * PMS_CreateCriticalSection(void) { pthread_mutex_t * ptCS; /* Mutex for critical section */ pthread_mutexattr_t mutexattr; /* Mutex attribute variable */ #ifdef PMS_OIL_MERGE_DISABLE_MEM ptCS = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); #else ptCS = (pthread_mutex_t *)mmalloc(sizeof(pthread_mutex_t)); #endif if(!ptCS) { PMS_SHOW_ERROR("Failed to allocation critical section structure\n"); return NULL; } /* Initialize the attributes set */ pthread_mutexattr_init(&mutexattr); #ifdef MACOSX /* Set the mutex as a normal mutex */ pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); #else #ifdef __NetBSD__ /* Set the mutex as a normal mutex */ pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_NORMAL); #else /* Set the mutex as a fast mutex */ pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP); #endif #endif /* create the mutex with the attributes set */ pthread_mutex_init(ptCS, &mutexattr); /* After initializing the mutex, the attribute can be destroyed */ pthread_mutexattr_destroy(&mutexattr); return (void*)ptCS; }
/** * \brief Check for memory leaks and display if there are any. * */ unsigned int CheckMemLeaks() { unsigned int retval=0; int pool; for(pool = 0; pool < PMS_NumOfMemPools; pool++) { if(l_tPMSMem[pool].bMemIsInitialised) { if(l_tPMSMem[pool].iCurrentMemory>0) { PMS_SHOW_ERROR("*** CheckMemLeaks: Pool %d - possible leak of %d ***\n", pool, l_tPMSMem[pool].iCurrentMemory); retval = 1; } } } PMS_SHOW("\n"); return (retval); }
/*! \brief Close and free a thread. * * UNIX/Linux * * \param[in] pThread Pointer to thread created by PMS_BeginThread. * \param[in] nWaitForThreadToExit Timeout in milliseconds. * Negative: wait forever or until thread exits. * Zero: do not wait. * Postive: wait for up to # milliseconds or until thread exits. * \return 0: failure, 1: success, -1: success, but timeout occured whilst waiting for thread to exit. */ int PMS_CloseThread(void *pThread, int nWaitForThreadToExit) { PMS_TyThreadFunction *pTInfo = pThread; int nRetVal = 0; if(!pTInfo) return 0; if(nWaitForThreadToExit > 0) { struct timeval now; struct timespec ts; int e; gettimeofday(&now, NULL); ts.tv_sec = now.tv_sec; ts.tv_nsec = now.tv_usec*1000; ts.tv_sec += nWaitForThreadToExit / 1000; ts.tv_nsec += nWaitForThreadToExit % 1000 * 1000000; if (ts.tv_nsec > 999999999){ ts.tv_sec++; ts.tv_nsec = ts.tv_nsec % 1000000000; } /* wait (with timeout) until thread has finished */ pthread_mutex_lock(&pTInfo->mutexThread); /* If the thread has already broadcasted the cond signal then we shouldn't wait for another one... there won't be another one. */ if(pTInfo->bCondBroadcasted == 1) { e = 0; } else { e = pthread_cond_timedwait(&pTInfo->condThread, &pTInfo->mutexThread, &ts); pthread_mutex_unlock(&pTInfo->mutexThread); } /* now join so we wait until thread has -really- finished */ if (e == ETIMEDOUT) { PMS_SHOW_ERROR("Timed out waiting for thread to exit.\n"); (void)pthread_cancel(pTInfo->tid); /* try to forcefully stop it at a cancellation point */ nRetVal = -1; } else { (void)pthread_join(pTInfo->tid, NULL); nRetVal = 1; } } else if (nWaitForThreadToExit < 0) { (void)pthread_join(pTInfo->tid, NULL) ; nRetVal = 1; } else { (void)pthread_cancel(pTInfo->tid); /* try to forcefully stop it at a cancellation point */ nRetVal = 1; } (void)pthread_cond_destroy(&pTInfo->condThread) ; (void)pthread_mutex_destroy(&pTInfo->mutexThread) ; OSFree(pTInfo, PMS_MemoryPoolPMS); return nRetVal; }
/** * \brief Open the next file. * * Opens the file contaning next job data.\n */ int File_OpenDataStream(void) { /* PMS_SHOW("File_OpenDataStream()\n"); */ if(!GetNextFilename(szJobFilename)) return -1; /* -1 means don't try opening this module again */ if ( (l_fileCurrent = fopen((char *)szJobFilename, "rb") ) == NULL ) { PMS_SHOW_ERROR(" ***ASSERT*** File_OpenDataStream: Failed to open file %s \n", szJobFilename); return 0; /* 0 means you may try this module again to try the next job */ } if(g_tSystemInfo.nStoreJobBeforeRip) { int nRead; int nChunk = 16 * 1024; PMS_SHOW("Storing job before passing to rip...\n"); l_pStoreBuffer = OSMalloc(g_tSystemInfo.cbReceiveBuffer,PMS_MemoryPoolMisc); if(!l_pStoreBuffer) { PMS_SHOW_ERROR(" ***ASSERT*** File_OpenDataStream: Failed to allocate buffer for storing job\n"); fclose(l_fileCurrent); l_fileCurrent = NULL; return 0; /* 0 means you may try this module again to try the next job */ } fseek(l_fileCurrent,0,SEEK_END); l_nStoreBufferUsed = ftell(l_fileCurrent); fseek(l_fileCurrent,0,SEEK_SET); if(l_nStoreBufferUsed > g_tSystemInfo.cbReceiveBuffer) { PMS_SHOW_ERROR(" ***ASSERT*** File_OpenDataStream: Failed to store job. Job size is larger than the memory buffer.\n"); fclose(l_fileCurrent); l_fileCurrent = NULL; OSFree(l_pStoreBuffer, PMS_MemoryPoolMisc); l_pStoreBuffer = NULL; return 0; /* 0 means you may try this module again to try the next job */ } l_nStoreBufferUsed = 0; l_nStoreBufferPos = 0; do { if((l_nStoreBufferUsed + nChunk) < g_tSystemInfo.cbReceiveBuffer) { nRead = (int)fread(l_pStoreBuffer + l_nStoreBufferUsed, 1, nChunk, l_fileCurrent); } else if(l_nStoreBufferUsed < g_tSystemInfo.cbReceiveBuffer) { nRead = (int)fread(l_pStoreBuffer + l_nStoreBufferUsed, 1, g_tSystemInfo.cbReceiveBuffer - l_nStoreBufferUsed, l_fileCurrent); } else { nRead = 0; } l_nStoreBufferUsed += nRead; } while (nRead>0); fclose(l_fileCurrent); l_fileCurrent = NULL; PMS_SHOW("Stored %d bytes.\n", l_nStoreBufferUsed); } return 1; /* 1 means we have a job ready to rip */ }
void *OSMallocEx(size_t size, PMS_TyMemPool pool) #endif { void *ptr; unsigned char *p; #ifdef PMS_MEM_LIMITED_POOLS PMS_TyMemTraceHdr hdr; #endif if(!l_tPMSMem[pool].bMemIsInitialised) { #ifdef PMS_MEM_LIMITED_POOLS memset(&l_apUsed[pool][0], 0, sizeof(l_apUsed[pool])); l_uCount[pool] = (unsigned int)-1; l_tPMSMem[pool].iCurrentMemory = 0; l_tPMSMem[pool].iPeakMemory = 0; switch(pool) { case PMS_MemoryPoolSys: l_tPMSMem[pool].iAvailMemory = g_tSystemInfo.cbSysMemory; if((g_tSystemInfo.cbSysMemory > 0) && (g_tSystemInfo.cbRIPMemory > g_tSystemInfo.cbSysMemory)) PMS_SHOW_ERROR("*** Warning : SYS MEMORY POOL %d has to be greater than [-m <RIP memory in MB>] %d.\n",g_tSystemInfo.cbSysMemory,g_tSystemInfo.cbRIPMemory); break; case PMS_MemoryPoolApp: l_tPMSMem[pool].iAvailMemory = g_tSystemInfo.cbAppMemory; break; case PMS_MemoryPoolJob: l_tPMSMem[pool].iAvailMemory = g_tSystemInfo.cbJobMemory; break; case PMS_MemoryPoolMisc: l_tPMSMem[pool].iAvailMemory = g_tSystemInfo.cbMiscMemory; break; case PMS_MemoryPoolPMS: l_tPMSMem[pool].iAvailMemory = g_tSystemInfo.cbPMSMemory; break; default: PMS_SHOW_ERROR("*** OSMalloc: Invalid memory pool - %d ***\n",pool); l_tPMSMem[pool].iAvailMemory = 0; } #endif l_tPMSMem[pool].bMemIsInitialised = 1; } #ifdef PMS_MEM_LIMITED_POOLS /* Must be multiple of 8 bytes to be aligned for doubles on un*x platforms */ size+=((sizeof(PMS_TyMemTraceHdr)+7)&~7); if(l_tPMSMem[pool].iAvailMemory) { if((l_tPMSMem[pool].iAvailMemory < l_tPMSMem[pool].iCurrentMemory) || (l_tPMSMem[pool].iAvailMemory - l_tPMSMem[pool].iCurrentMemory < (int)size)) { PMS_SHOW_ERROR(" ***ASSERT*** OSMalloc; lack of memory in memory pool. size=%u, pool=%d\n", size, pool); return NULL; } } #endif #ifdef PMS_OIL_MERGE_DISABLE_MEM ptr=(void*)malloc(size); #else ptr=(void*)MemAlloc(size, FALSE, FALSE); memVal+=size; if(memVal>count1) { count1=memVal; PMS_SHOW_ERROR("max memory %d\n", count1); } #endif p = (unsigned char*)ptr; if(!ptr) { gpsInterpNotifyError(gps_client, 30016); PMS_SHOW_ERROR(" ***ASSERT*** OSMalloc returning NULL, %u, %d\n", size, pool); } #ifdef PMS_MEM_LIMITED_POOLS else { memset(&hdr,0xAA,sizeof(hdr)); hdr.cbSize=size; hdr.nPool=pool; #ifdef SDK_MEMTRACE hdr.nLine=nLine; hdr.pszFile=pszFile; #endif hdr.pszThread=l_szDefaultThread; /*Critical section to avoid simultaneous access of global variable by multiple threads (PMS and OIL)*/ /*CRITICAL SECTION - START*/ /* Check that critical section has been created. The create cs routine calls this malloc function */ if(g_csMemoryUsage) { PMS_EnterCriticalSection(g_csMemoryUsage); } hdr.nCount=++l_uCount[pool]; memcpy(p, &hdr, sizeof(PMS_TyMemTraceHdr)); p += ((sizeof(PMS_TyMemTraceHdr)+7)&~7); #ifdef SDK_MEMTRACE if(l_chFirstCharFileName_BestGuess==' ') { l_chFirstCharFileName_BestGuess = pszFile[0]; } if(l_uCount[pool] < (sizeof(l_apUsed[pool]) / sizeof(l_apUsed[pool][0]))) l_apUsed[pool][l_uCount[pool]] = p; #endif l_tPMSMem[pool].iCurrentMemory+=(unsigned int)size; if(g_csMemoryUsage) { PMS_LeaveCriticalSection(g_csMemoryUsage); } /*CRITICAL SECTION - END*/ if(l_tPMSMem[pool].iCurrentMemory > l_tPMSMem[pool].iPeakMemory) { l_tPMSMem[pool].iPeakMemory = l_tPMSMem[pool].iCurrentMemory; } PMS_MALLOC_TRACE("OSMalloc, from %s(%d), %u, 0x%p, %d bytes, pool %d, returning p=0x%p\n", pszFile, nLine, l_uCount[pool], ptr, size, pool, p); } #endif return ((void*)p); }
/** * \brief Display memory PMS memory pool statistics. * */ void DisplayMemStats() { int pool; int i; PMS_TyMemTraceHdr *pHdr; size_t size; unsigned int uPool; unsigned int uCount; char *pszThread; #ifdef SDK_MEM_TRACE char *pszFileAlloc; int nLineAlloc; #endif unsigned int *p; PMS_SHOW_ERROR("Memory pool statistics\n"); for(pool = 0; pool < PMS_NumOfMemPools; pool++) { if(l_tPMSMem[pool].bMemIsInitialised) { switch (pool) { case PMS_MemoryPoolSys: PMS_SHOW_ERROR("Sys "); break; case PMS_MemoryPoolApp: PMS_SHOW_ERROR("App "); break; case PMS_MemoryPoolJob: PMS_SHOW_ERROR("Job "); break; case PMS_MemoryPoolMisc: PMS_SHOW_ERROR("Misc"); break; case PMS_MemoryPoolPMS: PMS_SHOW_ERROR("PMS "); break; default: PMS_SHOW_ERROR("??? "); break; } if(l_tPMSMem[pool].iAvailMemory>0) { PMS_SHOW_ERROR("\tAvail: %u\tPeak: %u\tCurrent: %u\n", l_tPMSMem[pool].iAvailMemory, l_tPMSMem[pool].iPeakMemory, l_tPMSMem[pool].iCurrentMemory); } else { PMS_SHOW_ERROR("\tAvail: no limit\tPeak: %u\tCurrent: %u\n", l_tPMSMem[pool].iPeakMemory, l_tPMSMem[pool].iCurrentMemory); } } for(i = 0; i < (sizeof(l_apUsed[pool]) / sizeof(l_apUsed[pool][0])); i++) { if(l_apUsed[pool][i]) { PMS_SHOW_ERROR("Found unfreed memory at %d, %p\n", i, l_apUsed[pool][i]); p = (unsigned int*)l_apUsed[pool][i]; pHdr = (PMS_TyMemTraceHdr *)(p - ((sizeof(PMS_TyMemTraceHdr)+7)&~7)); #ifdef SDK_MEM_TRACE nLineAlloc = pHdr->nLine; pszFileAlloc = pHdr->pszFile; #endif uCount = pHdr->nCount; uPool = pHdr->nPool; size = pHdr->cbSize; pszThread = pHdr->pszThread; #ifdef SDK_MEM_TRACE PMS_SHOW_ERROR("allocation %u from %s(%d) in thread %s, size %u, pool %u\n", uCount, pszFileAlloc, nLineAlloc, pszThread, size, uPool); #else PMS_SHOW_ERROR("allocation %u in thread %s, size %u, pool %u\n", uCount, pszThread, size, uPool); #endif } } } PMS_SHOW_ERROR("\n"); }
void OSFreeEx(void *ptr, PMS_TyMemPool pool) #endif { #ifndef SDK_MEMTRACE #ifndef PMS_MEM_LIMITED_POOLS UNUSED_PARAM((PMS_TyMemPool), pool); #endif #endif if(ptr) { #ifdef PMS_MEM_LIMITED_POOLS PMS_TyMemTraceHdr *pHdr; unsigned char *p = (unsigned char *)ptr; size_t size; unsigned int uPool; unsigned int uCount; #ifdef SDK_MEMTRACE char *pszFileAlloc; int nLineAlloc; #endif char *pszThread; p-=((sizeof(PMS_TyMemTraceHdr)+7)&~7); pHdr = (PMS_TyMemTraceHdr *)p; #ifdef SDK_MEMTRACE nLineAlloc = pHdr->nLine; pszFileAlloc = pHdr->pszFile; #endif pszThread = pHdr->pszThread; uCount = pHdr->nCount; uPool = pHdr->nPool; size = pHdr->cbSize; #ifdef SDK_MEMTRACE PMS_MALLOC_TRACE("OSFree, from %s(%d), %u, 0x%p (actual 0x%p), %d bytes, pool %d, malloc from %s(%d) in thread %s\n", pszFile, nLine, uCount, ptr, p, size, pool, pszFileAlloc?pszFileAlloc:"null", nLineAlloc, pszThread?pszThread:"null"); if(!pszThread || pszThread[0]!='G') { PMS_SHOW_ERROR("OSFree: Freeing memory that appears to have been stamped on. Unexpected thread string \"%s\"\n", pszThread?pszThread:"null"); PMS_SHOW_ERROR("OSFree, from %s(%d), %u, 0x%p (actual 0x%p), %d bytes, pool %d, malloc from %s(%d) in thread %s\n", pszFile, nLine, uCount, ptr, p, size, pool, pszFileAlloc, nLineAlloc, pszThread); } /* You may want to extend this check if your sources are compiled from various drives */ if(!pszFileAlloc || (pszFileAlloc[0]!=l_chFirstCharFileName_BestGuess)) { PMS_SHOW_ERROR("OSFree: Freeing memory that appears to have been stamped on. Unexpected source file string \"%s\"\n", pszFileAlloc?pszFileAlloc:"null"); PMS_SHOW_ERROR("OSFree, from %s(%d), %u, 0x%p (actual 0x%p), %d bytes, pool %d, malloc from %s(%d) in thread %s\n", pszFile, nLine, uCount, ptr, p, size, pool, pszFileAlloc, nLineAlloc, pszThread); } #endif #ifdef PMS_OIL_MERGE_DISABLE_MEM free(p); #else MemFree((void *)p); #endif #else #ifdef PMS_OIL_MERGE_DISABLE_MEM free(ptr); #else MemFree(ptr); #endif #endif #ifdef PMS_MEM_LIMITED_POOLS /*Critical section to avoid simultaneous access of global variable by multiple threads (PMS and OIL)*/ /*CRITICAL SECTION - START*/ if(g_csMemoryUsage) { PMS_EnterCriticalSection(g_csMemoryUsage); } l_tPMSMem[pool].iCurrentMemory-=size; #ifndef PMS_OIL_MERGE_DISABLE_MEM memVal -= size; #endif #ifdef SDK_MEMTRACE if(uCount < (sizeof(l_apUsed[pool]) / sizeof(l_apUsed[pool][0]))) l_apUsed[pool][uCount] = NULL; #endif if(g_csMemoryUsage) { PMS_LeaveCriticalSection(g_csMemoryUsage); } /*CRITICAL SECTION - END*/ #endif } }