/********************************************************************** * * Project: CPL - Common Portability Library * Purpose: CPL Multi-Threading, and process handling portability functions. * Author: Frank Warmerdam, warmerdam@pobox.com * ********************************************************************** * Copyright (c) 2002, Frank Warmerdam * Copyright (c) 2009-2013, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // Include cpl_config.h BEFORE cpl_multiproc.h, as the later may undefine // CPL_MULTIPROC_PTHREAD for mingw case. #include "cpl_config.h" #include "cpl_multiproc.h" #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS # include #endif #include #ifndef DEBUG_BOOL #include #endif #include #include #include #include #include #include #include "cpl_atomic_ops.h" #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_string.h" #include "cpl_vsi.h" CPL_CVSID("$Id: cpl_multiproc.cpp 93395860dd62e3850ed31b4c77c51a93fe2919c8 2021-01-28 16:51:58 +0100 Mateusz Łoskot $") #if defined(CPL_MULTIPROC_STUB) && !defined(DEBUG) # define MUTEX_NONE #endif // #define DEBUG_MUTEX #if defined(DEBUG) && (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) #ifndef DEBUG_CONTENTION #define DEBUG_CONTENTION #endif #endif typedef struct _CPLSpinLock CPLSpinLock; struct _CPLLock { CPLLockType eType; union { CPLMutex *hMutex; CPLSpinLock *hSpinLock; } u; #ifdef DEBUG_CONTENTION bool bDebugPerfAsked; bool bDebugPerf; volatile int nCurrentHolders; GUIntBig nStartTime; GIntBig nMaxDiff; double dfAvgDiff; GUIntBig nIters; #endif }; #ifdef DEBUG_CONTENTION #if defined(__x86_64) #define GCC_CPUID(level, a, b, c, d) \ __asm__ volatile ("xchgq %%rbx, %q1\n" \ "cpuid\n" \ "xchgq %%rbx, %q1" \ : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ : "0" (level)) #else #define GCC_CPUID(level, a, b, c, d) \ __asm__ volatile ("xchgl %%ebx, %1\n" \ "cpuid\n" \ "xchgl %%ebx, %1" \ : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ : "0" (level)) #endif static GUIntBig CPLrdtsc() { unsigned int a; unsigned int d; unsigned int unused1; unsigned int unused2; unsigned int unused3; unsigned int unused4; GCC_CPUID(0, unused1, unused2, unused3, unused4); __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d) ); return static_cast(a) | (static_cast(d) << 32); } static GUIntBig CPLrdtscp() { unsigned int a; unsigned int d; unsigned int unused1; unsigned int unused2; unsigned int unused3; unsigned int unused4; __asm__ volatile ("rdtscp" : "=a" (a), "=d" (d) ); GCC_CPUID(0, unused1, unused2, unused3, unused4); return static_cast(a) | (static_cast(d) << 32); } #endif static CPLSpinLock *CPLCreateSpinLock(); // Returned NON acquired. static int CPLCreateOrAcquireSpinLockInternal( CPLLock** ); static int CPLAcquireSpinLock( CPLSpinLock* ); static void CPLReleaseSpinLock( CPLSpinLock* ); static void CPLDestroySpinLock( CPLSpinLock* ); #ifndef CPL_MULTIPROC_PTHREAD #ifndef MUTEX_NONE static CPLMutex* CPLCreateOrAcquireMasterMutex( double ); static CPLMutex*& CPLCreateOrAcquireMasterMutexInternal( double ); static CPLMutex* CPLCreateUnacquiredMutex(); #endif #endif // We don't want it to be publicly used since it solves rather tricky issues // that are better to remain hidden. void CPLFinalizeTLS(); /************************************************************************/ /* CPLMutexHolder() */ /************************************************************************/ #ifdef MUTEX_NONE CPLMutexHolder::CPLMutexHolder( CPLMutex ** /* phMutex */, double /* dfWaitInSeconds */, const char * /* pszFileIn */, int /* nLineIn */, int /* nOptions */ ) {} #else CPLMutexHolder::CPLMutexHolder( CPLMutex **phMutex, double dfWaitInSeconds, const char *pszFileIn, int nLineIn, int nOptions ) : hMutex(nullptr), pszFile(pszFileIn), nLine(nLineIn) { if( phMutex == nullptr ) { fprintf( stderr, "CPLMutexHolder: phMutex )) NULL !\n" ); hMutex = nullptr; return; } #ifdef DEBUG_MUTEX // There is no way to use CPLDebug() here because it works with // mutexes itself so we will fall in infinite recursion. // fprintf() will do the job right. fprintf( stderr, "CPLMutexHolder: Request %p for pid %ld at %d/%s.\n", *phMutex, static_cast(CPLGetPID()), nLine, pszFile ); #else // TODO(schwehr): Find a better way to do handle this. (void)pszFile; (void)nLine; #endif if( !CPLCreateOrAcquireMutexEx( phMutex, dfWaitInSeconds, nOptions ) ) { fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); hMutex = nullptr; } else { #ifdef DEBUG_MUTEX fprintf( stderr, "CPLMutexHolder: Acquired %p for pid %ld at %d/%s.\n", *phMutex, static_cast(CPLGetPID()), nLine, pszFile ); #endif hMutex = *phMutex; } } #endif // ndef MUTEX_NONE /************************************************************************/ /* CPLMutexHolder() */ /************************************************************************/ #ifdef MUTEX_NONE CPLMutexHolder::CPLMutexHolder( CPLMutex * /* hMutexIn */, double /* dfWaitInSeconds */, const char * /* pszFileIn */, int /* nLineIn */ ) {} #else CPLMutexHolder::CPLMutexHolder( CPLMutex *hMutexIn, double dfWaitInSeconds, const char *pszFileIn, int nLineIn ) : hMutex(hMutexIn), pszFile(pszFileIn), nLine(nLineIn) { if( hMutex != nullptr && !CPLAcquireMutex( hMutex, dfWaitInSeconds ) ) { fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" ); hMutex = nullptr; } } #endif // ndef MUTEX_NONE /************************************************************************/ /* ~CPLMutexHolder() */ /************************************************************************/ #ifdef MUTEX_NONE CPLMutexHolder::~CPLMutexHolder() {} #else CPLMutexHolder::~CPLMutexHolder() { if( hMutex != nullptr ) { #ifdef DEBUG_MUTEX fprintf( stderr, "~CPLMutexHolder: Release %p for pid %ld at %d/%s.\n", hMutex, static_cast(CPLGetPID()), nLine, pszFile ); #endif CPLReleaseMutex( hMutex ); } } #endif // ndef MUTEX_NONE int CPLCreateOrAcquireMutex( CPLMutex **phMutex, double dfWaitInSeconds ) { return CPLCreateOrAcquireMutexEx(phMutex, dfWaitInSeconds, CPL_MUTEX_RECURSIVE); } /************************************************************************/ /* CPLCreateOrAcquireMutex() */ /************************************************************************/ #ifndef CPL_MULTIPROC_PTHREAD #ifndef MUTEX_NONE CPLMutex* CPLCreateUnacquiredMutex() { CPLMutex *hMutex = CPLCreateMutex(); if (hMutex) { CPLReleaseMutex(hMutex); } return hMutex; } CPLMutex*& CPLCreateOrAcquireMasterMutexInternal(double dfWaitInSeconds = 1000.0) { // The dynamic initialization of the block scope hCOAMutex // with static storage duration is thread-safe in C++11 static CPLMutex *hCOAMutex = CPLCreateUnacquiredMutex(); // WARNING: although adding an CPLAssert(hCOAMutex); might seem logical // here, do not enable it (see comment below). It calls CPLError that // uses the hCOAMutex itself leading to recursive mutex acquisition // and likely a stack overflow. if ( !hCOAMutex ) { // Fall back to this, ironically, NOT thread-safe re-initialisation of // hCOAMutex in case of a memory error or call to CPLCleanupMasterMutex // sequenced in an unusual, unexpected or erroneous way. // For example, an unusual sequence could be: // GDALDriverManager has been instantiated, // then OGRCleanupAll is called which calls CPLCleanupMasterMutex, // then CPLFreeConfig is called which acquires the hCOAMutex // that has already been released and destroyed. hCOAMutex = CPLCreateUnacquiredMutex(); } if( hCOAMutex ) { CPLAcquireMutex( hCOAMutex, dfWaitInSeconds ); } return hCOAMutex; } CPLMutex* CPLCreateOrAcquireMasterMutex(double dfWaitInSeconds = 1000.0) { CPLMutex *hCOAMutex = CPLCreateOrAcquireMasterMutexInternal(dfWaitInSeconds); return hCOAMutex; } #endif #ifdef MUTEX_NONE int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, int nOptions ) { return false; } #else int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, int nOptions ) { bool bSuccess = false; CPLMutex* hCOAMutex = CPLCreateOrAcquireMasterMutex( dfWaitInSeconds ); if( hCOAMutex == nullptr ) { *phMutex = nullptr; return FALSE; } if( *phMutex == nullptr ) { *phMutex = CPLCreateMutexEx( nOptions ); bSuccess = *phMutex != nullptr; CPLReleaseMutex( hCOAMutex ); } else { CPLReleaseMutex( hCOAMutex ); bSuccess = CPL_TO_BOOL(CPLAcquireMutex( *phMutex, dfWaitInSeconds )); } return bSuccess; } #endif // ndef MUTEX_NONE /************************************************************************/ /* CPLCreateOrAcquireMutexInternal() */ /************************************************************************/ #ifdef MUTEX_NONE static int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds, CPLLockType eType ) { return false; } #else static int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds, CPLLockType eType ) { bool bSuccess = false; CPLMutex* hCOAMutex = CPLCreateOrAcquireMasterMutex( dfWaitInSeconds ); if( hCOAMutex == nullptr ) { *phLock = nullptr; return FALSE; } if( *phLock == nullptr ) { *phLock = static_cast(calloc(1, sizeof(CPLLock))); if( *phLock ) { (*phLock)->eType = eType; (*phLock)->u.hMutex = CPLCreateMutexEx( (eType == LOCK_RECURSIVE_MUTEX) ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE ); if( (*phLock)->u.hMutex == nullptr ) { free(*phLock); *phLock = nullptr; } } bSuccess = *phLock != nullptr; CPLReleaseMutex( hCOAMutex ); } else { CPLReleaseMutex( hCOAMutex ); bSuccess = CPL_TO_BOOL(CPLAcquireMutex( (*phLock)->u.hMutex, dfWaitInSeconds )); } return bSuccess; } #endif // ndef MUTEX_NONE #endif // CPL_MULTIPROC_PTHREAD /************************************************************************/ /* CPLCleanupMasterMutex() */ /************************************************************************/ void CPLCleanupMasterMutex() { #ifndef CPL_MULTIPROC_PTHREAD #ifndef MUTEX_NONE CPLMutex*& hCOAMutex = CPLCreateOrAcquireMasterMutexInternal(); if( hCOAMutex != nullptr ) { CPLReleaseMutex( hCOAMutex ); CPLDestroyMutex( hCOAMutex ); hCOAMutex = nullptr; } #endif #endif } /************************************************************************/ /* CPLCleanupTLSList() */ /* */ /* Free resources associated with a TLS vector (implementation */ /* independent). */ /************************************************************************/ static void CPLCleanupTLSList( void **papTLSList ) { #ifdef DEBUG_VERBOSE printf( "CPLCleanupTLSList(%p)\n", papTLSList ); /*ok*/ #endif if( papTLSList == nullptr ) return; for( int i = 0; i < CTLS_MAX; i++ ) { if( papTLSList[i] != nullptr && papTLSList[i+CTLS_MAX] != nullptr ) { CPLTLSFreeFunc pfnFree = reinterpret_cast(papTLSList[i + CTLS_MAX]); pfnFree( papTLSList[i] ); papTLSList[i] = nullptr; } } CPLFree( papTLSList ); } #if defined(CPL_MULTIPROC_STUB) /************************************************************************/ /* ==================================================================== */ /* CPL_MULTIPROC_STUB */ /* */ /* Stub implementation. Mutexes don't provide exclusion, file */ /* locking is achieved with extra "lock files", and thread */ /* creation doesn't work. The PID is always just one. */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* CPLGetNumCPUs() */ /************************************************************************/ int CPLGetNumCPUs() { return 1; } /************************************************************************/ /* CPLGetThreadingModel() */ /************************************************************************/ const char *CPLGetThreadingModel() { return "stub"; } /************************************************************************/ /* CPLCreateMutex() */ /************************************************************************/ #ifdef MUTEX_NONE CPLMutex *CPLCreateMutex() { return (CPLMutex *) 0xdeadbeef; } #else CPLMutex *CPLCreateMutex() { unsigned char *pabyMutex = static_cast(malloc(4)); if( pabyMutex == nullptr ) return nullptr; pabyMutex[0] = 1; pabyMutex[1] = 'r'; pabyMutex[2] = 'e'; pabyMutex[3] = 'd'; return (CPLMutex *) pabyMutex; } #endif CPLMutex *CPLCreateMutexEx( int /*nOptions*/ ) { return CPLCreateMutex(); } /************************************************************************/ /* CPLAcquireMutex() */ /************************************************************************/ #ifdef MUTEX_NONE int CPLAcquireMutex( CPLMutex *hMutex, double /* dfWaitInSeconds */ ) { return TRUE; } #else int CPLAcquireMutex( CPLMutex *hMutex, double /*dfWaitInSeconds*/ ) { unsigned char *pabyMutex = reinterpret_cast(hMutex); CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && pabyMutex[3] == 'd' ); pabyMutex[0] += 1; return TRUE; } #endif // ! MUTEX_NONE /************************************************************************/ /* CPLReleaseMutex() */ /************************************************************************/ #ifdef MUTEX_NONE void CPLReleaseMutex( CPLMutex * /* hMutex */ ) {} #else void CPLReleaseMutex( CPLMutex *hMutex ) { unsigned char *pabyMutex = reinterpret_cast(hMutex); CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && pabyMutex[3] == 'd' ); if( pabyMutex[0] < 1 ) CPLDebug( "CPLMultiProc", "CPLReleaseMutex() called on mutex with %d as ref count!", pabyMutex[0] ); pabyMutex[0] -= 1; } #endif /************************************************************************/ /* CPLDestroyMutex() */ /************************************************************************/ #ifdef MUTEX_NONE void CPLDestroyMutex( CPLMutex * /* hMutex */ ) {} #else void CPLDestroyMutex( CPLMutex *hMutex ) { unsigned char *pabyMutex = reinterpret_cast(hMutex); CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && pabyMutex[3] == 'd' ); free( pabyMutex ); } #endif /************************************************************************/ /* CPLCreateCond() */ /************************************************************************/ CPLCond *CPLCreateCond() { return nullptr; } /************************************************************************/ /* CPLCondWait() */ /************************************************************************/ void CPLCondWait( CPLCond * /* hCond */ , CPLMutex* /* hMutex */ ) {} /************************************************************************/ /* CPLCondTimedWait() */ /************************************************************************/ CPLCondTimedWaitReason CPLCondTimedWait( CPLCond * /* hCond */ , CPLMutex* /* hMutex */, double ) { return COND_TIMED_WAIT_OTHER; } /************************************************************************/ /* CPLCondSignal() */ /************************************************************************/ void CPLCondSignal( CPLCond * /* hCond */ ) {} /************************************************************************/ /* CPLCondBroadcast() */ /************************************************************************/ void CPLCondBroadcast( CPLCond * /* hCond */ ) {} /************************************************************************/ /* CPLDestroyCond() */ /************************************************************************/ void CPLDestroyCond( CPLCond * /* hCond */ ) {} /************************************************************************/ /* CPLLockFile() */ /* */ /* Lock a file. This implementation has a terrible race */ /* condition. If we don't succeed in opening the lock file, we */ /* assume we can create one and own the target file, but other */ /* processes might easily try creating the target file at the */ /* same time, overlapping us. Death! Mayhem! The traditional */ /* solution is to use open() with _O_CREAT|_O_EXCL but this */ /* function and these arguments aren't trivially portable. */ /* Also, this still leaves a race condition on NFS drivers */ /* (apparently). */ /************************************************************************/ void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) { /* -------------------------------------------------------------------- */ /* We use a lock file with a name derived from the file we want */ /* to lock to represent the file being locked. Note that for */ /* the stub implementation the target file does not even need */ /* to exist to be locked. */ /* -------------------------------------------------------------------- */ char *pszLockFilename = static_cast( CPLMalloc(strlen(pszPath) + 30)); snprintf( pszLockFilename, strlen(pszPath) + 30, "%s.lock", pszPath ); FILE *fpLock = fopen( pszLockFilename, "r" ); while( fpLock != nullptr && dfWaitInSeconds > 0.0 ) { fclose( fpLock ); CPLSleep( std::min(dfWaitInSeconds, 0.5) ); dfWaitInSeconds -= 0.5; fpLock = fopen( pszLockFilename, "r" ); } if( fpLock != nullptr ) { fclose( fpLock ); CPLFree( pszLockFilename ); return nullptr; } fpLock = fopen( pszLockFilename, "w" ); if( fpLock == nullptr ) { CPLFree( pszLockFilename ); return nullptr; } fwrite( "held\n", 1, 5, fpLock ); fclose( fpLock ); return pszLockFilename; } /************************************************************************/ /* CPLUnlockFile() */ /************************************************************************/ void CPLUnlockFile( void *hLock ) { char *pszLockFilename = static_cast(hLock); if( hLock == nullptr ) return; VSIUnlink( pszLockFilename ); CPLFree( pszLockFilename ); } /************************************************************************/ /* CPLGetPID() */ /************************************************************************/ GIntBig CPLGetPID() { return 1; } /************************************************************************/ /* CPLCreateThread(); */ /************************************************************************/ int CPLCreateThread( CPLThreadFunc /* pfnMain */, void * /* pArg */) { CPLDebug( "CPLCreateThread", "Fails to dummy implementation" ); return -1; } /************************************************************************/ /* CPLCreateJoinableThread() */ /************************************************************************/ CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc /* pfnMain */, void * /* pThreadArg */ ) { CPLDebug( "CPLCreateJoinableThread", "Fails to dummy implementation" ); return nullptr; } /************************************************************************/ /* CPLJoinThread() */ /************************************************************************/ void CPLJoinThread( CPLJoinableThread* /* hJoinableThread */ ) {} /************************************************************************/ /* CPLSleep() */ /************************************************************************/ void CPLSleep( double dfWaitInSeconds ) { time_t ltime; time( <ime ); const time_t ttime = ltime + static_cast(dfWaitInSeconds + 0.5); for( ; ltime < ttime; time(<ime) ) { // Currently we just busy wait. Perhaps we could at least block on io? } } /************************************************************************/ /* CPLGetTLSList() */ /************************************************************************/ static void **papTLSList = nullptr; static void **CPLGetTLSList( int *pbMemoryErrorOccurred ) { if( pbMemoryErrorOccurred ) *pbMemoryErrorOccurred = FALSE; if( papTLSList == nullptr ) { papTLSList = static_cast(VSICalloc(sizeof(void*), CTLS_MAX * 2)); if( papTLSList == nullptr ) { if( pbMemoryErrorOccurred ) { *pbMemoryErrorOccurred = TRUE; fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n"); return nullptr; } CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); } } return papTLSList; } /************************************************************************/ /* CPLFinalizeTLS() */ /************************************************************************/ void CPLFinalizeTLS() { CPLCleanupTLS(); } /************************************************************************/ /* CPLCleanupTLS() */ /************************************************************************/ void CPLCleanupTLS() { CPLCleanupTLSList( papTLSList ); papTLSList = nullptr; } // endif CPL_MULTIPROC_STUB #elif defined(CPL_MULTIPROC_WIN32) /************************************************************************/ /* ==================================================================== */ /* CPL_MULTIPROC_WIN32 */ /* */ /* WIN32 Implementation of multiprocessing functions. */ /* ==================================================================== */ /************************************************************************/ /* InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x403 */ #undef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #include /************************************************************************/ /* CPLGetNumCPUs() */ /************************************************************************/ int CPLGetNumCPUs() { SYSTEM_INFO info; GetSystemInfo(&info); const DWORD dwNum = info.dwNumberOfProcessors; if( dwNum < 1 ) return 1; return static_cast(dwNum); } /************************************************************************/ /* CPLGetThreadingModel() */ /************************************************************************/ const char *CPLGetThreadingModel() { return "win32"; } /************************************************************************/ /* CPLCreateMutex() */ /************************************************************************/ CPLMutex *CPLCreateMutex() { #ifdef USE_WIN32_MUTEX HANDLE hMutex = CreateMutex( nullptr, TRUE, nullptr ); return (CPLMutex *) hMutex; #else // Do not use CPLMalloc() since its debugging infrastructure // can call the CPL*Mutex functions. CRITICAL_SECTION *pcs = static_cast(malloc(sizeof(*pcs))); if( pcs ) { InitializeCriticalSectionAndSpinCount(pcs, 4000); EnterCriticalSection(pcs); } return reinterpret_cast(pcs); #endif } CPLMutex *CPLCreateMutexEx( int /* nOptions */ ) { return CPLCreateMutex(); } /************************************************************************/ /* CPLAcquireMutex() */ /************************************************************************/ int CPLAcquireMutex( CPLMutex *hMutexIn, double dfWaitInSeconds ) { #ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; const DWORD hr = WaitForSingleObject(hMutex, static_cast(dfWaitInSeconds * 1000)); return hr != WAIT_TIMEOUT; #else CRITICAL_SECTION *pcs = reinterpret_cast(hMutexIn); BOOL ret; if( dfWaitInSeconds >= 1000.0 ) { // We assume this is the synonymous for infinite, so it is more // efficient to use EnterCriticalSection() directly EnterCriticalSection(pcs); ret = TRUE; } else { while( (ret = TryEnterCriticalSection(pcs)) == 0 && dfWaitInSeconds > 0.0 ) { CPLSleep( std::min(dfWaitInSeconds, 0.01) ); dfWaitInSeconds -= 0.01; } } return ret; #endif } /************************************************************************/ /* CPLReleaseMutex() */ /************************************************************************/ void CPLReleaseMutex( CPLMutex *hMutexIn ) { #ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; ReleaseMutex( hMutex ); #else CRITICAL_SECTION *pcs = reinterpret_cast(hMutexIn); LeaveCriticalSection(pcs); #endif } /************************************************************************/ /* CPLDestroyMutex() */ /************************************************************************/ void CPLDestroyMutex( CPLMutex *hMutexIn ) { #ifdef USE_WIN32_MUTEX HANDLE hMutex = (HANDLE) hMutexIn; CloseHandle( hMutex ); #else CRITICAL_SECTION *pcs = reinterpret_cast(hMutexIn); DeleteCriticalSection( pcs ); free( pcs ); #endif } /************************************************************************/ /* CPLCreateCond() */ /************************************************************************/ struct _WaiterItem { HANDLE hEvent; struct _WaiterItem* psNext; }; typedef struct _WaiterItem WaiterItem; typedef struct { CPLMutex *hInternalMutex; WaiterItem *psWaiterList; } Win32Cond; CPLCond *CPLCreateCond() { Win32Cond* psCond = static_cast(malloc(sizeof(Win32Cond))); if( psCond == nullptr ) return nullptr; psCond->hInternalMutex = CPLCreateMutex(); if( psCond->hInternalMutex == nullptr ) { free(psCond); return nullptr; } CPLReleaseMutex(psCond->hInternalMutex); psCond->psWaiterList = nullptr; return reinterpret_cast(psCond); } /************************************************************************/ /* CPLCondWait() */ /************************************************************************/ static void CPLTLSFreeEvent( void* pData ) { CloseHandle(static_cast(pData)); } void CPLCondWait( CPLCond *hCond, CPLMutex* hClientMutex ) { CPLCondTimedWait(hCond, hClientMutex, -1); } /************************************************************************/ /* CPLCondTimedWait() */ /************************************************************************/ CPLCondTimedWaitReason CPLCondTimedWait( CPLCond *hCond, CPLMutex* hClientMutex, double dfWaitInSeconds ) { Win32Cond* psCond = reinterpret_cast(hCond); HANDLE hEvent = static_cast(CPLGetTLS(CTLS_WIN32_COND)); if( hEvent == nullptr ) { hEvent = CreateEvent(nullptr, /* security attributes */ 0, /* manual reset = no */ 0, /* initial state = unsignaled */ nullptr /* no name */); CPLAssert(hEvent != nullptr); CPLSetTLSWithFreeFunc(CTLS_WIN32_COND, hEvent, CPLTLSFreeEvent); } /* Insert the waiter into the waiter list of the condition */ CPLAcquireMutex(psCond->hInternalMutex, 1000.0); WaiterItem* psItem = static_cast(malloc(sizeof(WaiterItem))); CPLAssert(psItem != nullptr); psItem->hEvent = hEvent; psItem->psNext = psCond->psWaiterList; psCond->psWaiterList = psItem; CPLReleaseMutex(psCond->hInternalMutex); // Release the client mutex before waiting for the event being signaled. CPLReleaseMutex(hClientMutex); // Ideally we would check that we do not get WAIT_FAILED but it is hard // to report a failure. auto ret = WaitForSingleObject(hEvent, dfWaitInSeconds < 0 ? INFINITE : static_cast(dfWaitInSeconds * 1000)); // Reacquire the client mutex. CPLAcquireMutex(hClientMutex, 1000.0); if( ret == WAIT_OBJECT_0 ) return COND_TIMED_WAIT_COND; if( ret == WAIT_TIMEOUT ) return COND_TIMED_WAIT_TIME_OUT; return COND_TIMED_WAIT_OTHER; } /************************************************************************/ /* CPLCondSignal() */ /************************************************************************/ void CPLCondSignal( CPLCond *hCond ) { Win32Cond* psCond = reinterpret_cast(hCond); // Signal the first registered event, and remove it from the list. CPLAcquireMutex(psCond->hInternalMutex, 1000.0); WaiterItem* psIter = psCond->psWaiterList; if( psIter != nullptr ) { SetEvent(psIter->hEvent); psCond->psWaiterList = psIter->psNext; free(psIter); } CPLReleaseMutex(psCond->hInternalMutex); } /************************************************************************/ /* CPLCondBroadcast() */ /************************************************************************/ void CPLCondBroadcast( CPLCond *hCond ) { Win32Cond* psCond = reinterpret_cast(hCond); // Signal all the registered events, and remove them from the list. CPLAcquireMutex(psCond->hInternalMutex, 1000.0); WaiterItem* psIter = psCond->psWaiterList; while( psIter != nullptr ) { WaiterItem* psNext = psIter->psNext; SetEvent(psIter->hEvent); free(psIter); psIter = psNext; } psCond->psWaiterList = nullptr; CPLReleaseMutex(psCond->hInternalMutex); } /************************************************************************/ /* CPLDestroyCond() */ /************************************************************************/ void CPLDestroyCond( CPLCond *hCond ) { Win32Cond* psCond = reinterpret_cast(hCond); CPLDestroyMutex(psCond->hInternalMutex); psCond->hInternalMutex = nullptr; CPLAssert(psCond->psWaiterList == nullptr); free(psCond); } /************************************************************************/ /* CPLLockFile() */ /************************************************************************/ void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) { char *pszLockFilename = static_cast(CPLMalloc(strlen(pszPath) + 30)); snprintf( pszLockFilename, strlen(pszPath) + 30, "%s.lock", pszPath ); // FIXME: use CreateFileW() HANDLE hLockFile = CreateFileA(pszLockFilename, GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, nullptr); while( GetLastError() == ERROR_ALREADY_EXISTS && dfWaitInSeconds > 0.0 ) { CloseHandle( hLockFile ); CPLSleep( std::min(dfWaitInSeconds, 0.125) ); dfWaitInSeconds -= 0.125; hLockFile = CreateFileA( pszLockFilename, GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, nullptr ); } CPLFree( pszLockFilename ); if( hLockFile == INVALID_HANDLE_VALUE ) return nullptr; if( GetLastError() == ERROR_ALREADY_EXISTS ) { CloseHandle( hLockFile ); return nullptr; } return static_cast(hLockFile); } /************************************************************************/ /* CPLUnlockFile() */ /************************************************************************/ void CPLUnlockFile( void *hLock ) { HANDLE hLockFile = static_cast(hLock); CloseHandle( hLockFile ); } /************************************************************************/ /* CPLGetPID() */ /************************************************************************/ GIntBig CPLGetPID() { return static_cast(GetCurrentThreadId()); } /************************************************************************/ /* CPLStdCallThreadJacket() */ /************************************************************************/ typedef struct { void *pAppData; CPLThreadFunc pfnMain; HANDLE hThread; } CPLStdCallThreadInfo; static DWORD WINAPI CPLStdCallThreadJacket( void *pData ) { CPLStdCallThreadInfo *psInfo = static_cast(pData); psInfo->pfnMain( psInfo->pAppData ); if( psInfo->hThread == nullptr ) CPLFree( psInfo ); // Only for detached threads. CPLCleanupTLS(); return 0; } /************************************************************************/ /* CPLCreateThread() */ /* */ /* The WIN32 CreateThread() call requires an entry point that */ /* has __stdcall conventions, so we provide a jacket function */ /* to supply that. */ /************************************************************************/ int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) { CPLStdCallThreadInfo *psInfo = static_cast( CPLCalloc(sizeof(CPLStdCallThreadInfo), 1)); psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; psInfo->hThread = nullptr; DWORD nThreadId = 0; HANDLE hThread = CreateThread( nullptr, 0, CPLStdCallThreadJacket, psInfo, 0, &nThreadId ); if( hThread == nullptr ) return -1; CloseHandle( hThread ); return nThreadId; } /************************************************************************/ /* CPLCreateJoinableThread() */ /************************************************************************/ CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) { CPLStdCallThreadInfo *psInfo = static_cast( CPLCalloc(sizeof(CPLStdCallThreadInfo), 1)); psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; DWORD nThreadId = 0; HANDLE hThread = CreateThread( nullptr, 0, CPLStdCallThreadJacket, psInfo, 0, &nThreadId ); if( hThread == nullptr ) return nullptr; psInfo->hThread = hThread; return reinterpret_cast(psInfo); } /************************************************************************/ /* CPLJoinThread() */ /************************************************************************/ void CPLJoinThread( CPLJoinableThread* hJoinableThread ) { CPLStdCallThreadInfo *psInfo = reinterpret_cast(hJoinableThread); WaitForSingleObject(psInfo->hThread, INFINITE); CloseHandle( psInfo->hThread ); CPLFree( psInfo ); } /************************************************************************/ /* CPLSleep() */ /************************************************************************/ void CPLSleep( double dfWaitInSeconds ) { Sleep( static_cast(dfWaitInSeconds * 1000.0) ); } static bool bTLSKeySetup = false; static DWORD nTLSKey = 0; /************************************************************************/ /* CPLGetTLSList() */ /************************************************************************/ static void **CPLGetTLSList( int *pbMemoryErrorOccurred ) { void **papTLSList = nullptr; if( pbMemoryErrorOccurred ) *pbMemoryErrorOccurred = FALSE; if( !bTLSKeySetup ) { nTLSKey = TlsAlloc(); if( nTLSKey == TLS_OUT_OF_INDEXES ) { if( pbMemoryErrorOccurred ) { *pbMemoryErrorOccurred = TRUE; fprintf(stderr, "CPLGetTLSList(): TlsAlloc() failed!\n" ); return nullptr; } CPLEmergencyError( "CPLGetTLSList(): TlsAlloc() failed!" ); } bTLSKeySetup = true; } papTLSList = static_cast(TlsGetValue( nTLSKey )); if( papTLSList == nullptr ) { papTLSList = static_cast(VSICalloc(sizeof(void*), CTLS_MAX * 2)); if( papTLSList == nullptr ) { if( pbMemoryErrorOccurred ) { *pbMemoryErrorOccurred = TRUE; fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n" ); return nullptr; } CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); } if( TlsSetValue( nTLSKey, papTLSList ) == 0 ) { if( pbMemoryErrorOccurred ) { *pbMemoryErrorOccurred = TRUE; fprintf(stderr, "CPLGetTLSList(): TlsSetValue() failed!\n" ); return nullptr; } CPLEmergencyError( "CPLGetTLSList(): TlsSetValue() failed!" ); } } return papTLSList; } /************************************************************************/ /* CPLFinalizeTLS() */ /************************************************************************/ void CPLFinalizeTLS() { CPLCleanupTLS(); } /************************************************************************/ /* CPLCleanupTLS() */ /************************************************************************/ void CPLCleanupTLS() { if( !bTLSKeySetup ) return; void **papTLSList = static_cast(TlsGetValue( nTLSKey )); if( papTLSList == nullptr ) return; TlsSetValue( nTLSKey, nullptr ); CPLCleanupTLSList( papTLSList ); } // endif CPL_MULTIPROC_WIN32 #elif defined(CPL_MULTIPROC_PTHREAD) #include #include #include #include /************************************************************************/ /* ==================================================================== */ /* CPL_MULTIPROC_PTHREAD */ /* */ /* PTHREAD Implementation of multiprocessing functions. */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* CPLGetNumCPUs() */ /************************************************************************/ int CPLGetNumCPUs() { int nCPUs; #ifdef _SC_NPROCESSORS_ONLN nCPUs = static_cast(sysconf(_SC_NPROCESSORS_ONLN)); #else nCPUs = 1; #endif // In a Docker/LXC containers the number of CPUs might be limited FILE* f = fopen("/sys/fs/cgroup/cpuset/cpuset.cpus", "rb"); if(f) { constexpr size_t nMaxCPUs = 8*64; // 8 Sockets * 64 threads = 512 constexpr size_t nBuffSize(nMaxCPUs*4); // 3 digits + delimiter per CPU char* pszBuffer = reinterpret_cast(CPLMalloc(nBuffSize)); const size_t nRead = fread(pszBuffer, 1, nBuffSize - 1, f); pszBuffer[nRead] = 0; fclose(f); char **papszCPUsList = CSLTokenizeStringComplex(pszBuffer, ",", FALSE, FALSE); CPLFree(pszBuffer); int nCpusetCpus = 0; for(int i = 0; papszCPUsList[i] != nullptr; ++i) { if(strchr(papszCPUsList[i], '-')) { char **papszCPUsRange = CSLTokenizeStringComplex(papszCPUsList[i], "-", FALSE, FALSE); if(CSLCount(papszCPUsRange) == 2) { int iBegin(atoi(papszCPUsRange[0])); int iEnd(atoi(papszCPUsRange[1])); nCpusetCpus += (iEnd - iBegin + 1); } CSLDestroy(papszCPUsRange); } else { ++nCpusetCpus; } } CSLDestroy(papszCPUsList); nCPUs = std::min(nCPUs, std::max(1, nCpusetCpus)); } return nCPUs; } /************************************************************************/ /* CPLCreateOrAcquireMutex() */ /************************************************************************/ #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic pop #endif static CPLMutex *CPLCreateMutexInternal( bool bAlreadyInGlobalLock, int nOptions ); int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, int nOptions ) { bool bSuccess = false; pthread_mutex_lock(&global_mutex); if( *phMutex == nullptr ) { *phMutex = CPLCreateMutexInternal(true, nOptions); bSuccess = *phMutex != nullptr; pthread_mutex_unlock(&global_mutex); } else { pthread_mutex_unlock(&global_mutex); bSuccess = CPL_TO_BOOL(CPLAcquireMutex( *phMutex, dfWaitInSeconds )); } return bSuccess; } /************************************************************************/ /* CPLCreateOrAcquireMutexInternal() */ /************************************************************************/ static int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds, CPLLockType eType ) { bool bSuccess = false; pthread_mutex_lock(&global_mutex); if( *phLock == nullptr ) { *phLock = static_cast(calloc(1, sizeof(CPLLock))); if( *phLock ) { (*phLock)->eType = eType; (*phLock)->u.hMutex = CPLCreateMutexInternal( true, eType == LOCK_RECURSIVE_MUTEX ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE ); if( (*phLock)->u.hMutex == nullptr ) { free(*phLock); *phLock = nullptr; } } bSuccess = *phLock != nullptr; pthread_mutex_unlock(&global_mutex); } else { pthread_mutex_unlock(&global_mutex); bSuccess = CPL_TO_BOOL( CPLAcquireMutex( (*phLock)->u.hMutex, dfWaitInSeconds )); } return bSuccess; } /************************************************************************/ /* CPLGetThreadingModel() */ /************************************************************************/ const char *CPLGetThreadingModel() { return "pthread"; } /************************************************************************/ /* CPLCreateMutex() */ /************************************************************************/ typedef struct _MutexLinkedElt MutexLinkedElt; struct _MutexLinkedElt { pthread_mutex_t sMutex; int nOptions; _MutexLinkedElt *psPrev; _MutexLinkedElt *psNext; }; static MutexLinkedElt* psMutexList = nullptr; static void CPLInitMutex( MutexLinkedElt* psItem ) { if( psItem->nOptions == CPL_MUTEX_REGULAR ) { #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif pthread_mutex_t tmp_mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic pop #endif psItem->sMutex = tmp_mutex; return; } // When an adaptive mutex is required, we can safely fallback to regular // mutex if we don't have HAVE_PTHREAD_MUTEX_ADAPTIVE_NP. if( psItem->nOptions == CPL_MUTEX_ADAPTIVE ) { #if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ADAPTIVE_NP ); pthread_mutex_init( &(psItem->sMutex), &attr ); #else pthread_mutex_t tmp_mutex = PTHREAD_MUTEX_INITIALIZER; psItem->sMutex = tmp_mutex; #endif return; } #if defined(PTHREAD_MUTEX_RECURSIVE) || defined(HAVE_PTHREAD_MUTEX_RECURSIVE) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); pthread_mutex_init( &(psItem->sMutex), &attr ); } // BSDs have PTHREAD_MUTEX_RECURSIVE as an enum, not a define. // But they have #define MUTEX_TYPE_COUNTING_FAST PTHREAD_MUTEX_RECURSIVE #elif defined(MUTEX_TYPE_COUNTING_FAST) { pthread_mutexattr_t attr; pthread_mutexattr_init( &attr ); pthread_mutexattr_settype( &attr, MUTEX_TYPE_COUNTING_FAST ); pthread_mutex_init( &(psItem->sMutex), &attr ); } #elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) pthread_mutex_t tmp_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; psItem->sMutex = tmp_mutex; #else #error "Recursive mutexes apparently unsupported, configure --without-threads" #endif } static CPLMutex *CPLCreateMutexInternal( bool bAlreadyInGlobalLock, int nOptions ) { MutexLinkedElt* psItem = static_cast( malloc(sizeof(MutexLinkedElt)) ); if( psItem == nullptr ) { fprintf(stderr, "CPLCreateMutexInternal() failed.\n"); return nullptr; } if( !bAlreadyInGlobalLock ) pthread_mutex_lock(&global_mutex); psItem->psPrev = nullptr; psItem->psNext = psMutexList; if( psMutexList ) psMutexList->psPrev = psItem; psMutexList = psItem; if( !bAlreadyInGlobalLock ) pthread_mutex_unlock(&global_mutex); psItem->nOptions = nOptions; CPLInitMutex(psItem); // Mutexes are implicitly acquired when created. CPLAcquireMutex( reinterpret_cast(psItem), 0.0 ); return reinterpret_cast(psItem); } CPLMutex *CPLCreateMutex() { return CPLCreateMutexInternal(false, CPL_MUTEX_RECURSIVE); } CPLMutex *CPLCreateMutexEx( int nOptions ) { return CPLCreateMutexInternal(false, nOptions); } /************************************************************************/ /* CPLAcquireMutex() */ /************************************************************************/ int CPLAcquireMutex( CPLMutex *hMutexIn, double /* dfWaitInSeconds */ ) { // TODO: Need to add timeout support. MutexLinkedElt* psItem = reinterpret_cast(hMutexIn); const int err = pthread_mutex_lock( &(psItem->sMutex) ); if( err != 0 ) { if( err == EDEADLK ) fprintf(stderr, "CPLAcquireMutex: Error = %d/EDEADLK\n", err ); else fprintf(stderr, "CPLAcquireMutex: Error = %d (%s)\n", err, strerror(err)); return FALSE; } return TRUE; } /************************************************************************/ /* CPLReleaseMutex() */ /************************************************************************/ void CPLReleaseMutex( CPLMutex *hMutexIn ) { MutexLinkedElt* psItem = reinterpret_cast(hMutexIn); const int err = pthread_mutex_unlock( &(psItem->sMutex) ); if( err != 0 ) { fprintf(stderr, "CPLReleaseMutex: Error = %d (%s)\n", err, strerror(err)); } } /************************************************************************/ /* CPLDestroyMutex() */ /************************************************************************/ void CPLDestroyMutex( CPLMutex *hMutexIn ) { MutexLinkedElt* psItem = reinterpret_cast(hMutexIn); const int err = pthread_mutex_destroy( &(psItem->sMutex) ); if( err != 0 ) { fprintf(stderr, "CPLDestroyMutex: Error = %d (%s)\n", err, strerror(err)); } pthread_mutex_lock(&global_mutex); if( psItem->psPrev ) psItem->psPrev->psNext = psItem->psNext; if( psItem->psNext ) psItem->psNext->psPrev = psItem->psPrev; if( psItem == psMutexList ) psMutexList = psItem->psNext; pthread_mutex_unlock(&global_mutex); free( hMutexIn ); } /************************************************************************/ /* CPLReinitAllMutex() */ /************************************************************************/ // Used by gdalclientserver.cpp just after forking, to avoid // deadlocks while mixing threads with fork. void CPLReinitAllMutex(); // TODO(schwehr): Put this in a header. void CPLReinitAllMutex() { MutexLinkedElt* psItem = psMutexList; while( psItem != nullptr ) { CPLInitMutex(psItem); psItem = psItem->psNext; } #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #endif pthread_mutex_t tmp_global_mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT #pragma GCC diagnostic pop #endif global_mutex = tmp_global_mutex; } /************************************************************************/ /* CPLCreateCond() */ /************************************************************************/ CPLCond *CPLCreateCond() { pthread_cond_t* pCond = static_cast(malloc(sizeof(pthread_cond_t))); if( pCond && pthread_cond_init(pCond, nullptr) == 0 ) return reinterpret_cast(pCond); fprintf(stderr, "CPLCreateCond() failed.\n"); free(pCond); return nullptr; } /************************************************************************/ /* CPLCondWait() */ /************************************************************************/ void CPLCondWait( CPLCond *hCond, CPLMutex* hMutex ) { pthread_cond_t* pCond = reinterpret_cast(hCond); MutexLinkedElt* psItem = reinterpret_cast(hMutex); pthread_mutex_t * pMutex = &(psItem->sMutex); pthread_cond_wait(pCond, pMutex); } /************************************************************************/ /* CPLCondTimedWait() */ /************************************************************************/ CPLCondTimedWaitReason CPLCondTimedWait( CPLCond *hCond, CPLMutex* hMutex, double dfWaitInSeconds ) { pthread_cond_t* pCond = reinterpret_cast(hCond); MutexLinkedElt* psItem = reinterpret_cast(hMutex); pthread_mutex_t * pMutex = &(psItem->sMutex); struct timeval tv; struct timespec ts; gettimeofday(&tv, nullptr); ts.tv_sec = time(nullptr) + static_cast(dfWaitInSeconds); ts.tv_nsec = tv.tv_usec * 1000 + static_cast( 1000 * 1000 * 1000 * fmod(dfWaitInSeconds, 1)); ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); ts.tv_nsec %= (1000 * 1000 * 1000); int ret = pthread_cond_timedwait(pCond, pMutex, &ts); if( ret == 0 ) return COND_TIMED_WAIT_COND; else if( ret == ETIMEDOUT ) return COND_TIMED_WAIT_TIME_OUT; else return COND_TIMED_WAIT_OTHER; } /************************************************************************/ /* CPLCondSignal() */ /************************************************************************/ void CPLCondSignal( CPLCond *hCond ) { pthread_cond_t* pCond = reinterpret_cast(hCond); pthread_cond_signal(pCond); } /************************************************************************/ /* CPLCondBroadcast() */ /************************************************************************/ void CPLCondBroadcast( CPLCond *hCond ) { pthread_cond_t* pCond = reinterpret_cast(hCond); pthread_cond_broadcast(pCond); } /************************************************************************/ /* CPLDestroyCond() */ /************************************************************************/ void CPLDestroyCond( CPLCond *hCond ) { pthread_cond_t* pCond = reinterpret_cast(hCond); pthread_cond_destroy(pCond); free(hCond); } /************************************************************************/ /* CPLLockFile() */ /* */ /* This is really a stub implementation, see first */ /* CPLLockFile() for caveats. */ /************************************************************************/ void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) { /* -------------------------------------------------------------------- */ /* We use a lock file with a name derived from the file we want */ /* to lock to represent the file being locked. Note that for */ /* the stub implementation the target file does not even need */ /* to exist to be locked. */ /* -------------------------------------------------------------------- */ const size_t nLen = strlen(pszPath) + 30; char *pszLockFilename = static_cast(CPLMalloc(nLen)); snprintf( pszLockFilename, nLen, "%s.lock", pszPath ); FILE *fpLock = fopen( pszLockFilename, "r" ); while( fpLock != nullptr && dfWaitInSeconds > 0.0 ) { fclose( fpLock ); CPLSleep( std::min(dfWaitInSeconds, 0.5) ); dfWaitInSeconds -= 0.5; fpLock = fopen( pszLockFilename, "r" ); } if( fpLock != nullptr ) { fclose( fpLock ); CPLFree( pszLockFilename ); return nullptr; } fpLock = fopen( pszLockFilename, "w" ); if( fpLock == nullptr ) { CPLFree( pszLockFilename ); return nullptr; } fwrite( "held\n", 1, 5, fpLock ); fclose( fpLock ); return pszLockFilename; } /************************************************************************/ /* CPLUnlockFile() */ /************************************************************************/ void CPLUnlockFile( void *hLock ) { char *pszLockFilename = static_cast(hLock); if( hLock == nullptr ) return; VSIUnlink( pszLockFilename ); CPLFree( pszLockFilename ); } /************************************************************************/ /* CPLGetPID() */ /************************************************************************/ GIntBig CPLGetPID() { return reinterpret_cast(reinterpret_cast(pthread_self())); } static pthread_key_t oTLSKey; static pthread_once_t oTLSKeySetup = PTHREAD_ONCE_INIT; /************************************************************************/ /* CPLMake_key() */ /************************************************************************/ static void CPLMake_key() { if( pthread_key_create( &oTLSKey, reinterpret_cast(CPLCleanupTLSList) ) != 0 ) { CPLError( CE_Fatal, CPLE_AppDefined, "pthread_key_create() failed!" ); } } /************************************************************************/ /* CPLGetTLSList() */ /************************************************************************/ static void **CPLGetTLSList( int* pbMemoryErrorOccurred ) { if( pbMemoryErrorOccurred ) *pbMemoryErrorOccurred = FALSE; if( pthread_once(&oTLSKeySetup, CPLMake_key) != 0 ) { if( pbMemoryErrorOccurred ) { fprintf(stderr, "CPLGetTLSList(): pthread_once() failed!\n" ); *pbMemoryErrorOccurred = TRUE; return nullptr; } CPLEmergencyError( "CPLGetTLSList(): pthread_once() failed!" ); } void **papTLSList = static_cast(pthread_getspecific( oTLSKey )); if( papTLSList == nullptr ) { papTLSList = static_cast(VSICalloc(sizeof(void*), CTLS_MAX * 2)); if( papTLSList == nullptr ) { if( pbMemoryErrorOccurred ) { fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n" ); *pbMemoryErrorOccurred = TRUE; return nullptr; } CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); } if( pthread_setspecific( oTLSKey, papTLSList ) != 0 ) { if( pbMemoryErrorOccurred ) { fprintf(stderr, "CPLGetTLSList(): pthread_setspecific() failed!\n" ); *pbMemoryErrorOccurred = TRUE; return nullptr; } CPLEmergencyError( "CPLGetTLSList(): pthread_setspecific() failed!" ); } } return papTLSList; } /************************************************************************/ /* CPLStdCallThreadJacket() */ /************************************************************************/ typedef struct { void *pAppData; CPLThreadFunc pfnMain; pthread_t hThread; bool bJoinable; #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS bool bInitSucceeded; bool bInitDone; pthread_mutex_t sMutex; pthread_cond_t sCond; #endif } CPLStdCallThreadInfo; static void *CPLStdCallThreadJacket( void *pData ) { CPLStdCallThreadInfo *psInfo = static_cast(pData); #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS int bMemoryError = FALSE; CPLGetTLSList(&bMemoryError); if( bMemoryError ) goto error; assert( pthread_mutex_lock( &(psInfo->sMutex) ) == 0); psInfo->bInitDone = true; assert( pthread_cond_signal( &(psInfo->sCond) ) == 0); assert( pthread_mutex_unlock( &(psInfo->sMutex) ) == 0); #endif psInfo->pfnMain( psInfo->pAppData ); if( !psInfo->bJoinable ) CPLFree( psInfo ); return nullptr; #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS error: assert( pthread_mutex_lock( &(psInfo->sMutex) ) == 0); psInfo->bInitSucceeded = false; psInfo->bInitDone = true; assert( pthread_cond_signal( &(psInfo->sCond) ) == 0); assert( pthread_mutex_unlock( &(psInfo->sMutex) ) == 0); return nullptr; #endif } /************************************************************************/ /* CPLCreateThread() */ /* */ /* The WIN32 CreateThread() call requires an entry point that */ /* has __stdcall conventions, so we provide a jacket function */ /* to supply that. */ /************************************************************************/ int CPLCreateThread( CPLThreadFunc pfnMain, void *pThreadArg ) { CPLStdCallThreadInfo *psInfo = static_cast( VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo), 1)); if( psInfo == nullptr ) return -1; psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; psInfo->bJoinable = false; #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS psInfo->bInitSucceeded = true; psInfo->bInitDone = false; pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; psInfo->sMutex = sMutex; if( pthread_cond_init(&(psInfo->sCond), nullptr) != 0 ) { CPLFree( psInfo ); fprintf(stderr, "CPLCreateThread() failed.\n"); return -1; } #endif pthread_attr_t hThreadAttr; pthread_attr_init( &hThreadAttr ); pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); if( pthread_create( &(psInfo->hThread), &hThreadAttr, CPLStdCallThreadJacket, static_cast(psInfo) ) != 0 ) { #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS pthread_cond_destroy(&(psInfo->sCond)); #endif CPLFree( psInfo ); fprintf(stderr, "CPLCreateThread() failed.\n"); return -1; } #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS bool bInitSucceeded; while( true ) { assert(pthread_mutex_lock( &(psInfo->sMutex) ) == 0); bool bInitDone = psInfo->bInitDone; if( !bInitDone ) assert( pthread_cond_wait( &(psInfo->sCond), &(psInfo->sMutex)) == 0); bInitSucceeded = psInfo->bInitSucceeded; assert(pthread_mutex_unlock( &(psInfo->sMutex) ) == 0); if( bInitDone ) break; } pthread_cond_destroy(&(psInfo->sCond)); if( !bInitSucceeded ) { CPLFree( psInfo ); fprintf(stderr, "CPLCreateThread() failed.\n"); return -1; } #endif return 1; // Can we return the actual thread pid? } /************************************************************************/ /* CPLCreateJoinableThread() */ /************************************************************************/ CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg ) { CPLStdCallThreadInfo *psInfo = static_cast( VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo), 1)); if( psInfo == nullptr ) return nullptr; psInfo->pAppData = pThreadArg; psInfo->pfnMain = pfnMain; psInfo->bJoinable = true; #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS psInfo->bInitSucceeded = true; psInfo->bInitDone = false; pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; psInfo->sMutex = sMutex; { int err = pthread_cond_init(&(psInfo->sCond), nullptr); if( err != 0 ) { CPLFree( psInfo ); fprintf(stderr, "CPLCreateJoinableThread() failed: %s.\n", strerror(err)); return nullptr; } } #endif pthread_attr_t hThreadAttr; pthread_attr_init( &hThreadAttr ); pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_JOINABLE ); int err = pthread_create( &(psInfo->hThread), &hThreadAttr, CPLStdCallThreadJacket, static_cast(psInfo) ); if( err != 0 ) { #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS pthread_cond_destroy(&(psInfo->sCond)); #endif CPLFree( psInfo ); fprintf(stderr, "CPLCreateJoinableThread() failed: %s.\n", strerror(err)); return nullptr; } #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS bool bInitSucceeded; while( true ) { assert(pthread_mutex_lock( &(psInfo->sMutex) ) == 0); bool bInitDone = psInfo->bInitDone; if( !bInitDone ) assert( pthread_cond_wait( &(psInfo->sCond), &(psInfo->sMutex)) == 0); bInitSucceeded = psInfo->bInitSucceeded; assert(pthread_mutex_unlock( &(psInfo->sMutex) ) == 0); if( bInitDone ) break; } pthread_cond_destroy(&(psInfo->sCond)); if( !bInitSucceeded ) { void* status; pthread_join( psInfo->hThread, &status); CPLFree( psInfo ); fprintf(stderr, "CPLCreateJoinableThread() failed.\n"); return nullptr; } #endif return reinterpret_cast(psInfo); } /************************************************************************/ /* CPLJoinThread() */ /************************************************************************/ void CPLJoinThread(CPLJoinableThread* hJoinableThread) { CPLStdCallThreadInfo *psInfo = reinterpret_cast(hJoinableThread); if( psInfo == nullptr ) return; void* status; pthread_join( psInfo->hThread, &status); CPLFree(psInfo); } /************************************************************************/ /* CPLSleep() */ /************************************************************************/ void CPLSleep( double dfWaitInSeconds ) { struct timespec sRequest; struct timespec sRemain; sRequest.tv_sec = static_cast(floor(dfWaitInSeconds)); sRequest.tv_nsec = static_cast((dfWaitInSeconds - sRequest.tv_sec) * 1000000000); nanosleep( &sRequest, &sRemain ); } /************************************************************************/ /* CPLFinalizeTLS() */ /************************************************************************/ void CPLFinalizeTLS() { CPLCleanupTLS(); // See #5509 for the explanation why this may be needed. pthread_key_delete(oTLSKey); } /************************************************************************/ /* CPLCleanupTLS() */ /************************************************************************/ void CPLCleanupTLS() { void **papTLSList = static_cast(pthread_getspecific( oTLSKey )); if( papTLSList == nullptr ) return; pthread_setspecific( oTLSKey, nullptr ); CPLCleanupTLSList( papTLSList ); } /************************************************************************/ /* CPLCreateSpinLock() */ /************************************************************************/ #if defined(HAVE_PTHREAD_SPINLOCK) #define HAVE_SPINLOCK_IMPL struct _CPLSpinLock { pthread_spinlock_t spin; }; CPLSpinLock *CPLCreateSpinLock() { CPLSpinLock* psSpin = static_cast(malloc(sizeof(CPLSpinLock))); if( psSpin != nullptr && pthread_spin_init(&(psSpin->spin), PTHREAD_PROCESS_PRIVATE) == 0 ) { return psSpin; } else { fprintf(stderr, "CPLCreateSpinLock() failed.\n"); free(psSpin); return nullptr; } } /************************************************************************/ /* CPLAcquireSpinLock() */ /************************************************************************/ int CPLAcquireSpinLock( CPLSpinLock* psSpin ) { return pthread_spin_lock( &(psSpin->spin) ) == 0; } /************************************************************************/ /* CPLCreateOrAcquireSpinLockInternal() */ /************************************************************************/ int CPLCreateOrAcquireSpinLockInternal( CPLLock** ppsLock ) { pthread_mutex_lock(&global_mutex); if( *ppsLock == nullptr ) { *ppsLock = static_cast(calloc(1, sizeof(CPLLock))); if( *ppsLock != nullptr ) { (*ppsLock)->eType = LOCK_SPIN; (*ppsLock)->u.hSpinLock = CPLCreateSpinLock(); if( (*ppsLock)->u.hSpinLock == nullptr ) { free(*ppsLock); *ppsLock = nullptr; } } } pthread_mutex_unlock(&global_mutex); // coverity[missing_unlock] return( *ppsLock != nullptr && CPLAcquireSpinLock( (*ppsLock)->u.hSpinLock ) ); } /************************************************************************/ /* CPLReleaseSpinLock() */ /************************************************************************/ void CPLReleaseSpinLock( CPLSpinLock* psSpin ) { pthread_spin_unlock( &(psSpin->spin) ); } /************************************************************************/ /* CPLDestroySpinLock() */ /************************************************************************/ void CPLDestroySpinLock( CPLSpinLock* psSpin ) { pthread_spin_destroy( &(psSpin->spin) ); free( psSpin ); } #endif // HAVE_PTHREAD_SPINLOCK #endif // def CPL_MULTIPROC_PTHREAD /************************************************************************/ /* CPLGetTLS() */ /************************************************************************/ void *CPLGetTLS( int nIndex ) { void** l_papTLSList = CPLGetTLSList(nullptr); CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); return l_papTLSList[nIndex]; } /************************************************************************/ /* CPLGetTLSEx() */ /************************************************************************/ void *CPLGetTLSEx( int nIndex, int* pbMemoryErrorOccurred ) { void** l_papTLSList = CPLGetTLSList(pbMemoryErrorOccurred); if( l_papTLSList == nullptr ) return nullptr; CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); return l_papTLSList[nIndex]; } /************************************************************************/ /* CPLSetTLS() */ /************************************************************************/ void CPLSetTLS( int nIndex, void *pData, int bFreeOnExit ) { CPLSetTLSWithFreeFunc(nIndex, pData, (bFreeOnExit) ? CPLFree : nullptr); } /************************************************************************/ /* CPLSetTLSWithFreeFunc() */ /************************************************************************/ // Warning: The CPLTLSFreeFunc must not in any case directly or indirectly // use or fetch any TLS data, or a terminating thread will hang! void CPLSetTLSWithFreeFunc( int nIndex, void *pData, CPLTLSFreeFunc pfnFree ) { void **l_papTLSList = CPLGetTLSList(nullptr); CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); l_papTLSList[nIndex] = pData; l_papTLSList[CTLS_MAX + nIndex] = reinterpret_cast(pfnFree); } /************************************************************************/ /* CPLSetTLSWithFreeFuncEx() */ /************************************************************************/ // Warning: the CPLTLSFreeFunc must not in any case directly or indirectly // use or fetch any TLS data, or a terminating thread will hang! void CPLSetTLSWithFreeFuncEx( int nIndex, void *pData, CPLTLSFreeFunc pfnFree, int* pbMemoryErrorOccurred ) { void **l_papTLSList = CPLGetTLSList(pbMemoryErrorOccurred); CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX ); l_papTLSList[nIndex] = pData; l_papTLSList[CTLS_MAX + nIndex] = reinterpret_cast(pfnFree); } #ifndef HAVE_SPINLOCK_IMPL // No spinlock specific API? Fallback to mutex. /************************************************************************/ /* CPLCreateSpinLock() */ /************************************************************************/ CPLSpinLock *CPLCreateSpinLock( void ) { CPLSpinLock* psSpin = reinterpret_cast(CPLCreateMutex()); if( psSpin ) CPLReleaseSpinLock(psSpin); return psSpin; } /************************************************************************/ /* CPLCreateOrAcquireSpinLock() */ /************************************************************************/ int CPLCreateOrAcquireSpinLockInternal( CPLLock** ppsLock ) { return CPLCreateOrAcquireMutexInternal( ppsLock, 1000, LOCK_ADAPTIVE_MUTEX ); } /************************************************************************/ /* CPLAcquireSpinLock() */ /************************************************************************/ int CPLAcquireSpinLock( CPLSpinLock* psSpin ) { return CPLAcquireMutex( reinterpret_cast(psSpin), 1000 ); } /************************************************************************/ /* CPLReleaseSpinLock() */ /************************************************************************/ void CPLReleaseSpinLock( CPLSpinLock* psSpin ) { CPLReleaseMutex( reinterpret_cast(psSpin) ); } /************************************************************************/ /* CPLDestroySpinLock() */ /************************************************************************/ void CPLDestroySpinLock( CPLSpinLock* psSpin ) { CPLDestroyMutex( reinterpret_cast(psSpin) ); } #endif // HAVE_SPINLOCK_IMPL /************************************************************************/ /* CPLCreateLock() */ /************************************************************************/ CPLLock *CPLCreateLock( CPLLockType eType ) { switch( eType ) { case LOCK_RECURSIVE_MUTEX: case LOCK_ADAPTIVE_MUTEX: { CPLMutex* hMutex = CPLCreateMutexEx( eType == LOCK_RECURSIVE_MUTEX ? CPL_MUTEX_RECURSIVE : CPL_MUTEX_ADAPTIVE); if( !hMutex ) return nullptr; CPLReleaseMutex(hMutex); CPLLock* psLock = static_cast(malloc(sizeof(CPLLock))); if( psLock == nullptr ) { fprintf(stderr, "CPLCreateLock() failed.\n"); CPLDestroyMutex(hMutex); return nullptr; } psLock->eType = eType; psLock->u.hMutex = hMutex; #ifdef DEBUG_CONTENTION psLock->bDebugPerf = false; psLock->bDebugPerfAsked = false; psLock->nCurrentHolders = 0; psLock->nStartTime = 0; #endif return psLock; } case LOCK_SPIN: { CPLSpinLock* hSpinLock = CPLCreateSpinLock(); if( !hSpinLock ) return nullptr; CPLLock* psLock = static_cast(malloc(sizeof(CPLLock))); if( psLock == nullptr ) { fprintf(stderr, "CPLCreateLock() failed.\n"); CPLDestroySpinLock(hSpinLock); return nullptr; } psLock->eType = eType; psLock->u.hSpinLock = hSpinLock; #ifdef DEBUG_CONTENTION psLock->bDebugPerf = false; psLock->bDebugPerfAsked = false; psLock->nCurrentHolders = 0; psLock->nStartTime = 0; #endif return psLock; } default: CPLAssert(false); return nullptr; } } /************************************************************************/ /* CPLCreateOrAcquireLock() */ /************************************************************************/ int CPLCreateOrAcquireLock( CPLLock** ppsLock, CPLLockType eType ) { #ifdef DEBUG_CONTENTION GUIntBig nStartTime = 0; if( (*ppsLock) && (*ppsLock)->bDebugPerfAsked ) nStartTime = CPLrdtsc(); #endif int ret = 0; switch( eType ) { case LOCK_RECURSIVE_MUTEX: case LOCK_ADAPTIVE_MUTEX: { ret = CPLCreateOrAcquireMutexInternal( ppsLock, 1000, eType); break; } case LOCK_SPIN: { ret = CPLCreateOrAcquireSpinLockInternal( ppsLock ); break; } default: CPLAssert(false); return FALSE; } #ifdef DEBUG_CONTENTION if( ret && (*ppsLock)->bDebugPerfAsked && CPLAtomicInc(&((*ppsLock)->nCurrentHolders)) == 1 ) { (*ppsLock)->bDebugPerf = true; (*ppsLock)->nStartTime = nStartTime; } #endif return ret; } /************************************************************************/ /* CPLAcquireLock() */ /************************************************************************/ int CPLAcquireLock( CPLLock* psLock ) { #ifdef DEBUG_CONTENTION GUIntBig nStartTime = 0; if( psLock->bDebugPerfAsked ) nStartTime = CPLrdtsc(); #endif int ret; if( psLock->eType == LOCK_SPIN ) ret = CPLAcquireSpinLock( psLock->u.hSpinLock ); else ret = CPLAcquireMutex( psLock->u.hMutex, 1000 ); #ifdef DEBUG_CONTENTION if( ret && psLock->bDebugPerfAsked && CPLAtomicInc(&(psLock->nCurrentHolders)) == 1 ) { psLock->bDebugPerf = true; psLock->nStartTime = nStartTime; } #endif return ret; } /************************************************************************/ /* CPLReleaseLock() */ /************************************************************************/ void CPLReleaseLock( CPLLock* psLock ) { #ifdef DEBUG_CONTENTION bool bHitMaxDiff = false; GIntBig nMaxDiff = 0; double dfAvgDiff = 0; GUIntBig nIters = 0; if( psLock->bDebugPerf && CPLAtomicDec(&(psLock->nCurrentHolders)) == 0 ) { const GUIntBig nStopTime = CPLrdtscp(); const GIntBig nDiffTime = static_cast(nStopTime - psLock->nStartTime); if( nDiffTime > psLock->nMaxDiff ) { bHitMaxDiff = true; psLock->nMaxDiff = nDiffTime; } nMaxDiff = psLock->nMaxDiff; psLock->nIters++; nIters = psLock->nIters; psLock->dfAvgDiff += (nDiffTime - psLock->dfAvgDiff) / nIters; dfAvgDiff = psLock->dfAvgDiff; } #endif if( psLock->eType == LOCK_SPIN ) CPLReleaseSpinLock( psLock->u.hSpinLock ); else CPLReleaseMutex( psLock->u.hMutex ); #ifdef DEBUG_CONTENTION if( psLock->bDebugPerf && (bHitMaxDiff || (psLock->nIters % 1000000) == (1000000-1) )) { CPLDebug("LOCK", "Lock contention : max = " CPL_FRMT_GIB ", avg = %.0f", nMaxDiff, dfAvgDiff); } #endif } /************************************************************************/ /* CPLDestroyLock() */ /************************************************************************/ void CPLDestroyLock( CPLLock* psLock ) { if( psLock->eType == LOCK_SPIN ) CPLDestroySpinLock( psLock->u.hSpinLock ); else CPLDestroyMutex( psLock->u.hMutex ); free( psLock ); } /************************************************************************/ /* CPLLockSetDebugPerf() */ /************************************************************************/ #ifdef DEBUG_CONTENTION void CPLLockSetDebugPerf(CPLLock* psLock, int bEnableIn) { psLock->bDebugPerfAsked = CPL_TO_BOOL(bEnableIn); } #else void CPLLockSetDebugPerf(CPLLock* /* psLock */, int bEnableIn) { if( !bEnableIn ) return; static bool bOnce = false; if( !bOnce ) { bOnce = true; CPLDebug("LOCK", "DEBUG_CONTENTION not available"); } } #endif /************************************************************************/ /* CPLLockHolder() */ /************************************************************************/ CPLLockHolder::CPLLockHolder( CPLLock **phLock, CPLLockType eType, const char *pszFileIn, int nLineIn ) { #ifndef MUTEX_NONE pszFile = pszFileIn; nLine = nLineIn; #ifdef DEBUG_MUTEX // XXX: There is no way to use CPLDebug() here because it works with // mutexes itself so we will fall in infinite recursion. Good old // fprintf() will do the job right. fprintf( stderr, "CPLLockHolder: Request %p for pid %ld at %d/%s.\n", *phLock, static_cast(CPLGetPID()), nLine, pszFile ); #endif if( !CPLCreateOrAcquireLock( phLock, eType ) ) { fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" ); hLock = nullptr; } else { #ifdef DEBUG_MUTEX fprintf( stderr, "CPLLockHolder: Acquired %p for pid %ld at %d/%s.\n", *phLock, static_cast(CPLGetPID()), nLine, pszFile ); #endif hLock = *phLock; } #endif // ndef MUTEX_NONE } /************************************************************************/ /* CPLLockHolder() */ /************************************************************************/ CPLLockHolder::CPLLockHolder( CPLLock *hLockIn, const char *pszFileIn, int nLineIn ) { #ifndef MUTEX_NONE pszFile = pszFileIn; nLine = nLineIn; hLock = hLockIn; if( hLock != nullptr ) { if( !CPLAcquireLock( hLock ) ) { fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" ); hLock = nullptr; } } #endif // ndef MUTEX_NONE } /************************************************************************/ /* ~CPLLockHolder() */ /************************************************************************/ CPLLockHolder::~CPLLockHolder() { #ifndef MUTEX_NONE if( hLock != nullptr ) { #ifdef DEBUG_MUTEX fprintf( stderr, "~CPLLockHolder: Release %p for pid %ld at %d/%s.\n", hLock, static_cast(CPLGetPID()), nLine, pszFile ); #endif CPLReleaseLock( hLock ); } #endif // ndef MUTEX_NONE } /************************************************************************/ /* CPLGetCurrentProcessID() */ /************************************************************************/ #ifdef CPL_MULTIPROC_WIN32 int CPLGetCurrentProcessID() { return GetCurrentProcessId(); } #else #include #include int CPLGetCurrentProcessID() { return getpid(); } #endif