/**********************************************************************
 * $Id: cpl_multiproc.cpp 33817 2016-03-30 17:35:37Z rouault $
 *
 * 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 <even dot rouault at mines-paris dot org>
 *
 * 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_multiproc.h"

#include "cpl_conv.h"

#include <time.h>
#include <assert.h>

CPL_CVSID("$Id: cpl_multiproc.cpp 33817 2016-03-30 17:35:37Z rouault $");

#if defined(CPL_MULTIPROC_STUB) && !defined(DEBUG)
#  define MUTEX_NONE
#endif

//#define DEBUG_MUTEX

#if defined(DEBUG) && (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
#define DEBUG_CONTENTION
#endif

typedef struct _CPLSpinLock CPLSpinLock;

struct _CPLLock
{
    CPLLockType eType;
    union
    {
        CPLMutex        *hMutex;
        CPLSpinLock     *hSpinLock;
    } u;

#ifdef DEBUG_CONTENTION
    int      bDebugPerf;
    GUIntBig nStartTime;
    GIntBig  nMaxDiff;
    double   dfAvgDiff;
    GUIntBig nIters;
#endif
};

#ifdef DEBUG_CONTENTION
static GUIntBig CPLrdtsc(void)
{
   unsigned int a, d, x, y;
   __asm__ volatile ("cpuid" : "=a" (x), "=d" (y) : "a"(0) : "cx", "bx" );
   __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d) );
   return ((GUIntBig )a) | (((GUIntBig )d) << 32);
}

static GUIntBig CPLrdtscp(void)
{
   unsigned int a, d, x, y;
   __asm__ volatile ("rdtscp" : "=a" (a), "=d" (d) );
   __asm__ volatile ("cpuid"  : "=a" (x), "=d" (y) : "a"(0) : "cx", "bx" );
   return ((GUIntBig )a) | (((GUIntBig )d) << 32);
}
#endif

static CPLSpinLock   *CPLCreateSpinLock( void ); /* returned NON acquired */
static int     CPLCreateOrAcquireSpinLockInternal( CPLLock** );
static int     CPLAcquireSpinLock( CPLSpinLock* );
static void    CPLReleaseSpinLock( CPLSpinLock* );
static void    CPLDestroySpinLock( CPLSpinLock* );

/* We don't want it to be publicly used since it solves rather tricky issues */
/* that are better to remain hidden... */
void CPLFinalizeTLS();

/************************************************************************/
/*                           CPLMutexHolder()                           */
/************************************************************************/

CPLMutexHolder::CPLMutexHolder( CPLMutex **phMutex, double dfWaitInSeconds,
                                const char *pszFileIn,
                                int nLineIn,
                                int nOptions )

{
#ifndef MUTEX_NONE
    pszFile = pszFileIn;
    nLine = nLineIn;

    if( phMutex == NULL )
    {
        fprintf( stderr, "CPLMutexHolder: phMutex )) NULL !\n" );
        hMutex = NULL;
        return;
    }

#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,
             "CPLMutexHolder: Request %p for pid %ld at %d/%s.\n",
             *phMutex, (long) CPLGetPID(), nLine, pszFile );
#endif

    if( !CPLCreateOrAcquireMutexEx( phMutex, dfWaitInSeconds, nOptions ) )
    {
        fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" );
        hMutex = NULL;
    }
    else
    {
#ifdef DEBUG_MUTEX
        fprintf( stderr,
                 "CPLMutexHolder: Acquired %p for pid %ld at %d/%s.\n",
                 *phMutex, (long) CPLGetPID(), nLine, pszFile );
#endif

        hMutex = *phMutex;
    }
#endif /* ndef MUTEX_NONE */
}

/************************************************************************/
/*                           CPLMutexHolder()                           */
/************************************************************************/

CPLMutexHolder::CPLMutexHolder( CPLMutex *hMutexIn, double dfWaitInSeconds,
                                const char *pszFileIn,
                                int nLineIn )

{
#ifndef MUTEX_NONE
    pszFile = pszFileIn;
    nLine = nLineIn;
    hMutex = hMutexIn;

    if( hMutex != NULL &&
        !CPLAcquireMutex( hMutex, dfWaitInSeconds ) )
    {
        fprintf( stderr, "CPLMutexHolder: Failed to acquire mutex!\n" );
        hMutex = NULL;
    }
#endif /* ndef MUTEX_NONE */
}

/************************************************************************/
/*                          ~CPLMutexHolder()                           */
/************************************************************************/

CPLMutexHolder::~CPLMutexHolder()

{
#ifndef MUTEX_NONE
    if( hMutex != NULL )
    {
#ifdef DEBUG_MUTEX
        fprintf( stderr,
                 "~CPLMutexHolder: Release %p for pid %ld at %d/%s.\n",
                 hMutex, (long) 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
static CPLMutex *hCOAMutex = NULL;
#endif

int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds, int nOptions )

{
    int bSuccess = FALSE;

#ifndef MUTEX_NONE

    /*
    ** ironically, creation of this initial mutex is not threadsafe
    ** even though we use it to ensure that creation of other mutexes
    ** is threadsafe.
    */
    if( hCOAMutex == NULL )
    {
        hCOAMutex = CPLCreateMutex();
        if (hCOAMutex == NULL)
        {
            *phMutex = NULL;
            return FALSE;
        }
    }
    else
    {
        CPLAcquireMutex( hCOAMutex, dfWaitInSeconds );
    }

    if( *phMutex == NULL )
    {
        *phMutex = CPLCreateMutexEx( nOptions );
        bSuccess = *phMutex != NULL;
        CPLReleaseMutex( hCOAMutex );
    }
    else
    {
        CPLReleaseMutex( hCOAMutex );

        bSuccess = CPLAcquireMutex( *phMutex, dfWaitInSeconds );
    }
#endif /* ndef MUTEX_NONE */

    return bSuccess;
}

/************************************************************************/
/*                   CPLCreateOrAcquireMutexInternal()                  */
/************************************************************************/

static
int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds,
                                     CPLLockType eType )

{
    int bSuccess = FALSE;

#ifndef MUTEX_NONE

    /*
    ** ironically, creation of this initial mutex is not threadsafe
    ** even though we use it to ensure that creation of other mutexes
    ** is threadsafe.
    */
    if( hCOAMutex == NULL )
    {
        hCOAMutex = CPLCreateMutex();
        if (hCOAMutex == NULL)
        {
            *phLock = NULL;
            return FALSE;
        }
    }
    else
    {
        CPLAcquireMutex( hCOAMutex, dfWaitInSeconds );
    }

    if( *phLock == NULL )
    {
        *phLock = (CPLLock*) 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 == NULL )
            {
                free(*phLock);
                *phLock = NULL;
            }
        }
        bSuccess = *phLock != NULL;
        CPLReleaseMutex( hCOAMutex );
    }
    else
    {
        CPLReleaseMutex( hCOAMutex );

        bSuccess = CPLAcquireMutex( (*phLock)->u.hMutex, dfWaitInSeconds );
    }
