00001 /********************************************************************** 00002 * $Id: cpl_multiproc_cpp-source.html,v 1.3 2002/12/21 19:13:12 warmerda Exp $ 00003 * 00004 * Project: CPL - Common Portability Library 00005 * Purpose: CPL Multi-Threading, and process handling portability functions. 00006 * Author: Frank Warmerdam, warmerdam@pobox.com 00007 * 00008 ********************************************************************** 00009 * Copyright (c) 2002, Frank Warmerdam 00010 * 00011 * Permission is hereby granted, free of charge, to any person obtaining a 00012 * copy of this software and associated documentation files (the "Software"), 00013 * to deal in the Software without restriction, including without limitation 00014 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00015 * and/or sell copies of the Software, and to permit persons to whom the 00016 * Software is furnished to do so, subject to the following conditions: 00017 * 00018 * The above copyright notice and this permission notice shall be included 00019 * in all copies or substantial portions of the Software. 00020 * 00021 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00022 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00023 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00024 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00025 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00026 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00027 * DEALINGS IN THE SOFTWARE. 00028 ********************************************************************** 00029 * 00030 * $Log: cpl_multiproc_cpp-source.html,v $ 00030 * Revision 1.3 2002/12/21 19:13:12 warmerda 00030 * updated 00030 * 00031 * Revision 1.2 2002/07/11 19:36:34 warmerda 00032 * CPLCreateMutex() should implicitly acquire it, fix stub version 00033 * 00034 * Revision 1.1 2002/05/24 04:01:01 warmerda 00035 * New 00036 * 00037 **********************************************************************/ 00038 00039 #include "cpl_multiproc.h" 00040 #include "cpl_conv.h" 00041 #include <time.h> 00042 00043 CPL_CVSID("$Id: cpl_multiproc_cpp-source.html,v 1.3 2002/12/21 19:13:12 warmerda Exp $"); 00044 00045 #ifdef CPL_MULTIPROC_STUB 00046 /************************************************************************/ 00047 /* ==================================================================== */ 00048 /* CPL_MULTIPROC_STUB */ 00049 /* */ 00050 /* Stub implementation. Mutexes don't provide exclusion, file */ 00051 /* locking is achieved with extra "lock files", and thread */ 00052 /* creation doesn't work. The PID is always just one. */ 00053 /* ==================================================================== */ 00054 /************************************************************************/ 00055 00056 /************************************************************************/ 00057 /* CPLCreateMutex() */ 00058 /************************************************************************/ 00059 00060 void *CPLCreateMutex() 00061 00062 { 00063 unsigned char *pabyMutex = (unsigned char *) CPLMalloc( 4 ); 00064 00065 pabyMutex[0] = 1; 00066 pabyMutex[1] = 'r'; 00067 pabyMutex[2] = 'e'; 00068 pabyMutex[3] = 'd'; 00069 00070 return (void *) pabyMutex; 00071 } 00072 00073 /************************************************************************/ 00074 /* CPLAcquireMutex() */ 00075 /************************************************************************/ 00076 00077 int CPLAcquireMutex( void *hMutex, double dfWaitInSeconds ) 00078 00079 { 00080 unsigned char *pabyMutex = (unsigned char *) hMutex; 00081 00082 CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' 00083 && pabyMutex[3] == 'd' ); 00084 00085 if( pabyMutex[0] != 0 ) 00086 CPLDebug( "CPLMultiProc", 00087 "CPLAcquireMutex() called on mutex with %d as ref count!", 00088 pabyMutex[0] ); 00089 00090 pabyMutex[0] += 1; 00091 00092 return TRUE; 00093 } 00094 00095 /************************************************************************/ 00096 /* CPLReleaseMutex() */ 00097 /************************************************************************/ 00098 00099 void CPLReleaseMutex( void *hMutex ) 00100 00101 { 00102 unsigned char *pabyMutex = (unsigned char *) hMutex; 00103 00104 CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' 00105 && pabyMutex[3] == 'd' ); 00106 00107 if( pabyMutex[0] != 1 ) 00108 CPLDebug( "CPLMultiProc", 00109 "CPLReleaseMutex() called on mutex with %d as ref count!", 00110 pabyMutex[0] ); 00111 00112 pabyMutex[0] -= 1; 00113 } 00114 00115 /************************************************************************/ 00116 /* CPLDestroyMutex() */ 00117 /************************************************************************/ 00118 00119 void CPLDestroyMutex( void *hMutex ) 00120 00121 { 00122 unsigned char *pabyMutex = (unsigned char *) hMutex; 00123 00124 CPLAssert( pabyMutex[1] == 'r' && pabyMutex[2] == 'e' 00125 && pabyMutex[3] == 'd' ); 00126 00127 CPLFree( pabyMutex ); 00128 } 00129 00130 /************************************************************************/ 00131 /* CPLLockFile() */ 00132 /* */ 00133 /* Lock a file. This implementation has a terrible race */ 00134 /* condition. If we don't succeed in opening the lock file, we */ 00135 /* assume we can create one and own the target file, but other */ 00136 /* processes might easily try creating the target file at the */ 00137 /* same time, overlapping us. Death! Mayhem! The traditional */ 00138 /* solution is to use open() with _O_CREAT|_O_EXCL but this */ 00139 /* function and these arguments aren't trivially portable. */ 00140 /* Also, this still leaves a race condition on NFS drivers */ 00141 /* (apparently). */ 00142 /************************************************************************/ 00143 00144 void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) 00145 00146 { 00147 FILE *fpLock; 00148 char *pszLockFilename; 00149 00150 /* -------------------------------------------------------------------- */ 00151 /* We use a lock file with a name derived from the file we want */ 00152 /* to lock to represent the file being locked. Note that for */ 00153 /* the stub implementation the target file does not even need */ 00154 /* to exist to be locked. */ 00155 /* -------------------------------------------------------------------- */ 00156 pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30); 00157 sprintf( pszLockFilename, "%s.lock", pszPath ); 00158 00159 fpLock = fopen( pszLockFilename, "r" ); 00160 while( fpLock != NULL && dfWaitInSeconds > 0.0 ) 00161 { 00162 fclose( fpLock ); 00163 CPLSleep( MIN(dfWaitInSeconds,0.5) ); 00164 dfWaitInSeconds -= 0.5; 00165 00166 fpLock = fopen( pszLockFilename, "r" ); 00167 } 00168 00169 if( fpLock != NULL ) 00170 { 00171 fclose( fpLock ); 00172 CPLFree( pszLockFilename ); 00173 return NULL; 00174 } 00175 00176 fpLock = fopen( pszLockFilename, "w" ); 00177 00178 if( fpLock == NULL ) 00179 { 00180 CPLFree( pszLockFilename ); 00181 return NULL; 00182 } 00183 00184 fwrite( "held\n", 1, 5, fpLock ); 00185 fclose( fpLock ); 00186 00187 return pszLockFilename; 00188 } 00189 00190 /************************************************************************/ 00191 /* CPLUnlockFile() */ 00192 /************************************************************************/ 00193 00194 void CPLUnlockFile( void *hLock ) 00195 00196 { 00197 char *pszLockFilename = (char *) hLock; 00198 00199 if( hLock == NULL ) 00200 return; 00201 00202 VSIUnlink( pszLockFilename ); 00203 00204 CPLFree( pszLockFilename ); 00205 } 00206 00207 /************************************************************************/ 00208 /* CPLGetPID() */ 00209 /************************************************************************/ 00210 00211 int CPLGetPID() 00212 00213 { 00214 return 1; 00215 } 00216 00217 /************************************************************************/ 00218 /* CPLCreateThread(); */ 00219 /************************************************************************/ 00220 00221 int CPLCreateThread( void (*pfnMain)(void *), void *pArg ) 00222 00223 { 00224 return -1; 00225 } 00226 00227 /************************************************************************/ 00228 /* CPLSleep() */ 00229 /************************************************************************/ 00230 00231 void CPLSleep( double dfWaitInSeconds ) 00232 00233 { 00234 time_t ltime; 00235 time_t ttime; 00236 00237 time( <ime ); 00238 ttime = ltime + (int) (dfWaitInSeconds+0.5); 00239 00240 for( ; ltime < ttime; time(<ime) ) 00241 { 00242 /* currently we just busy wait. Perhaps we could at least block on 00243 io? */ 00244 } 00245 } 00246 00247 #endif /* def CPL_MULTIPROC_STUB */ 00248 00249 #ifdef CPL_MULTIPROC_WIN32 00250 #include <windows.h> 00251 00252 /************************************************************************/ 00253 /* ==================================================================== */ 00254 /* CPL_MULTIPROC_WIN32 */ 00255 /* */ 00256 /* WIN32 Implementation of multiprocessing functions. */ 00257 /* ==================================================================== */ 00258 /************************************************************************/ 00259 00260 00261 /************************************************************************/ 00262 /* CPLCreateMutex() */ 00263 /************************************************************************/ 00264 00265 void *CPLCreateMutex() 00266 00267 { 00268 HANDLE hMutex; 00269 00270 hMutex = CreateMutex( NULL, TRUE, NULL ); 00271 00272 return (void *) hMutex; 00273 } 00274 00275 /************************************************************************/ 00276 /* CPLAcquireMutex() */ 00277 /************************************************************************/ 00278 00279 int CPLAcquireMutex( void *hMutexIn, double dfWaitInSeconds ) 00280 00281 { 00282 HANDLE hMutex = (HANDLE) hMutexIn; 00283 DWORD hr; 00284 00285 hr = WaitForSingleObject( hMutex, (int) (dfWaitInSeconds * 1000) ); 00286 00287 return hr != WAIT_TIMEOUT; 00288 } 00289 00290 /************************************************************************/ 00291 /* CPLReleaseMutex() */ 00292 /************************************************************************/ 00293 00294 void CPLReleaseMutex( void *hMutexIn ) 00295 00296 { 00297 HANDLE hMutex = (HANDLE) hMutexIn; 00298 00299 ReleaseMutex( hMutex ); 00300 } 00301 00302 /************************************************************************/ 00303 /* CPLDestroyMutex() */ 00304 /************************************************************************/ 00305 00306 void CPLDestroyMutex( void *hMutexIn ) 00307 00308 { 00309 HANDLE hMutex = (HANDLE) hMutexIn; 00310 00311 CloseHandle( hMutex ); 00312 } 00313 00314 /************************************************************************/ 00315 /* CPLLockFile() */ 00316 /************************************************************************/ 00317 00318 void *CPLLockFile( const char *pszPath, double dfWaitInSeconds ) 00319 00320 { 00321 char *pszLockFilename; 00322 HANDLE hLockFile; 00323 00324 pszLockFilename = (char *) CPLMalloc(strlen(pszPath) + 30); 00325 sprintf( pszLockFilename, "%s.lock", pszPath ); 00326 00327 hLockFile = 00328 CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL,CREATE_NEW, 00329 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, NULL ); 00330 00331 while( GetLastError() == ERROR_ALREADY_EXISTS 00332 && dfWaitInSeconds > 0.0 ) 00333 { 00334 CloseHandle( hLockFile ); 00335 CPLSleep( MIN(dfWaitInSeconds,0.125) ); 00336 dfWaitInSeconds -= 0.125; 00337 00338 hLockFile = 00339 CreateFile( pszLockFilename, GENERIC_WRITE, 0, NULL, CREATE_NEW, 00340 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_DELETE_ON_CLOSE, 00341 NULL ); 00342 } 00343 00344 CPLFree( pszLockFilename ); 00345 00346 if( hLockFile == INVALID_HANDLE_VALUE ) 00347 return NULL; 00348 00349 if( GetLastError() == ERROR_ALREADY_EXISTS ) 00350 { 00351 CloseHandle( hLockFile ); 00352 return NULL; 00353 } 00354 00355 return (void *) hLockFile; 00356 } 00357 00358 /************************************************************************/ 00359 /* CPLUnlockFile() */ 00360 /************************************************************************/ 00361 00362 void CPLUnlockFile( void *hLock ) 00363 00364 { 00365 HANDLE hLockFile = (HANDLE) hLock; 00366 00367 CloseHandle( hLockFile ); 00368 } 00369 00370 /************************************************************************/ 00371 /* CPLGetPID() */ 00372 /************************************************************************/ 00373 00374 int CPLGetPID() 00375 00376 { 00377 return GetCurrentThreadId(); 00378 } 00379 00380 /************************************************************************/ 00381 /* CPLStdCallThreadJacket() */ 00382 /************************************************************************/ 00383 00384 typedef struct { 00385 void *pAppData; 00386 void (*pfnMain)(void *); 00387 } CPLStdCallThreadInfo; 00388 00389 static DWORD WINAPI CPLStdCallThreadJacket( void *pData ) 00390 00391 { 00392 CPLStdCallThreadInfo *psInfo = (CPLStdCallThreadInfo *) pData; 00393 00394 psInfo->pfnMain( psInfo->pAppData ); 00395 00396 CPLFree( psInfo ); 00397 00398 return 0; 00399 } 00400 00401 /************************************************************************/ 00402 /* CPLCreateThread() */ 00403 /* */ 00404 /* The WIN32 CreateThread() call requires an entry point that */ 00405 /* has __stdcall conventions, so we provide a jacket function */ 00406 /* to supply that. */ 00407 /************************************************************************/ 00408 00409 int CPLCreateThread( void (*pfnMain)(void *), void *pThreadArg ) 00410 00411 { 00412 HANDLE hThread; 00413 DWORD nThreadId; 00414 CPLStdCallThreadInfo *psInfo; 00415 00416 psInfo = (CPLStdCallThreadInfo*) CPLCalloc(sizeof(CPLStdCallThreadInfo),1); 00417 psInfo->pAppData = pThreadArg; 00418 psInfo->pfnMain = pfnMain; 00419 00420 hThread = CreateThread( NULL, 0, CPLStdCallThreadJacket, psInfo, 00421 0, &nThreadId ); 00422 00423 if( hThread == NULL ) 00424 return -1; 00425 00426 CloseHandle( hThread ); 00427 00428 return nThreadId; 00429 } 00430 00431 /************************************************************************/ 00432 /* CPLSleep() */ 00433 /************************************************************************/ 00434 00435 void CPLSleep( double dfWaitInSeconds ) 00436 00437 { 00438 Sleep( (DWORD) (dfWaitInSeconds * 1000.0) ); 00439 } 00440 00441 #endif /* def CPL_MULTIPROC_WIN32 */ 00442