#endif /* ndef MUTEX_NONE */

    return bSuccess;
}

#endif /* CPL_MULTIPROC_PTHREAD */

/************************************************************************/
/*                      CPLCleanupMasterMutex()                         */
/************************************************************************/

void CPLCleanupMasterMutex()
{
#ifndef CPL_MULTIPROC_PTHREAD
#ifndef MUTEX_NONE
    if( hCOAMutex != NULL )
    {
        CPLDestroyMutex( hCOAMutex );
        hCOAMutex = NULL;
    }
#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 );
#endif

    if( papTLSList == NULL )
        return;

    for( int i = 0; i < CTLS_MAX; i++ )
    {
        if( papTLSList[i] != NULL && papTLSList[i+CTLS_MAX] != NULL )
        {
            CPLTLSFreeFunc pfnFree = (CPLTLSFreeFunc) papTLSList[i + CTLS_MAX];
            pfnFree( papTLSList[i] );
            papTLSList[i] = NULL;
        }
    }

    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()                           */
/************************************************************************/

CPLMutex *CPLCreateMutex()

{
#ifndef MUTEX_NONE
    unsigned char *pabyMutex = (unsigned char *) malloc( 4 );
    if( pabyMutex == NULL )
        return NULL;

    pabyMutex[0] = 1;
    pabyMutex[1] = 'r';
    pabyMutex[2] = 'e';
    pabyMutex[3] = 'd';

    return (CPLMutex *) pabyMutex;
#else
    return (CPLMutex *) 0xdeadbeef;
#endif
}

CPLMutex *CPLCreateMutexEx(CPL_UNUSED int nOptions)

{
    return CPLCreateMutex();
}

/************************************************************************/
/*                          CPLAcquireMutex()                           */
/************************************************************************/

int CPLAcquireMutex( CPLMutex *hMutex,
                     CPL_UNUSED double dfWaitInSeconds )
{
#ifndef MUTEX_NONE
    unsigned char *pabyMutex = (unsigned char *) hMutex;

    CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e'
               && pabyMutex[3] == 'd' );

    pabyMutex[0] += 1;

    return TRUE;
#else
    return TRUE;
#endif
}

/************************************************************************/
/*                          CPLReleaseMutex()                           */
/************************************************************************/

void CPLReleaseMutex( CPLMutex *hMutex )

{
#ifndef MUTEX_NONE
    unsigned char *pabyMutex = (unsigned char *) 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()                           */
/************************************************************************/

void CPLDestroyMutex( CPLMutex *hMutex )

{
#ifndef MUTEX_NONE
    unsigned char *pabyMutex = (unsigned char *) hMutex;

    CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e'
               && pabyMutex[3] == 'd' );

    free( pabyMutex );
#endif
}

/************************************************************************/
/*                            CPLCreateCond()                           */
/************************************************************************/

CPLCond  *CPLCreateCond()
{
    return NULL;
}

/************************************************************************/
/*                            CPLCondWait()                             */
/************************************************************************/

void  CPLCondWait( CPL_UNUSED CPLCond *hCond, CPL_UNUSED CPLMutex* hMutex )
{
}

/************************************************************************/
/*                            CPLCondSignal()                           */
/************************************************************************/

void  CPLCondSignal( CPL_UNUSED CPLCond *hCond )
{
}

/************************************************************************/
/*                           CPLCondBroadcast()                         */
/************************************************************************/

void  CPLCondBroadcast( CPL_UNUSED CPLCond *hCond )
{
}

/************************************************************************/
/*                            CPLDestroyCond()                          */
/************************************************************************/

void  CPLDestroyCond( CPL_UNUSED 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 )

{
    FILE      *fpLock;
    char      *pszLockFilename;

/* -------------------------------------------------------------------- */
/*      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.                                          */
/* -------------------------------------------------------------------- */
    pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30);
    snprintf( pszLockFilename, strlen(pszPath) + 30, "%s.lock", pszPath );

    fpLock = fopen( pszLockFilename, "r" );
    while( fpLock != NULL && dfWaitInSeconds > 0.0 )
    {
        fclose( fpLock );
        CPLSleep( MIN(dfWaitInSeconds,0.5) );
        dfWaitInSeconds -= 0.5;

        fpLock = fopen( pszLockFilename, "r" );
    }

    if( fpLock != NULL )
    {
        fclose( fpLock );
        CPLFree( pszLockFilename );
        return NULL;
    }

    fpLock = fopen( pszLockFilename, "w" );

    if( fpLock == NULL )
    {
        CPLFree( pszLockFilename );
        return NULL;
    }

    fwrite( "held\n", 1, 5, fpLock );
    fclose( fpLock );

    return pszLockFilename;
}

/************************************************************************/
/*                           CPLUnlockFile()                            */
/************************************************************************/

void CPLUnlockFile( void *hLock )

{
    char *pszLockFilename = (char *) hLock;

    if( hLock == NULL )
        return;

    VSIUnlink( pszLockFilename );

    CPLFree( pszLockFilename );
}

/************************************************************************/
/*                             CPLGetPID()                              */
/************************************************************************/

GIntBig CPLGetPID()

{
    return 1;
}

/************************************************************************/
/*                          CPLCreateThread();                          */
/************************************************************************/

int CPLCreateThread( CPL_UNUSED CPLThreadFunc pfnMain, CPL_UNUSED void *pArg )
{
    CPLDebug( "CPLCreateThread", "Fails to dummy implementation" );

    return -1;
}

/************************************************************************/
/*                      CPLCreateJoinableThread()                       */
/************************************************************************/

CPLJoinableThread* CPLCreateJoinableThread( CPL_UNUSED CPLThreadFunc pfnMain, CPL_UNUSED void *pThreadArg )
{
    CPLDebug( "CPLCreateJoinableThread", "Fails to dummy implementation" );

    return NULL;
}

/************************************************************************/
/*                          CPLJoinThread()                             */
/************************************************************************/

void CPLJoinThread(CPL_UNUSED CPLJoinableThread* hJoinableThread)
{
}

/************************************************************************/
/*                              CPLSleep()                              */
/************************************************************************/

void CPLSleep( double dfWaitInSeconds )
{
    time_t  ltime;

    time( &ltime );
    time_t ttime = ltime + (int) (dfWaitInSeconds+0.5);

    for( ; ltime < ttime; time(&ltime) )
    {
        /* currently we just busy wait.  Perhaps we could at least block on
           io? */
    }
}

/************************************************************************/
/*                           CPLGetTLSList()                            */
/************************************************************************/

static void **papTLSList = NULL;

static void **CPLGetTLSList(int *pbMemoryErrorOccurred)

{
    if( pbMemoryErrorOccurred )
        *pbMemoryErrorOccurred = FALSE;
    if( papTLSList == NULL )
    {
        papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2);
        if( papTLSList == NULL )
        {
            if( pbMemoryErrorOccurred )
            {
                *pbMemoryErrorOccurred = TRUE;
                fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n");
                return NULL;
            }
            CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!");
        }
    }

    return papTLSList;
}

/************************************************************************/
/*                             CPLFinalizeTLS()                         */
/************************************************************************/

void CPLFinalizeTLS()
{
    CPLCleanupTLS();
}

/************************************************************************/
/*                           CPLCleanupTLS()                            */
/************************************************************************/

void CPLCleanupTLS()

{
    CPLCleanupTLSList( papTLSList );
    papTLSList = NULL;
}

/* 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 <windows.h>

/************************************************************************/
/*                             CPLGetNumCPUs()                          */
/************************************************************************/

int CPLGetNumCPUs()
{
    SYSTEM_INFO info;
    GetSystemInfo(&info);
    DWORD dwNum = info.dwNumberOfProcessors;
    if( dwNum < 1 )
        return 1;
    return (int)dwNum;
}

/************************************************************************/
/*                        CPLGetThreadingModel()                        */
/************************************************************************/

const char *CPLGetThreadingModel()

{
    return "win32";
}

/************************************************************************/
/*                           CPLCreateMutex()                           */
/************************************************************************/

CPLMutex *CPLCreateMutex()

{
#ifdef USE_WIN32_MUTEX
    HANDLE hMutex;

    hMutex = CreateMutex( NULL, TRUE, NULL );

    return (CPLMutex *) hMutex;
#else
    CRITICAL_SECTION *pcs;

	/* Do not use CPLMalloc() since its debugging infrastructure */
	/* can call the CPL*Mutex functions... */
    pcs = (CRITICAL_SECTION *)malloc(sizeof(*pcs));
    if( pcs )
    {
      InitializeCriticalSectionAndSpinCount(pcs, 4000);
      EnterCriticalSection(pcs);
    }

    return (CPLMutex *) pcs;
#endif
}

CPLMutex *CPLCreateMutexEx(CPL_UNUSED int nOptions)

{
    return CPLCreateMutex();
}

/************************************************************************/
/*                          CPLAcquireMutex()                           */
/************************************************************************/

int CPLAcquireMutex( CPLMutex *hMutexIn, double dfWaitInSeconds )

{
#ifdef USE_WIN32_MUTEX
    HANDLE hMutex = (HANDLE) hMutexIn;
    const DWORD hr =
        WaitForSingleObject( hMutex, (int) (dfWaitInSeconds * 1000) );

    return hr != WAIT_TIMEOUT;
#else
    CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)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( 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 = (CRITICAL_SECTION *)hMutexIn;

    LeaveCriticalSection(pcs);
#endif
}

/************************************************************************/
/*                          CPLDestroyMutex()                           */
/************************************************************************/

void CPLDestroyMutex( CPLMutex *hMutexIn )

{
#ifdef USE_WIN32_MUTEX
    HANDLE hMutex = (HANDLE) hMutexIn;

    CloseHandle( hMutex );
#else
    CRITICAL_SECTION *pcs = (CRITICAL_SECTION *)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 = (Win32Cond*) malloc(sizeof(Win32Cond));
    if (psCond == NULL)
        return NULL;
    psCond->hInternalMutex = CPLCreateMutex();
    if (psCond->hInternalMutex == NULL)
    {
        free(psCond);
        return NULL;
    }
    CPLReleaseMutex(psCond->hInternalMutex);
    psCond->psWaiterList = NULL;
    return (CPLCond*) psCond;
}

/************************************************************************/
/*                            CPLCondWait()                             */
/************************************************************************/

static void CPLTLSFreeEvent(void* pData)
{
    CloseHandle((HANDLE)pData);
}

void  CPLCondWait( CPLCond *hCond, CPLMutex* hClientMutex )
{
    Win32Cond* psCond = (Win32Cond*) hCond;

    HANDLE hEvent = (HANDLE) CPLGetTLS(CTLS_WIN32_COND);
    if (hEvent == NULL)
    {
        hEvent = CreateEvent(NULL, /* security attributes */
                             0,    /* manual reset = no */
                             0,    /* initial state = unsignaled */
                             NULL  /* no name */);
        CPLAssert(hEvent != NULL);

        CPLSetTLSWithFreeFunc(CTLS_WIN32_COND, hEvent, CPLTLSFreeEvent);
    }

    /* Insert the waiter into the waiter list of the condition */
    CPLAcquireMutex(psCond->hInternalMutex, 1000.0);

    WaiterItem* psItem = (WaiterItem*)malloc(sizeof(WaiterItem));
    CPLAssert(psItem != NULL);

    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.
    WaitForSingleObject(hEvent, INFINITE);

    /* Reacquire the client mutex */
    CPLAcquireMutex(hClientMutex, 1000.0);
}

/************************************************************************/
/*                            CPLCondSignal()                           */
/************************************************************************/

void  CPLCondSignal( CPLCond *hCond )
{
    Win32Cond* psCond = (Win32Cond*) hCond;

    /* Signal the first registered event, and remove it from the list */
    CPLAcquireMutex(psCond->hInternalMutex, 1000.0);

    WaiterItem* psIter = psCond->psWaiterList;
    if (psIter != NULL)
    {
        SetEvent(psIter->hEvent);
        psCond->psWaiterList = psIter->psNext;
        free(psIter);
    }

    CPLReleaseMutex(psCond->hInternalMutex);
}

/************************************************************************/
/*                           CPLCondBroadcast()                         */
/************************************************************************/

void  CPLCondBroadcast( CPLCond *hCond )
{
    Win32Cond* psCond = (Win32Cond*) hCond;

    /* Signal all the registered events, and remove them from the list */
    CPLAcquireMutex(psCond->hInternalMutex, 1000.0);

    WaiterItem* psIter = psCond->psWaiterList;
    while (psIter != NULL)
    {
        WaiterItem* psNext = psIter->psNext;
        SetEvent(psIter->hEvent);
        free(psIter);
        psIter = psNext;
    }
    psCond->psWaiterList = NULL;

    CPLReleaseMutex(psCond->hInternalMutex);
}

/************************************************************************/
/*                            CPLDestroyCond()                          */
/************************************************************************/

void  CPLDestroyCond( CPLCond *hCond )
{
    Win32Cond* psCond = (Win32Cond*) hCond;
    CPLDestroyMutex(psCond->hInternalMutex);
    psCond->hInternalMutex = NULL;
    CPLAssert(psCond->psWaiterList == NULL);
    free(psCond);
}

/************************************************************************/
/*                            CPLLockFile()                             */
/************************************************************************/

void *CPLLockFile( const char *pszPath, double dfWaitInSeconds )

{
    char *pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30);
    snprintf( pszLockFilename, strlen(pszPath) + 30, "%s.lock", pszPath );

    HANDLE hLockFile =
        CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL,CREATE_NEW,
                    FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, NULL );

    while( GetLastError() == ERROR_ALREADY_EXISTS
           && dfWaitInSeconds > 0.0 )
    {
        CloseHandle( hLockFile );
        CPLSleep( MIN(dfWaitInSeconds,0.125) );
        dfWaitInSeconds -= 0.125;

        hLockFile =
            CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL, CREATE_NEW,
                        FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE,
                        NULL );
    }

    CPLFree( pszLockFilename );

    if( hLockFile == INVALID_HANDLE_VALUE )
        return NULL;

    if( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        CloseHandle( hLockFile );
        return NULL;
    }

    return (void *) hLockFile;
}

/************************************************************************/
/*                           CPLUnlockFile()                            */
/************************************************************************/

void CPLUnlockFile( void *hLock )

{
    HANDLE    hLockFile = (HANDLE) hLock;

    CloseHandle( hLockFile );
}

/************************************************************************/
/*                             CPLGetPID()                              */
/************************************************************************/

GIntBig CPLGetPID()

{
    return (GIntBig) GetCurrentThreadId();
}

/************************************************************************/
/*                       CPLStdCallThreadJacket()                       */
/************************************************************************/

typedef struct {
    void *pAppData;
    CPLThreadFunc pfnMain;
    HANDLE hThread;
} CPLStdCallThreadInfo;

static DWORD WINAPI CPLStdCallThreadJacket( void *pData )

{
    CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) pData;

    psInfo->pfnMain( psInfo->pAppData );

    if (psInfo->hThread == NULL)
        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;

    psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1);
    psInfo->pAppData = pThreadArg;
    psInfo->pfnMain = pfnMain;
    psInfo->hThread = NULL;

    DWORD nThreadId = 0;
    HANDLE hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo,
                                   0, &nThreadId );

    if( hThread == NULL )
        return -1;

    CloseHandle( hThread );

    return nThreadId;
}

/************************************************************************/
/*                      CPLCreateJoinableThread()                       */
/************************************************************************/

CPLJoinableThread* CPLCreateJoinableThread( CPLThreadFunc pfnMain, void *pThreadArg )

{
    CPLStdCallThreadInfo *psInfo;

    psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1);
    psInfo->pAppData = pThreadArg;
    psInfo->pfnMain = pfnMain;

    DWORD nThreadId = 0;
    HANDLE hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo,
                                   0, &nThreadId );

    if( hThread == NULL )
        return NULL;

    psInfo->hThread = hThread;
    return (CPLJoinableThread*) psInfo;
}

/************************************************************************/
/*                          CPLJoinThread()                             */
/************************************************************************/

void CPLJoinThread(CPLJoinableThread* hJoinableThread)
{
    CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) hJoinableThread;

    WaitForSingleObject(psInfo->hThread, INFINITE);
    CloseHandle( psInfo->hThread );
    CPLFree( psInfo );
}

/************************************************************************/
/*                              CPLSleep()                              */
/************************************************************************/

void CPLSleep( double dfWaitInSeconds )

{
    Sleep( (DWORD) (dfWaitInSeconds * 1000.0) );
}

static bool          bTLSKeySetup = false;
static DWORD nTLSKey = 0;

/************************************************************************/
/*                           CPLGetTLSList()                            */
/************************************************************************/

static void **CPLGetTLSList(int *pbMemoryErrorOccurred)

{
    void **papTLSList;

    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 NULL;
            }
            CPLEmergencyError( "CPLGetTLSList(): TlsAlloc() failed!" );
        }
        bTLSKeySetup = true;
    }

    papTLSList = (void **) TlsGetValue( nTLSKey );
    if( papTLSList == NULL )
    {
        papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2);
        if( papTLSList == NULL )
        {
            if( pbMemoryErrorOccurred )
            {
                *pbMemoryErrorOccurred = TRUE;
                fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n" );
                return NULL;
            }
            CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!");
        }
        if( TlsSetValue( nTLSKey, papTLSList ) == 0 )
        {
            if( pbMemoryErrorOccurred )
            {
                *pbMemoryErrorOccurred = TRUE;
                fprintf(stderr, "CPLGetTLSList(): TlsSetValue() failed!\n" );
                return NULL;
            }
            CPLEmergencyError( "CPLGetTLSList(): TlsSetValue() failed!" );
        }
    }

    return papTLSList;
}

/************************************************************************/
/*                             CPLFinalizeTLS()                         */
/************************************************************************/

void CPLFinalizeTLS()
{
    CPLCleanupTLS();
}

/************************************************************************/
/*                           CPLCleanupTLS()                            */
/************************************************************************/

void CPLCleanupTLS()

{
    void **papTLSList;

    if( !bTLSKeySetup )
        return;

    papTLSList = (void **) TlsGetValue( nTLSKey );
    if( papTLSList == NULL )
        return;

    TlsSetValue( nTLSKey, NULL );

    CPLCleanupTLSList( papTLSList );
}

/* endif CPL_MULTIPROC_WIN32 */

#elif defined(CPL_MULTIPROC_PTHREAD)

#include <pthread.h>
#include <time.h>
#include <unistd.h>

  /************************************************************************/
  /* ==================================================================== */
  /*                        CPL_MULTIPROC_PTHREAD                         */
  /*                                                                      */
  /*    PTHREAD Implementation of multiprocessing functions.              */
  /* ==================================================================== */
  /************************************************************************/

/************************************************************************/
/*                             CPLGetNumCPUs()                          */
/************************************************************************/

int CPLGetNumCPUs()
{
#ifdef _SC_NPROCESSORS_ONLN
    return (int)sysconf(_SC_NPROCESSORS_ONLN);
#else
    return 1;
#endif
}

/************************************************************************/
/*                      CPLCreateOrAcquireMutex()                       */
/************************************************************************/

static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER;
static CPLMutex *CPLCreateMutexInternal(int bAlreadyInGlobalLock, int nOptions);

int CPLCreateOrAcquireMutexEx( CPLMutex **phMutex, double dfWaitInSeconds,
                               int nOptions )

{
    int bSuccess = FALSE;

    pthread_mutex_lock(&global_mutex);
    if( *phMutex == NULL )
    {
        *phMutex = CPLCreateMutexInternal(TRUE, nOptions);
        bSuccess = *phMutex != NULL;
        pthread_mutex_unlock(&global_mutex);
    }
    else
    {
        pthread_mutex_unlock(&global_mutex);

        bSuccess = CPLAcquireMutex( *phMutex, dfWaitInSeconds );
    }

    return bSuccess;
}

/************************************************************************/
/*                   CPLCreateOrAcquireMutexInternal()                  */
/************************************************************************/

static
int CPLCreateOrAcquireMutexInternal( CPLLock **phLock, double dfWaitInSeconds,
                                     CPLLockType eType )
{
    int bSuccess = FALSE;

    pthread_mutex_lock(&global_mutex);
    if( *phLock == NULL )
    {
        *phLock = (CPLLock*) 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 == NULL )
            {
                free(*phLock);
                *phLock = NULL;
            }
        }
        bSuccess = *phLock != NULL;
        pthread_mutex_unlock(&global_mutex);
    }
    else
    {
        pthread_mutex_unlock(&global_mutex);

        bSuccess = 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 = NULL;

static void CPLInitMutex(MutexLinkedElt* psItem)
{
    if( psItem->nOptions == CPL_MUTEX_REGULAR )
    {
        pthread_mutex_t tmp_mutex = PTHREAD_MUTEX_INITIALIZER;
        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(int bAlreadyInGlobalLock, int nOptions)
{
    MutexLinkedElt* psItem = (MutexLinkedElt *) malloc(sizeof(MutexLinkedElt));
    if (psItem == NULL)
    {
        fprintf(stderr, "CPLCreateMutexInternal() failed.\n");
        return NULL;
    }

    if( !bAlreadyInGlobalLock )
        pthread_mutex_lock(&global_mutex);
    psItem->psPrev = NULL;
    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( (CPLMutex*)psItem, 0.0 );

    return (CPLMutex*)psItem;
}

CPLMutex *CPLCreateMutex()
{
    return CPLCreateMutexInternal(FALSE, CPL_MUTEX_RECURSIVE);
}

CPLMutex *CPLCreateMutexEx(int nOptions)
{
    return CPLCreateMutexInternal(FALSE, nOptions);
}

/************************************************************************/
/*                          CPLAcquireMutex()                           */
/************************************************************************/

int CPLAcquireMutex( CPLMutex *hMutexIn, CPL_UNUSED double dfWaitInSeconds )
{

    /* we need to add timeout support */
    MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn;
    const int err = pthread_mutex_lock( &(psItem->sMutex) );

    if( err != 0 )
    {
        if( err == EDEADLK )
            fprintf(stderr, "CPLAcquireMutex: Error = %d/EDEADLK", err );
        else
            fprintf(stderr, "CPLAcquireMutex: Error = %d", err );

        return FALSE;
    }

    return TRUE;
}

/************************************************************************/
/*                          CPLReleaseMutex()                           */
/************************************************************************/

void CPLReleaseMutex( CPLMutex *hMutexIn )

{
    MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn;
    pthread_mutex_unlock( &(psItem->sMutex) );
}

/************************************************************************/
/*                          CPLDestroyMutex()                           */
/************************************************************************/

void CPLDestroyMutex( CPLMutex *hMutexIn )

{
    MutexLinkedElt* psItem = (MutexLinkedElt *) hMutexIn;
    pthread_mutex_destroy( &(psItem->sMutex) );
    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();
void CPLReinitAllMutex()
{
    MutexLinkedElt* psItem = psMutexList;
    while(psItem != NULL )
    {
        CPLInitMutex(psItem);
        psItem = psItem->psNext;
    }
    pthread_mutex_t tmp_global_mutex = PTHREAD_MUTEX_INITIALIZER;
    global_mutex = tmp_global_mutex;
}

/************************************************************************/
/*                            CPLCreateCond()                           */
/************************************************************************/

CPLCond  *CPLCreateCond()
{
    pthread_cond_t* pCond = (pthread_cond_t* )malloc(sizeof(pthread_cond_t));
    if (pCond && pthread_cond_init(pCond, NULL) == 0 )
        return (CPLCond*) pCond;
    fprintf(stderr, "CPLCreateCond() failed.\n");
    free(pCond);
    return NULL;
}

/************************************************************************/
/*                            CPLCondWait()                             */
/************************************************************************/

void  CPLCondWait( CPLCond *hCond, CPLMutex* hMutex )
{
    pthread_cond_t* pCond = (pthread_cond_t* )hCond;
    MutexLinkedElt* psItem = (MutexLinkedElt *) hMutex;
    pthread_mutex_t * pMutex = &(psItem->sMutex);
    pthread_cond_wait(pCond,  pMutex);
}

/************************************************************************/
/*                            CPLCondSignal()                           */
/************************************************************************/

void  CPLCondSignal( CPLCond *hCond )
{
    pthread_cond_t* pCond = (pthread_cond_t* )hCond;
    pthread_cond_signal(pCond);
}

/************************************************************************/
/*                           CPLCondBroadcast()                         */
/************************************************************************/

void  CPLCondBroadcast( CPLCond *hCond )
{
    pthread_cond_t* pCond = (pthread_cond_t* )hCond;
    pthread_cond_broadcast(pCond);
}

/************************************************************************/
/*                            CPLDestroyCond()                          */
/************************************************************************/

void  CPLDestroyCond( CPLCond *hCond )
{
    pthread_cond_t* pCond = (pthread_cond_t* )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 )

{
    FILE      *fpLock;
    char      *pszLockFilename;

/* -------------------------------------------------------------------- */
/*      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;
    pszLockFilename = (char *) CPLMalloc(nLen);
    snprintf( pszLockFilename, nLen, "%s.lock", pszPath );

    fpLock = fopen( pszLockFilename, "r" );
    while( fpLock != NULL && dfWaitInSeconds > 0.0 )
    {
        fclose( fpLock );
        CPLSleep( MIN(dfWaitInSeconds,0.5) );
        dfWaitInSeconds -= 0.5;

        fpLock = fopen( pszLockFilename, "r" );
    }

    if( fpLock != NULL )
    {
        fclose( fpLock );
        CPLFree( pszLockFilename );
        return NULL;
    }

    fpLock = fopen( pszLockFilename, "w" );

    if( fpLock == NULL )
    {
        CPLFree( pszLockFilename );
        return NULL;
    }

    fwrite( "held\n", 1, 5, fpLock );
    fclose( fpLock );

    return pszLockFilename;
}

/************************************************************************/
/*                           CPLUnlockFile()                            */
/************************************************************************/

void CPLUnlockFile( void *hLock )

{
    char *pszLockFilename = (char *) hLock;

    if( hLock == NULL )
        return;

    VSIUnlink( pszLockFilename );

    CPLFree( pszLockFilename );
}

/************************************************************************/
/*                             CPLGetPID()                              */
/************************************************************************/

GIntBig CPLGetPID()

{
    return (GIntBig) 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, (void (*)(void*)) CPLCleanupTLSList ) != 0 )
    {
        CPLError( CE_Fatal, CPLE_AppDefined, "pthread_key_create() failed!" );
    }
}

/************************************************************************/
/*                           CPLGetTLSList()                            */
/************************************************************************/

static void **CPLGetTLSList(int* pbMemoryErrorOccurred)

{
    void **papTLSList;

    if( pbMemoryErrorOccurred )
        *pbMemoryErrorOccurred = FALSE;
    if ( pthread_once(&oTLSKeySetup, CPLMake_key) != 0 )
    {
        if( pbMemoryErrorOccurred )
        {
            fprintf(stderr, "CPLGetTLSList(): pthread_once() failed!\n" );
            *pbMemoryErrorOccurred = TRUE;
            return NULL;
        }
        CPLEmergencyError( "CPLGetTLSList(): pthread_once() failed!" );
    }

    papTLSList = (void **) pthread_getspecific( oTLSKey );
    if( papTLSList == NULL )
    {
        papTLSList = (void **) VSICalloc(sizeof(void*),CTLS_MAX*2);
        if( papTLSList == NULL )
        {
            if( pbMemoryErrorOccurred )
            {
                fprintf(stderr, "CPLGetTLSList() failed to allocate TLS list!\n" );
                *pbMemoryErrorOccurred = TRUE;
                return NULL;
            }
            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 NULL;
            }
            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 = (CPLStdCallThreadInfo *) 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 NULL;

#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 NULL;
#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;
    pthread_attr_t hThreadAttr;

    psInfo = (CPLStdCallThreadInfo*) VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo),1);
    if( psInfo == NULL )
        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), NULL) != 0 )
    {
        CPLFree( psInfo );
        fprintf(stderr, "CPLCreateThread() failed.\n");
        return -1;
    }
#endif

    pthread_attr_init( &hThreadAttr );
    pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED );
    if( pthread_create( &(psInfo->hThread), &hThreadAttr,
                        CPLStdCallThreadJacket, (void *) 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;
    pthread_attr_t hThreadAttr;

    psInfo = (CPLStdCallThreadInfo*) VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo),1);
    if( psInfo == NULL )
        return NULL;
    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;
    if( pthread_cond_init(&(psInfo->sCond), NULL) != 0 )
    {
        CPLFree( psInfo );
        fprintf(stderr, "CPLCreateJoinableThread() failed.\n");
        return NULL;
    }
#endif

    pthread_attr_init( &hThreadAttr );
    pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_JOINABLE );
    if( pthread_create( &(psInfo->hThread), &hThreadAttr,
                        CPLStdCallThreadJacket, (void *) psInfo ) != 0 )
    {
#ifdef CHECK_THREAD_CAN_ALLOCATE_TLS
        pthread_cond_destroy(&(psInfo->sCond));
#endif
        CPLFree( psInfo );
        fprintf(stderr, "CPLCreateJoinableThread() failed.\n");
        return NULL;
    }

#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 NULL;
    }
#endif

    return (CPLJoinableThread*) psInfo;
}

/************************************************************************/
/*                          CPLJoinThread()                             */
/************************************************************************/

void CPLJoinThread(CPLJoinableThread* hJoinableThread)
{
    CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo*) hJoinableThread;
    if( psInfo == NULL )
        return;

    void* status;
    pthread_join( psInfo->hThread, &status);

    CPLFree(psInfo);
}

/************************************************************************/
/*                              CPLSleep()                              */
/************************************************************************/

void CPLSleep( double dfWaitInSeconds )

{
    struct timespec sRequest, sRemain;

    sRequest.tv_sec = (int) floor(dfWaitInSeconds);
    sRequest.tv_nsec = (int) ((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;

    papTLSList = (void **) pthread_getspecific( oTLSKey );
    if( papTLSList == NULL )
        return;

    pthread_setspecific( oTLSKey, NULL );

    CPLCleanupTLSList( papTLSList );
}

/************************************************************************/
/*                          CPLCreateSpinLock()                         */
/************************************************************************/

#if defined(HAVE_PTHREAD_SPINLOCK)
#define HAVE_SPINLOCK_IMPL

struct _CPLSpinLock
{
    pthread_spinlock_t spin;
};

CPLSpinLock  *CPLCreateSpinLock( void )
{
    CPLSpinLock* psSpin = (CPLSpinLock*)malloc(sizeof(CPLSpinLock));
    if( psSpin != NULL &&
        pthread_spin_init(&(psSpin->spin), PTHREAD_PROCESS_PRIVATE) == 0 )
    {
        return psSpin;
    }
    else
    {
        fprintf(stderr, "CPLCreateSpinLock() failed.\n");
        free(psSpin);
        return NULL;
    }
}

/************************************************************************/
/*                        CPLAcquireSpinLock()                          */
/************************************************************************/

int   CPLAcquireSpinLock( CPLSpinLock* psSpin )
{
    return pthread_spin_lock( &(psSpin->spin) ) == 0;
}

/************************************************************************/
/*                   CPLCreateOrAcquireSpinLockInternal()               */
/************************************************************************/

int  CPLCreateOrAcquireSpinLockInternal( CPLLock** ppsLock )
{
    pthread_mutex_lock(&global_mutex);
    if( *ppsLock == NULL )
    {
        *ppsLock = (CPLLock*) calloc(1, sizeof(CPLLock));
        if( *ppsLock != NULL )
        {
            (*ppsLock)->eType = LOCK_SPIN;
            (*ppsLock)->u.hSpinLock = CPLCreateSpinLock();
            if( (*ppsLock)->u.hSpinLock == NULL )
            {
                free(*ppsLock);
                *ppsLock = NULL;
            }
        }
    }
    pthread_mutex_unlock(&global_mutex);
    /* coverity[missing_unlock] */
    return( *ppsLock != NULL && 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** papTLSList = CPLGetTLSList(NULL);

    CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX );

    return papTLSList[nIndex];
}

/************************************************************************/
/*                            CPLGetTLSEx()                             */
/************************************************************************/

void *CPLGetTLSEx( int nIndex, int* pbMemoryErrorOccurred )

{
    void** papTLSList = CPLGetTLSList(pbMemoryErrorOccurred);
    if( papTLSList == NULL )
        return NULL;

    CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX );

    return papTLSList[nIndex];
}

/************************************************************************/
/*                             CPLSetTLS()                              */
/************************************************************************/

void CPLSetTLS( int nIndex, void *pData, int bFreeOnExit )

{
    CPLSetTLSWithFreeFunc(nIndex, pData, (bFreeOnExit) ? CPLFree : NULL);
}

/************************************************************************/
/*                      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 **papTLSList = CPLGetTLSList(NULL);

    CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX );

    papTLSList[nIndex] = pData;
    papTLSList[CTLS_MAX + nIndex] = (void*) 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 **papTLSList = CPLGetTLSList(pbMemoryErrorOccurred);

    CPLAssert( nIndex >= 0 && nIndex < CTLS_MAX );

    papTLSList[nIndex] = pData;
    papTLSList[CTLS_MAX + nIndex] = (void*) pfnFree;
}
#ifndef HAVE_SPINLOCK_IMPL

/* No spinlock specific API ? Fallback to mutex */

/************************************************************************/
/*                          CPLCreateSpinLock()                         */
/************************************************************************/

CPLSpinLock *CPLCreateSpinLock( void )
{
    CPLSpinLock* psSpin = (CPLSpinLock *)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( (CPLMutex*)psSpin, 1000 );
}

/************************************************************************/
/*                       CPLReleaseSpinLock()                           */
/************************************************************************/

void  CPLReleaseSpinLock( CPLSpinLock* psSpin )
{
    CPLReleaseMutex( (CPLMutex*)psSpin );
}

/************************************************************************/
/*                        CPLDestroySpinLock()                          */
/************************************************************************/

void  CPLDestroySpinLock( CPLSpinLock* psSpin )
{
    CPLDestroyMutex( (CPLMutex*)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 NULL;
            CPLReleaseMutex(hMutex);
            CPLLock* psLock = (CPLLock*)malloc(sizeof(CPLLock));
            if( psLock == NULL )
            {
                fprintf(stderr, "CPLCreateLock() failed.\n");
                CPLDestroyMutex(hMutex);
                return NULL;
            }
            psLock->eType = eType;
            psLock->u.hMutex = hMutex;
#ifdef DEBUG_CONTENTION
            psLock->bDebugPerf = FALSE;
#endif
            return psLock;
        }
        case LOCK_SPIN:
        {
            CPLSpinLock* hSpinLock = CPLCreateSpinLock();
            if( !hSpinLock )
                return NULL;
            CPLLock* psLock = (CPLLock*)malloc(sizeof(CPLLock));
            if( psLock == NULL )
            {
                fprintf(stderr, "CPLCreateLock() failed.\n");
                CPLDestroySpinLock(hSpinLock);
                return NULL;
            }
            psLock->eType = eType;
            psLock->u.hSpinLock = hSpinLock;
#ifdef DEBUG_CONTENTION
            psLock->bDebugPerf = FALSE;
#endif
            return psLock;
        }
        default:
            CPLAssert(0);
            return NULL;
    }
}

/************************************************************************/
/*                       CPLCreateOrAcquireLock()                       */
/************************************************************************/

int   CPLCreateOrAcquireLock( CPLLock** ppsLock, CPLLockType eType )
{
#ifdef DEBUG_CONTENTION
    GUIntBig nStartTime = 0;
    if( (*ppsLock) && (*ppsLock)->bDebugPerf )
        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(0);
            return FALSE;
    }
#ifdef DEBUG_CONTENTION
    if( ret && (*ppsLock)->bDebugPerf )
    {
        (*ppsLock)->nStartTime = nStartTime;
    }
#endif
    return ret;
}

/************************************************************************/
/*                          CPLAcquireLock()                            */
/************************************************************************/

int  CPLAcquireLock( CPLLock* psLock )
{
#ifdef DEBUG_CONTENTION
    if( psLock->bDebugPerf )
        psLock->nStartTime = CPLrdtsc();
#endif
    if( psLock->eType == LOCK_SPIN )
        return CPLAcquireSpinLock( psLock->u.hSpinLock );
    else
        return CPLAcquireMutex( psLock->u.hMutex, 1000 );
}

/************************************************************************/
/*                         CPLReleaseLock()                             */
/************************************************************************/

void  CPLReleaseLock( CPLLock* psLock )
{
#ifdef DEBUG_CONTENTION
    int bHitMaxDiff = FALSE;
    GIntBig nMaxDiff = 0;
    double dfAvgDiff = 0;
    GUIntBig nIters = 0;
    if( psLock->bDebugPerf && psLock->nStartTime )
    {
        GUIntBig nStopTime = CPLrdtscp();
        GIntBig nDiffTime = (GIntBig)(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->bDebugPerf = 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, (long) CPLGetPID(), nLine, pszFile );
#endif

    if( !CPLCreateOrAcquireLock( phLock, eType ) )
    {
        fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" );
        hLock = NULL;
    }
    else
    {
#ifdef DEBUG_MUTEX
        fprintf( stderr,
                 "CPLLockHolder: Acquired %p for pid %ld at %d/%s.\n",
                 *phLock, (long) 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 != NULL )
    {
        if( !CPLAcquireLock( hLock ) )
        {
            fprintf( stderr, "CPLLockHolder: Failed to acquire lock!\n" );
            hLock = NULL;
        }
    }
#endif /* ndef MUTEX_NONE */
}

/************************************************************************/
/*                          ~CPLLockHolder()                            */
/************************************************************************/

CPLLockHolder::~CPLLockHolder()

{
#ifndef MUTEX_NONE
    if( hLock != NULL )
    {
#ifdef DEBUG_MUTEX
        fprintf( stderr,
                 "~CPLLockHolder: Release %p for pid %ld at %d/%s.\n",
                 hLock, (long) CPLGetPID(), nLine, pszFile );
#endif
        CPLReleaseLock( hLock );
    }
#endif /* ndef MUTEX_NONE */
}

/************************************************************************/
/*                       CPLGetCurrentProcessID()                       */
/************************************************************************/

#ifdef CPL_MULTIPROC_WIN32

int CPLGetCurrentProcessID()
{
    return GetCurrentProcessId();
}

#else

#include <sys/types.h>
#include <unistd.h>

int CPLGetCurrentProcessID()
{
    return getpid();
}

#endif
