/src/gdal/port/cpl_multiproc.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************** |
2 | | * |
3 | | * Project: CPL - Common Portability Library |
4 | | * Purpose: CPL Multi-Threading, and process handling portability functions. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ********************************************************************** |
8 | | * Copyright (c) 2002, Frank Warmerdam |
9 | | * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #ifndef _GNU_SOURCE |
15 | | #define _GNU_SOURCE |
16 | | #endif |
17 | | |
18 | | // Include cpl_config.h BEFORE cpl_multiproc.h, as the later may undefine |
19 | | // CPL_MULTIPROC_PTHREAD for mingw case. |
20 | | |
21 | | #include "cpl_config.h" |
22 | | #include "cpl_multiproc.h" |
23 | | |
24 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
25 | | #include <cassert> |
26 | | #endif |
27 | | #include <cerrno> |
28 | | #include <cmath> |
29 | | #include <cstddef> |
30 | | #include <cstdio> |
31 | | #include <cstdlib> |
32 | | #include <cstring> |
33 | | #include <ctime> |
34 | | #include <algorithm> |
35 | | |
36 | | #include "cpl_atomic_ops.h" |
37 | | #include "cpl_conv.h" |
38 | | #include "cpl_error.h" |
39 | | #include "cpl_string.h" |
40 | | #include "cpl_vsi.h" |
41 | | |
42 | | #if defined(CPL_MULTIPROC_STUB) && !defined(DEBUG) |
43 | | #define MUTEX_NONE |
44 | | #endif |
45 | | |
46 | | // #define DEBUG_MUTEX |
47 | | |
48 | | #if defined(DEBUG) && \ |
49 | | (defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))) |
50 | | #ifndef DEBUG_CONTENTION |
51 | | #define DEBUG_CONTENTION |
52 | | #endif |
53 | | #endif |
54 | | |
55 | | typedef struct _CPLSpinLock CPLSpinLock; |
56 | | |
57 | | struct _CPLLock |
58 | | { |
59 | | CPLLockType eType; |
60 | | |
61 | | union |
62 | | { |
63 | | CPLMutex *hMutex; |
64 | | CPLSpinLock *hSpinLock; |
65 | | } u; |
66 | | |
67 | | #ifdef DEBUG_CONTENTION |
68 | | bool bDebugPerfAsked; |
69 | | bool bDebugPerf; |
70 | | volatile int nCurrentHolders; |
71 | | GUIntBig nStartTime; |
72 | | GIntBig nMaxDiff; |
73 | | double dfAvgDiff; |
74 | | GUIntBig nIters; |
75 | | #endif |
76 | | }; |
77 | | |
78 | | #ifdef DEBUG_CONTENTION |
79 | | |
80 | | #if defined(__x86_64) |
81 | | #define GCC_CPUID(level, a, b, c, d) \ |
82 | 0 | __asm__ volatile("xchgq %%rbx, %q1\n" \ |
83 | 0 | "cpuid\n" \ |
84 | 0 | "xchgq %%rbx, %q1" \ |
85 | 0 | : "=a"(a), "=r"(b), "=c"(c), "=d"(d) \ |
86 | 0 | : "0"(level)) |
87 | | #else |
88 | | #define GCC_CPUID(level, a, b, c, d) \ |
89 | | __asm__ volatile("xchgl %%ebx, %1\n" \ |
90 | | "cpuid\n" \ |
91 | | "xchgl %%ebx, %1" \ |
92 | | : "=a"(a), "=r"(b), "=c"(c), "=d"(d) \ |
93 | | : "0"(level)) |
94 | | #endif |
95 | | |
96 | | static GUIntBig CPLrdtsc() |
97 | 0 | { |
98 | 0 | unsigned int a; |
99 | 0 | unsigned int d; |
100 | 0 | unsigned int unused1; |
101 | 0 | unsigned int unused2; |
102 | 0 | unsigned int unused3; |
103 | 0 | unsigned int unused4; |
104 | 0 | GCC_CPUID(0, unused1, unused2, unused3, unused4); |
105 | 0 | __asm__ volatile("rdtsc" : "=a"(a), "=d"(d)); |
106 | 0 | return static_cast<GUIntBig>(a) | (static_cast<GUIntBig>(d) << 32); |
107 | 0 | } |
108 | | |
109 | | static GUIntBig CPLrdtscp() |
110 | 0 | { |
111 | 0 | unsigned int a; |
112 | 0 | unsigned int d; |
113 | 0 | unsigned int unused1; |
114 | 0 | unsigned int unused2; |
115 | 0 | unsigned int unused3; |
116 | 0 | unsigned int unused4; |
117 | 0 | __asm__ volatile("rdtscp" : "=a"(a), "=d"(d)); |
118 | 0 | GCC_CPUID(0, unused1, unused2, unused3, unused4); |
119 | 0 | return static_cast<GUIntBig>(a) | (static_cast<GUIntBig>(d) << 32); |
120 | 0 | } |
121 | | #endif |
122 | | |
123 | | static CPLSpinLock *CPLCreateSpinLock(); // Returned NON acquired. |
124 | | static int CPLCreateOrAcquireSpinLockInternal(CPLLock **); |
125 | | static int CPLAcquireSpinLock(CPLSpinLock *); |
126 | | static void CPLReleaseSpinLock(CPLSpinLock *); |
127 | | static void CPLDestroySpinLock(CPLSpinLock *); |
128 | | |
129 | | #ifndef CPL_MULTIPROC_PTHREAD |
130 | | #ifndef MUTEX_NONE |
131 | | static CPLMutex *CPLCreateOrAcquireMasterMutex(double); |
132 | | static CPLMutex *&CPLCreateOrAcquireMasterMutexInternal(double); |
133 | | static CPLMutex *CPLCreateUnacquiredMutex(); |
134 | | #endif |
135 | | #endif |
136 | | |
137 | | // We don't want it to be publicly used since it solves rather tricky issues |
138 | | // that are better to remain hidden. |
139 | | void CPLFinalizeTLS(); |
140 | | |
141 | | /************************************************************************/ |
142 | | /* CPLMutexHolder() */ |
143 | | /************************************************************************/ |
144 | | |
145 | | #ifdef MUTEX_NONE |
146 | | CPLMutexHolder::CPLMutexHolder(CPLMutex ** /* phMutex */, |
147 | | double /* dfWaitInSeconds */, |
148 | | const char * /* pszFileIn */, int /* nLineIn */, |
149 | | int /* nOptions */) |
150 | | { |
151 | | } |
152 | | |
153 | | #else |
154 | | CPLMutexHolder::CPLMutexHolder(CPLMutex **phMutex, double dfWaitInSeconds, |
155 | | const char *pszFileIn, int nLineIn, int nOptions) |
156 | 267k | : hMutex(nullptr), pszFile(pszFileIn), nLine(nLineIn) |
157 | 267k | { |
158 | 267k | if (phMutex == nullptr) |
159 | 0 | { |
160 | 0 | fprintf(stderr, "CPLMutexHolder: phMutex )) NULL !\n"); |
161 | 0 | hMutex = nullptr; |
162 | 0 | return; |
163 | 0 | } |
164 | | |
165 | | #ifdef DEBUG_MUTEX |
166 | | // There is no way to use CPLDebug() here because it works with |
167 | | // mutexes itself so we will fall in infinite recursion. |
168 | | // fprintf() will do the job right. |
169 | | fprintf(stderr, "CPLMutexHolder: Request %p for pid %ld at %d/%s.\n", |
170 | | *phMutex, static_cast<long>(CPLGetPID()), nLine, pszFile); |
171 | | #else |
172 | | // TODO(schwehr): Find a better way to do handle this. |
173 | 267k | (void)pszFile; |
174 | 267k | (void)nLine; |
175 | 267k | #endif |
176 | | |
177 | 267k | if (!CPLCreateOrAcquireMutexEx(phMutex, dfWaitInSeconds, nOptions)) |
178 | 0 | { |
179 | 0 | fprintf(stderr, "CPLMutexHolder: Failed to acquire mutex!\n"); |
180 | 0 | hMutex = nullptr; |
181 | 0 | } |
182 | 267k | else |
183 | 267k | { |
184 | | #ifdef DEBUG_MUTEX |
185 | | fprintf(stderr, "CPLMutexHolder: Acquired %p for pid %ld at %d/%s.\n", |
186 | | *phMutex, static_cast<long>(CPLGetPID()), nLine, pszFile); |
187 | | #endif |
188 | | |
189 | 267k | hMutex = *phMutex; |
190 | 267k | } |
191 | 267k | } |
192 | | #endif // ndef MUTEX_NONE |
193 | | |
194 | | /************************************************************************/ |
195 | | /* CPLMutexHolder() */ |
196 | | /************************************************************************/ |
197 | | |
198 | | #ifdef MUTEX_NONE |
199 | | CPLMutexHolder::CPLMutexHolder(CPLMutex * /* hMutexIn */, |
200 | | double /* dfWaitInSeconds */, |
201 | | const char * /* pszFileIn */, int /* nLineIn */) |
202 | | { |
203 | | } |
204 | | #else |
205 | | |
206 | | static CPLMutex *GetMutexHolderMutexMember(CPLMutex *hMutexIn, |
207 | | double dfWaitInSeconds) |
208 | 0 | { |
209 | 0 | if (hMutexIn && !CPLAcquireMutex(hMutexIn, dfWaitInSeconds)) |
210 | 0 | { |
211 | 0 | fprintf(stderr, "CPLMutexHolder: Failed to acquire mutex!\n"); |
212 | 0 | return nullptr; |
213 | 0 | } |
214 | 0 | return hMutexIn; |
215 | 0 | } |
216 | | |
217 | | CPLMutexHolder::CPLMutexHolder(CPLMutex *hMutexIn, double dfWaitInSeconds, |
218 | | const char *pszFileIn, int nLineIn) |
219 | 0 | : hMutex(GetMutexHolderMutexMember(hMutexIn, dfWaitInSeconds)), |
220 | 0 | pszFile(pszFileIn), nLine(nLineIn) |
221 | 0 | { |
222 | 0 | } |
223 | | #endif // ndef MUTEX_NONE |
224 | | |
225 | | /************************************************************************/ |
226 | | /* ~CPLMutexHolder() */ |
227 | | /************************************************************************/ |
228 | | |
229 | | #ifdef MUTEX_NONE |
230 | | CPLMutexHolder::~CPLMutexHolder() |
231 | | { |
232 | | } |
233 | | #else |
234 | | CPLMutexHolder::~CPLMutexHolder() |
235 | 267k | { |
236 | 267k | if (hMutex != nullptr) |
237 | 267k | { |
238 | | #ifdef DEBUG_MUTEX |
239 | | fprintf(stderr, "~CPLMutexHolder: Release %p for pid %ld at %d/%s.\n", |
240 | | hMutex, static_cast<long>(CPLGetPID()), nLine, pszFile); |
241 | | #endif |
242 | 267k | CPLReleaseMutex(hMutex); |
243 | 267k | } |
244 | 267k | } |
245 | | #endif // ndef MUTEX_NONE |
246 | | |
247 | | int CPLCreateOrAcquireMutex(CPLMutex **phMutex, double dfWaitInSeconds) |
248 | 0 | { |
249 | 0 | return CPLCreateOrAcquireMutexEx(phMutex, dfWaitInSeconds, |
250 | 0 | CPL_MUTEX_RECURSIVE); |
251 | 0 | } |
252 | | |
253 | | /************************************************************************/ |
254 | | /* CPLCreateOrAcquireMutex() */ |
255 | | /************************************************************************/ |
256 | | |
257 | | #ifndef CPL_MULTIPROC_PTHREAD |
258 | | |
259 | | #ifndef MUTEX_NONE |
260 | | CPLMutex *CPLCreateUnacquiredMutex() |
261 | | { |
262 | | CPLMutex *hMutex = CPLCreateMutex(); |
263 | | if (hMutex) |
264 | | { |
265 | | CPLReleaseMutex(hMutex); |
266 | | } |
267 | | return hMutex; |
268 | | } |
269 | | |
270 | | CPLMutex *& |
271 | | CPLCreateOrAcquireMasterMutexInternal(double dfWaitInSeconds = 1000.0) |
272 | | { |
273 | | // The dynamic initialization of the block scope hCOAMutex |
274 | | // with static storage duration is thread-safe in C++11 |
275 | | static CPLMutex *hCOAMutex = CPLCreateUnacquiredMutex(); |
276 | | |
277 | | // WARNING: although adding an CPLAssert(hCOAMutex); might seem logical |
278 | | // here, do not enable it (see comment below). It calls CPLError that |
279 | | // uses the hCOAMutex itself leading to recursive mutex acquisition |
280 | | // and likely a stack overflow. |
281 | | |
282 | | if (!hCOAMutex) |
283 | | { |
284 | | // Fall back to this, ironically, NOT thread-safe re-initialisation of |
285 | | // hCOAMutex in case of a memory error or call to CPLCleanupMasterMutex |
286 | | // sequenced in an unusual, unexpected or erroneous way. |
287 | | // For example, an unusual sequence could be: |
288 | | // GDALDriverManager has been instantiated, |
289 | | // then OGRCleanupAll is called which calls CPLCleanupMasterMutex, |
290 | | // then CPLFreeConfig is called which acquires the hCOAMutex |
291 | | // that has already been released and destroyed. |
292 | | |
293 | | hCOAMutex = CPLCreateUnacquiredMutex(); |
294 | | } |
295 | | |
296 | | if (hCOAMutex) |
297 | | { |
298 | | CPLAcquireMutex(hCOAMutex, dfWaitInSeconds); |
299 | | } |
300 | | |
301 | | return hCOAMutex; |
302 | | } |
303 | | |
304 | | CPLMutex *CPLCreateOrAcquireMasterMutex(double dfWaitInSeconds = 1000.0) |
305 | | { |
306 | | CPLMutex *hCOAMutex = |
307 | | CPLCreateOrAcquireMasterMutexInternal(dfWaitInSeconds); |
308 | | return hCOAMutex; |
309 | | } |
310 | | #endif |
311 | | |
312 | | #ifdef MUTEX_NONE |
313 | | |
314 | | int CPLCreateOrAcquireMutexEx(CPLMutex **phMutex, double dfWaitInSeconds, |
315 | | int nOptions) |
316 | | { |
317 | | return false; |
318 | | } |
319 | | #else |
320 | | int CPLCreateOrAcquireMutexEx(CPLMutex **phMutex, double dfWaitInSeconds, |
321 | | int nOptions) |
322 | | { |
323 | | bool bSuccess = false; |
324 | | |
325 | | CPLMutex *hCOAMutex = CPLCreateOrAcquireMasterMutex(dfWaitInSeconds); |
326 | | if (hCOAMutex == nullptr) |
327 | | { |
328 | | *phMutex = nullptr; |
329 | | return FALSE; |
330 | | } |
331 | | |
332 | | if (*phMutex == nullptr) |
333 | | { |
334 | | *phMutex = CPLCreateMutexEx(nOptions); |
335 | | bSuccess = *phMutex != nullptr; |
336 | | CPLReleaseMutex(hCOAMutex); |
337 | | } |
338 | | else |
339 | | { |
340 | | CPLReleaseMutex(hCOAMutex); |
341 | | |
342 | | bSuccess = CPL_TO_BOOL(CPLAcquireMutex(*phMutex, dfWaitInSeconds)); |
343 | | } |
344 | | |
345 | | return bSuccess; |
346 | | } |
347 | | #endif // ndef MUTEX_NONE |
348 | | |
349 | | /************************************************************************/ |
350 | | /* CPLCreateOrAcquireMutexInternal() */ |
351 | | /************************************************************************/ |
352 | | |
353 | | #ifdef MUTEX_NONE |
354 | | static bool CPLCreateOrAcquireMutexInternal(CPLLock **phLock, |
355 | | double dfWaitInSeconds, |
356 | | CPLLockType eType) |
357 | | { |
358 | | return false; |
359 | | } |
360 | | #else |
361 | | static bool CPLCreateOrAcquireMutexInternal(CPLLock **phLock, |
362 | | double dfWaitInSeconds, |
363 | | CPLLockType eType) |
364 | | |
365 | | { |
366 | | bool bSuccess = false; |
367 | | |
368 | | CPLMutex *hCOAMutex = CPLCreateOrAcquireMasterMutex(dfWaitInSeconds); |
369 | | if (hCOAMutex == nullptr) |
370 | | { |
371 | | *phLock = nullptr; |
372 | | return FALSE; |
373 | | } |
374 | | |
375 | | if (*phLock == nullptr) |
376 | | { |
377 | | *phLock = static_cast<CPLLock *>(calloc(1, sizeof(CPLLock))); |
378 | | if (*phLock) |
379 | | { |
380 | | (*phLock)->eType = eType; |
381 | | (*phLock)->u.hMutex = CPLCreateMutexEx( |
382 | | (eType == LOCK_RECURSIVE_MUTEX) ? CPL_MUTEX_RECURSIVE |
383 | | : CPL_MUTEX_ADAPTIVE); |
384 | | if ((*phLock)->u.hMutex == nullptr) |
385 | | { |
386 | | free(*phLock); |
387 | | *phLock = nullptr; |
388 | | } |
389 | | } |
390 | | bSuccess = *phLock != nullptr; |
391 | | CPLReleaseMutex(hCOAMutex); |
392 | | } |
393 | | else |
394 | | { |
395 | | CPLReleaseMutex(hCOAMutex); |
396 | | |
397 | | bSuccess = |
398 | | CPL_TO_BOOL(CPLAcquireMutex((*phLock)->u.hMutex, dfWaitInSeconds)); |
399 | | } |
400 | | |
401 | | return bSuccess; |
402 | | } |
403 | | #endif // ndef MUTEX_NONE |
404 | | |
405 | | #endif // CPL_MULTIPROC_PTHREAD |
406 | | |
407 | | /************************************************************************/ |
408 | | /* CPLCleanupMasterMutex() */ |
409 | | /************************************************************************/ |
410 | | |
411 | | void CPLCleanupMasterMutex() |
412 | 0 | { |
413 | | #ifndef CPL_MULTIPROC_PTHREAD |
414 | | #ifndef MUTEX_NONE |
415 | | CPLMutex *&hCOAMutex = CPLCreateOrAcquireMasterMutexInternal(); |
416 | | if (hCOAMutex != nullptr) |
417 | | { |
418 | | CPLReleaseMutex(hCOAMutex); |
419 | | CPLDestroyMutex(hCOAMutex); |
420 | | hCOAMutex = nullptr; |
421 | | } |
422 | | #endif |
423 | | #endif |
424 | 0 | } |
425 | | |
426 | | /************************************************************************/ |
427 | | /* CPLCleanupTLSList() */ |
428 | | /* */ |
429 | | /* Free resources associated with a TLS vector (implementation */ |
430 | | /* independent). */ |
431 | | /************************************************************************/ |
432 | | |
433 | | static void CPLCleanupTLSList(void **papTLSList) |
434 | | |
435 | 0 | { |
436 | | #ifdef DEBUG_VERBOSE |
437 | | printf("CPLCleanupTLSList(%p)\n", papTLSList); /*ok*/ |
438 | | #endif |
439 | |
|
440 | 0 | if (papTLSList == nullptr) |
441 | 0 | return; |
442 | | |
443 | 0 | for (int i = 0; i < CTLS_MAX; i++) |
444 | 0 | { |
445 | 0 | if (papTLSList[i] != nullptr && papTLSList[i + CTLS_MAX] != nullptr) |
446 | 0 | { |
447 | 0 | CPLTLSFreeFunc pfnFree = |
448 | 0 | reinterpret_cast<CPLTLSFreeFunc>(papTLSList[i + CTLS_MAX]); |
449 | 0 | pfnFree(papTLSList[i]); |
450 | 0 | papTLSList[i] = nullptr; |
451 | 0 | } |
452 | 0 | } |
453 | |
|
454 | 0 | CPLFree(papTLSList); |
455 | 0 | } |
456 | | |
457 | | #if defined(CPL_MULTIPROC_STUB) |
458 | | /************************************************************************/ |
459 | | /* ==================================================================== */ |
460 | | /* CPL_MULTIPROC_STUB */ |
461 | | /* */ |
462 | | /* Stub implementation. Mutexes don't provide exclusion, file */ |
463 | | /* locking is achieved with extra "lock files", and thread */ |
464 | | /* creation doesn't work. The PID is always just one. */ |
465 | | /* ==================================================================== */ |
466 | | /************************************************************************/ |
467 | | |
468 | | /************************************************************************/ |
469 | | /* CPLGetNumCPUs() */ |
470 | | /************************************************************************/ |
471 | | |
472 | | int CPLGetNumCPUs() |
473 | | { |
474 | | return 1; |
475 | | } |
476 | | |
477 | | /************************************************************************/ |
478 | | /* CPLGetThreadingModel() */ |
479 | | /************************************************************************/ |
480 | | |
481 | | const char *CPLGetThreadingModel() |
482 | | |
483 | | { |
484 | | return "stub"; |
485 | | } |
486 | | |
487 | | /************************************************************************/ |
488 | | /* CPLCreateMutex() */ |
489 | | /************************************************************************/ |
490 | | |
491 | | #ifdef MUTEX_NONE |
492 | | CPLMutex *CPLCreateMutex() |
493 | | { |
494 | | return (CPLMutex *)0xdeadbeef; |
495 | | } |
496 | | #else |
497 | | CPLMutex *CPLCreateMutex() |
498 | | { |
499 | | unsigned char *pabyMutex = static_cast<unsigned char *>(malloc(4)); |
500 | | if (pabyMutex == nullptr) |
501 | | return nullptr; |
502 | | |
503 | | pabyMutex[0] = 1; |
504 | | pabyMutex[1] = 'r'; |
505 | | pabyMutex[2] = 'e'; |
506 | | pabyMutex[3] = 'd'; |
507 | | |
508 | | return (CPLMutex *)pabyMutex; |
509 | | } |
510 | | #endif |
511 | | |
512 | | CPLMutex *CPLCreateMutexEx(int /*nOptions*/) |
513 | | |
514 | | { |
515 | | return CPLCreateMutex(); |
516 | | } |
517 | | |
518 | | /************************************************************************/ |
519 | | /* CPLAcquireMutex() */ |
520 | | /************************************************************************/ |
521 | | |
522 | | #ifdef MUTEX_NONE |
523 | | int CPLAcquireMutex(CPLMutex *hMutex, double /* dfWaitInSeconds */) |
524 | | { |
525 | | return TRUE; |
526 | | } |
527 | | #else |
528 | | int CPLAcquireMutex(CPLMutex *hMutex, double /*dfWaitInSeconds*/) |
529 | | { |
530 | | unsigned char *pabyMutex = reinterpret_cast<unsigned char *>(hMutex); |
531 | | |
532 | | CPLAssert(pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && |
533 | | pabyMutex[3] == 'd'); |
534 | | |
535 | | pabyMutex[0] += 1; |
536 | | |
537 | | return TRUE; |
538 | | } |
539 | | #endif // ! MUTEX_NONE |
540 | | |
541 | | /************************************************************************/ |
542 | | /* CPLReleaseMutex() */ |
543 | | /************************************************************************/ |
544 | | |
545 | | #ifdef MUTEX_NONE |
546 | | void CPLReleaseMutex(CPLMutex * /* hMutex */) |
547 | | { |
548 | | } |
549 | | #else |
550 | | void CPLReleaseMutex(CPLMutex *hMutex) |
551 | | { |
552 | | unsigned char *pabyMutex = reinterpret_cast<unsigned char *>(hMutex); |
553 | | |
554 | | CPLAssert(pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && |
555 | | pabyMutex[3] == 'd'); |
556 | | |
557 | | if (pabyMutex[0] < 1) |
558 | | CPLDebug("CPLMultiProc", |
559 | | "CPLReleaseMutex() called on mutex with %d as ref count!", |
560 | | pabyMutex[0]); |
561 | | |
562 | | pabyMutex[0] -= 1; |
563 | | } |
564 | | #endif |
565 | | |
566 | | /************************************************************************/ |
567 | | /* CPLDestroyMutex() */ |
568 | | /************************************************************************/ |
569 | | |
570 | | #ifdef MUTEX_NONE |
571 | | void CPLDestroyMutex(CPLMutex * /* hMutex */) |
572 | | { |
573 | | } |
574 | | #else |
575 | | void CPLDestroyMutex(CPLMutex *hMutex) |
576 | | { |
577 | | unsigned char *pabyMutex = reinterpret_cast<unsigned char *>(hMutex); |
578 | | |
579 | | CPLAssert(pabyMutex[1] == 'r' && pabyMutex[2] == 'e' && |
580 | | pabyMutex[3] == 'd'); |
581 | | |
582 | | free(pabyMutex); |
583 | | } |
584 | | #endif |
585 | | |
586 | | /************************************************************************/ |
587 | | /* CPLCreateCond() */ |
588 | | /************************************************************************/ |
589 | | |
590 | | CPLCond *CPLCreateCond() |
591 | | { |
592 | | return nullptr; |
593 | | } |
594 | | |
595 | | /************************************************************************/ |
596 | | /* CPLCondWait() */ |
597 | | /************************************************************************/ |
598 | | |
599 | | void CPLCondWait(CPLCond * /* hCond */, CPLMutex * /* hMutex */) |
600 | | { |
601 | | } |
602 | | |
603 | | /************************************************************************/ |
604 | | /* CPLCondTimedWait() */ |
605 | | /************************************************************************/ |
606 | | |
607 | | CPLCondTimedWaitReason CPLCondTimedWait(CPLCond * /* hCond */, |
608 | | CPLMutex * /* hMutex */, double) |
609 | | { |
610 | | return COND_TIMED_WAIT_OTHER; |
611 | | } |
612 | | |
613 | | /************************************************************************/ |
614 | | /* CPLCondSignal() */ |
615 | | /************************************************************************/ |
616 | | |
617 | | void CPLCondSignal(CPLCond * /* hCond */) |
618 | | { |
619 | | } |
620 | | |
621 | | /************************************************************************/ |
622 | | /* CPLCondBroadcast() */ |
623 | | /************************************************************************/ |
624 | | |
625 | | void CPLCondBroadcast(CPLCond * /* hCond */) |
626 | | { |
627 | | } |
628 | | |
629 | | /************************************************************************/ |
630 | | /* CPLDestroyCond() */ |
631 | | /************************************************************************/ |
632 | | |
633 | | void CPLDestroyCond(CPLCond * /* hCond */) |
634 | | { |
635 | | } |
636 | | |
637 | | /************************************************************************/ |
638 | | /* CPLLockFile() */ |
639 | | /************************************************************************/ |
640 | | |
641 | | void *CPLLockFile(const char *pszPath, double dfWaitInSeconds) |
642 | | |
643 | | { |
644 | | CPLLockFileHandle hHandle = nullptr; |
645 | | CPLStringList aosOptions; |
646 | | aosOptions.SetNameValue("WAIT_TIME", CPLSPrintf("%f", dfWaitInSeconds)); |
647 | | CPLLockFileEx(pszPath, &hHandle, aosOptions); |
648 | | return hHandle; |
649 | | } |
650 | | |
651 | | /************************************************************************/ |
652 | | /* CPLUnlockFile() */ |
653 | | /************************************************************************/ |
654 | | |
655 | | void CPLUnlockFile(void *hLock) |
656 | | |
657 | | { |
658 | | CPLUnlockFileEx(static_cast<CPLLockFileHandle>(hLock)); |
659 | | } |
660 | | |
661 | | /************************************************************************/ |
662 | | /* CPLGetPID() */ |
663 | | /************************************************************************/ |
664 | | |
665 | | GIntBig CPLGetPID() |
666 | | |
667 | | { |
668 | | return 1; |
669 | | } |
670 | | |
671 | | /************************************************************************/ |
672 | | /* CPLCreateThread(); */ |
673 | | /************************************************************************/ |
674 | | |
675 | | int CPLCreateThread(CPLThreadFunc /* pfnMain */, void * /* pArg */) |
676 | | { |
677 | | CPLDebug("CPLCreateThread", "Fails to dummy implementation"); |
678 | | |
679 | | return -1; |
680 | | } |
681 | | |
682 | | /************************************************************************/ |
683 | | /* CPLCreateJoinableThread() */ |
684 | | /************************************************************************/ |
685 | | |
686 | | CPLJoinableThread *CPLCreateJoinableThread(CPLThreadFunc /* pfnMain */, |
687 | | void * /* pThreadArg */) |
688 | | { |
689 | | CPLDebug("CPLCreateJoinableThread", "Fails to dummy implementation"); |
690 | | |
691 | | return nullptr; |
692 | | } |
693 | | |
694 | | /************************************************************************/ |
695 | | /* CPLJoinThread() */ |
696 | | /************************************************************************/ |
697 | | |
698 | | void CPLJoinThread(CPLJoinableThread * /* hJoinableThread */) |
699 | | { |
700 | | } |
701 | | |
702 | | /************************************************************************/ |
703 | | /* CPLSleep() */ |
704 | | /************************************************************************/ |
705 | | |
706 | | void CPLSleep(double dfWaitInSeconds) |
707 | | { |
708 | | time_t ltime; |
709 | | |
710 | | time(<ime); |
711 | | const time_t ttime = ltime + static_cast<int>(dfWaitInSeconds + 0.5); |
712 | | |
713 | | for (; ltime < ttime; time(<ime)) |
714 | | { |
715 | | // Currently we just busy wait. Perhaps we could at least block on io? |
716 | | } |
717 | | } |
718 | | |
719 | | /************************************************************************/ |
720 | | /* CPLGetTLSList() */ |
721 | | /************************************************************************/ |
722 | | |
723 | | static void **papTLSList = nullptr; |
724 | | |
725 | | static void **CPLGetTLSList(int *pbMemoryErrorOccurred) |
726 | | |
727 | | { |
728 | | if (pbMemoryErrorOccurred) |
729 | | *pbMemoryErrorOccurred = FALSE; |
730 | | if (papTLSList == nullptr) |
731 | | { |
732 | | papTLSList = |
733 | | static_cast<void **>(VSICalloc(sizeof(void *), CTLS_MAX * 2)); |
734 | | if (papTLSList == nullptr) |
735 | | { |
736 | | if (pbMemoryErrorOccurred) |
737 | | { |
738 | | *pbMemoryErrorOccurred = TRUE; |
739 | | fprintf(stderr, |
740 | | "CPLGetTLSList() failed to allocate TLS list!\n"); |
741 | | return nullptr; |
742 | | } |
743 | | CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); |
744 | | } |
745 | | } |
746 | | |
747 | | return papTLSList; |
748 | | } |
749 | | |
750 | | /************************************************************************/ |
751 | | /* CPLFinalizeTLS() */ |
752 | | /************************************************************************/ |
753 | | |
754 | | void CPLFinalizeTLS() |
755 | | { |
756 | | CPLCleanupTLS(); |
757 | | } |
758 | | |
759 | | /************************************************************************/ |
760 | | /* CPLCleanupTLS() */ |
761 | | /************************************************************************/ |
762 | | |
763 | | void CPLCleanupTLS() |
764 | | |
765 | | { |
766 | | CPLCleanupTLSList(papTLSList); |
767 | | papTLSList = nullptr; |
768 | | } |
769 | | |
770 | | // endif CPL_MULTIPROC_STUB |
771 | | |
772 | | #elif defined(CPL_MULTIPROC_WIN32) |
773 | | |
774 | | /************************************************************************/ |
775 | | /* ==================================================================== */ |
776 | | /* CPL_MULTIPROC_WIN32 */ |
777 | | /* */ |
778 | | /* WIN32 Implementation of multiprocessing functions. */ |
779 | | /* ==================================================================== */ |
780 | | /************************************************************************/ |
781 | | |
782 | | /* InitializeCriticalSectionAndSpinCount requires _WIN32_WINNT >= 0x403 */ |
783 | | #undef _WIN32_WINNT |
784 | | #define _WIN32_WINNT 0x0500 |
785 | | |
786 | | #include <windows.h> |
787 | | |
788 | | /************************************************************************/ |
789 | | /* CPLGetNumCPUs() */ |
790 | | /************************************************************************/ |
791 | | |
792 | | int CPLGetNumCPUs() |
793 | | { |
794 | | SYSTEM_INFO info; |
795 | | GetSystemInfo(&info); |
796 | | const DWORD dwNum = info.dwNumberOfProcessors; |
797 | | if (dwNum < 1) |
798 | | return 1; |
799 | | return static_cast<int>(dwNum); |
800 | | } |
801 | | |
802 | | /************************************************************************/ |
803 | | /* CPLGetThreadingModel() */ |
804 | | /************************************************************************/ |
805 | | |
806 | | const char *CPLGetThreadingModel() |
807 | | |
808 | | { |
809 | | return "win32"; |
810 | | } |
811 | | |
812 | | /************************************************************************/ |
813 | | /* CPLCreateMutex() */ |
814 | | /************************************************************************/ |
815 | | |
816 | | CPLMutex *CPLCreateMutex() |
817 | | |
818 | | { |
819 | | #ifdef USE_WIN32_MUTEX |
820 | | HANDLE hMutex = CreateMutex(nullptr, TRUE, nullptr); |
821 | | |
822 | | return (CPLMutex *)hMutex; |
823 | | #else |
824 | | |
825 | | // Do not use CPLMalloc() since its debugging infrastructure |
826 | | // can call the CPL*Mutex functions. |
827 | | CRITICAL_SECTION *pcs = |
828 | | static_cast<CRITICAL_SECTION *>(malloc(sizeof(*pcs))); |
829 | | if (pcs) |
830 | | { |
831 | | InitializeCriticalSectionAndSpinCount(pcs, 4000); |
832 | | EnterCriticalSection(pcs); |
833 | | } |
834 | | |
835 | | return reinterpret_cast<CPLMutex *>(pcs); |
836 | | #endif |
837 | | } |
838 | | |
839 | | CPLMutex *CPLCreateMutexEx(int /* nOptions */) |
840 | | |
841 | | { |
842 | | return CPLCreateMutex(); |
843 | | } |
844 | | |
845 | | /************************************************************************/ |
846 | | /* CPLAcquireMutex() */ |
847 | | /************************************************************************/ |
848 | | |
849 | | int CPLAcquireMutex(CPLMutex *hMutexIn, double dfWaitInSeconds) |
850 | | |
851 | | { |
852 | | #ifdef USE_WIN32_MUTEX |
853 | | HANDLE hMutex = (HANDLE)hMutexIn; |
854 | | const DWORD hr = |
855 | | WaitForSingleObject(hMutex, static_cast<int>(dfWaitInSeconds * 1000)); |
856 | | |
857 | | return hr != WAIT_TIMEOUT; |
858 | | #else |
859 | | CRITICAL_SECTION *pcs = reinterpret_cast<CRITICAL_SECTION *>(hMutexIn); |
860 | | BOOL ret; |
861 | | |
862 | | if (dfWaitInSeconds >= 1000.0) |
863 | | { |
864 | | // We assume this is the synonymous for infinite, so it is more |
865 | | // efficient to use EnterCriticalSection() directly |
866 | | EnterCriticalSection(pcs); |
867 | | ret = TRUE; |
868 | | } |
869 | | else |
870 | | { |
871 | | while ((ret = TryEnterCriticalSection(pcs)) == 0 && |
872 | | dfWaitInSeconds > 0.0) |
873 | | { |
874 | | CPLSleep(std::min(dfWaitInSeconds, 0.01)); |
875 | | dfWaitInSeconds -= 0.01; |
876 | | } |
877 | | } |
878 | | |
879 | | return ret; |
880 | | #endif |
881 | | } |
882 | | |
883 | | /************************************************************************/ |
884 | | /* CPLReleaseMutex() */ |
885 | | /************************************************************************/ |
886 | | |
887 | | void CPLReleaseMutex(CPLMutex *hMutexIn) |
888 | | |
889 | | { |
890 | | #ifdef USE_WIN32_MUTEX |
891 | | HANDLE hMutex = (HANDLE)hMutexIn; |
892 | | |
893 | | ReleaseMutex(hMutex); |
894 | | #else |
895 | | CRITICAL_SECTION *pcs = reinterpret_cast<CRITICAL_SECTION *>(hMutexIn); |
896 | | |
897 | | LeaveCriticalSection(pcs); |
898 | | #endif |
899 | | } |
900 | | |
901 | | /************************************************************************/ |
902 | | /* CPLDestroyMutex() */ |
903 | | /************************************************************************/ |
904 | | |
905 | | void CPLDestroyMutex(CPLMutex *hMutexIn) |
906 | | |
907 | | { |
908 | | #ifdef USE_WIN32_MUTEX |
909 | | HANDLE hMutex = (HANDLE)hMutexIn; |
910 | | |
911 | | CloseHandle(hMutex); |
912 | | #else |
913 | | CRITICAL_SECTION *pcs = reinterpret_cast<CRITICAL_SECTION *>(hMutexIn); |
914 | | |
915 | | DeleteCriticalSection(pcs); |
916 | | free(pcs); |
917 | | #endif |
918 | | } |
919 | | |
920 | | /************************************************************************/ |
921 | | /* CPLCreateCond() */ |
922 | | /************************************************************************/ |
923 | | |
924 | | struct _WaiterItem |
925 | | { |
926 | | HANDLE hEvent; |
927 | | struct _WaiterItem *psNext; |
928 | | }; |
929 | | typedef struct _WaiterItem WaiterItem; |
930 | | |
931 | | typedef struct |
932 | | { |
933 | | CPLMutex *hInternalMutex; |
934 | | WaiterItem *psWaiterList; |
935 | | } Win32Cond; |
936 | | |
937 | | CPLCond *CPLCreateCond() |
938 | | { |
939 | | Win32Cond *psCond = static_cast<Win32Cond *>(malloc(sizeof(Win32Cond))); |
940 | | if (psCond == nullptr) |
941 | | return nullptr; |
942 | | psCond->hInternalMutex = CPLCreateMutex(); |
943 | | if (psCond->hInternalMutex == nullptr) |
944 | | { |
945 | | free(psCond); |
946 | | return nullptr; |
947 | | } |
948 | | CPLReleaseMutex(psCond->hInternalMutex); |
949 | | psCond->psWaiterList = nullptr; |
950 | | return reinterpret_cast<CPLCond *>(psCond); |
951 | | } |
952 | | |
953 | | /************************************************************************/ |
954 | | /* CPLCondWait() */ |
955 | | /************************************************************************/ |
956 | | |
957 | | static void CPLTLSFreeEvent(void *pData) |
958 | | { |
959 | | CloseHandle(static_cast<HANDLE>(pData)); |
960 | | } |
961 | | |
962 | | void CPLCondWait(CPLCond *hCond, CPLMutex *hClientMutex) |
963 | | { |
964 | | CPLCondTimedWait(hCond, hClientMutex, -1); |
965 | | } |
966 | | |
967 | | /************************************************************************/ |
968 | | /* CPLCondTimedWait() */ |
969 | | /************************************************************************/ |
970 | | |
971 | | CPLCondTimedWaitReason CPLCondTimedWait(CPLCond *hCond, CPLMutex *hClientMutex, |
972 | | double dfWaitInSeconds) |
973 | | { |
974 | | Win32Cond *psCond = reinterpret_cast<Win32Cond *>(hCond); |
975 | | |
976 | | HANDLE hEvent = static_cast<HANDLE>(CPLGetTLS(CTLS_WIN32_COND)); |
977 | | if (hEvent == nullptr) |
978 | | { |
979 | | hEvent = CreateEvent(nullptr, /* security attributes */ |
980 | | 0, /* manual reset = no */ |
981 | | 0, /* initial state = unsignaled */ |
982 | | nullptr /* no name */); |
983 | | CPLAssert(hEvent != nullptr); |
984 | | |
985 | | CPLSetTLSWithFreeFunc(CTLS_WIN32_COND, hEvent, CPLTLSFreeEvent); |
986 | | } |
987 | | |
988 | | /* Insert the waiter into the waiter list of the condition */ |
989 | | CPLAcquireMutex(psCond->hInternalMutex, 1000.0); |
990 | | |
991 | | WaiterItem *psItem = static_cast<WaiterItem *>(malloc(sizeof(WaiterItem))); |
992 | | CPLAssert(psItem != nullptr); |
993 | | |
994 | | psItem->hEvent = hEvent; |
995 | | psItem->psNext = psCond->psWaiterList; |
996 | | |
997 | | psCond->psWaiterList = psItem; |
998 | | |
999 | | CPLReleaseMutex(psCond->hInternalMutex); |
1000 | | |
1001 | | // Release the client mutex before waiting for the event being signaled. |
1002 | | CPLReleaseMutex(hClientMutex); |
1003 | | |
1004 | | // Ideally we would check that we do not get WAIT_FAILED but it is hard |
1005 | | // to report a failure. |
1006 | | auto ret = WaitForSingleObject( |
1007 | | hEvent, dfWaitInSeconds < 0 ? INFINITE |
1008 | | : static_cast<int>(dfWaitInSeconds * 1000)); |
1009 | | |
1010 | | // Reacquire the client mutex. |
1011 | | CPLAcquireMutex(hClientMutex, 1000.0); |
1012 | | |
1013 | | if (ret == WAIT_OBJECT_0) |
1014 | | return COND_TIMED_WAIT_COND; |
1015 | | if (ret == WAIT_TIMEOUT) |
1016 | | return COND_TIMED_WAIT_TIME_OUT; |
1017 | | return COND_TIMED_WAIT_OTHER; |
1018 | | } |
1019 | | |
1020 | | /************************************************************************/ |
1021 | | /* CPLCondSignal() */ |
1022 | | /************************************************************************/ |
1023 | | |
1024 | | void CPLCondSignal(CPLCond *hCond) |
1025 | | { |
1026 | | Win32Cond *psCond = reinterpret_cast<Win32Cond *>(hCond); |
1027 | | |
1028 | | // Signal the first registered event, and remove it from the list. |
1029 | | CPLAcquireMutex(psCond->hInternalMutex, 1000.0); |
1030 | | |
1031 | | WaiterItem *psIter = psCond->psWaiterList; |
1032 | | if (psIter != nullptr) |
1033 | | { |
1034 | | SetEvent(psIter->hEvent); |
1035 | | psCond->psWaiterList = psIter->psNext; |
1036 | | free(psIter); |
1037 | | } |
1038 | | |
1039 | | CPLReleaseMutex(psCond->hInternalMutex); |
1040 | | } |
1041 | | |
1042 | | /************************************************************************/ |
1043 | | /* CPLCondBroadcast() */ |
1044 | | /************************************************************************/ |
1045 | | |
1046 | | void CPLCondBroadcast(CPLCond *hCond) |
1047 | | { |
1048 | | Win32Cond *psCond = reinterpret_cast<Win32Cond *>(hCond); |
1049 | | |
1050 | | // Signal all the registered events, and remove them from the list. |
1051 | | CPLAcquireMutex(psCond->hInternalMutex, 1000.0); |
1052 | | |
1053 | | WaiterItem *psIter = psCond->psWaiterList; |
1054 | | while (psIter != nullptr) |
1055 | | { |
1056 | | WaiterItem *psNext = psIter->psNext; |
1057 | | SetEvent(psIter->hEvent); |
1058 | | free(psIter); |
1059 | | psIter = psNext; |
1060 | | } |
1061 | | psCond->psWaiterList = nullptr; |
1062 | | |
1063 | | CPLReleaseMutex(psCond->hInternalMutex); |
1064 | | } |
1065 | | |
1066 | | /************************************************************************/ |
1067 | | /* CPLDestroyCond() */ |
1068 | | /************************************************************************/ |
1069 | | |
1070 | | void CPLDestroyCond(CPLCond *hCond) |
1071 | | { |
1072 | | Win32Cond *psCond = reinterpret_cast<Win32Cond *>(hCond); |
1073 | | CPLDestroyMutex(psCond->hInternalMutex); |
1074 | | psCond->hInternalMutex = nullptr; |
1075 | | CPLAssert(psCond->psWaiterList == nullptr); |
1076 | | free(psCond); |
1077 | | } |
1078 | | |
1079 | | /************************************************************************/ |
1080 | | /* CPLLockFile() */ |
1081 | | /************************************************************************/ |
1082 | | |
1083 | | void *CPLLockFile(const char *pszPath, double dfWaitInSeconds) |
1084 | | |
1085 | | { |
1086 | | char *pszLockFilename = |
1087 | | static_cast<char *>(CPLMalloc(strlen(pszPath) + 30)); |
1088 | | snprintf(pszLockFilename, strlen(pszPath) + 30, "%s.lock", pszPath); |
1089 | | |
1090 | | // FIXME: use CreateFileW() |
1091 | | HANDLE hLockFile = |
1092 | | CreateFileA(pszLockFilename, GENERIC_WRITE, 0, nullptr, CREATE_NEW, |
1093 | | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr); |
1094 | | |
1095 | | while (GetLastError() == ERROR_ALREADY_EXISTS && dfWaitInSeconds > 0.0) |
1096 | | { |
1097 | | CloseHandle(hLockFile); |
1098 | | CPLSleep(std::min(dfWaitInSeconds, 0.125)); |
1099 | | dfWaitInSeconds -= 0.125; |
1100 | | |
1101 | | hLockFile = CreateFileA( |
1102 | | pszLockFilename, GENERIC_WRITE, 0, nullptr, CREATE_NEW, |
1103 | | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, nullptr); |
1104 | | } |
1105 | | |
1106 | | CPLFree(pszLockFilename); |
1107 | | |
1108 | | if (hLockFile == INVALID_HANDLE_VALUE) |
1109 | | return nullptr; |
1110 | | |
1111 | | if (GetLastError() == ERROR_ALREADY_EXISTS) |
1112 | | { |
1113 | | CloseHandle(hLockFile); |
1114 | | return nullptr; |
1115 | | } |
1116 | | |
1117 | | return static_cast<void *>(hLockFile); |
1118 | | } |
1119 | | |
1120 | | /************************************************************************/ |
1121 | | /* CPLUnlockFile() */ |
1122 | | /************************************************************************/ |
1123 | | |
1124 | | void CPLUnlockFile(void *hLock) |
1125 | | |
1126 | | { |
1127 | | HANDLE hLockFile = static_cast<HANDLE>(hLock); |
1128 | | |
1129 | | CloseHandle(hLockFile); |
1130 | | } |
1131 | | |
1132 | | /************************************************************************/ |
1133 | | /* CPLGetPID() */ |
1134 | | /************************************************************************/ |
1135 | | |
1136 | | GIntBig CPLGetPID() |
1137 | | |
1138 | | { |
1139 | | return static_cast<GIntBig>(GetCurrentThreadId()); |
1140 | | } |
1141 | | |
1142 | | /************************************************************************/ |
1143 | | /* CPLStdCallThreadJacket() */ |
1144 | | /************************************************************************/ |
1145 | | |
1146 | | typedef struct |
1147 | | { |
1148 | | void *pAppData; |
1149 | | CPLThreadFunc pfnMain; |
1150 | | HANDLE hThread; |
1151 | | } CPLStdCallThreadInfo; |
1152 | | |
1153 | | static DWORD WINAPI CPLStdCallThreadJacket(void *pData) |
1154 | | |
1155 | | { |
1156 | | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>(pData); |
1157 | | |
1158 | | psInfo->pfnMain(psInfo->pAppData); |
1159 | | |
1160 | | if (psInfo->hThread == nullptr) |
1161 | | CPLFree(psInfo); // Only for detached threads. |
1162 | | |
1163 | | CPLCleanupTLS(); |
1164 | | |
1165 | | return 0; |
1166 | | } |
1167 | | |
1168 | | /************************************************************************/ |
1169 | | /* CPLCreateThread() */ |
1170 | | /* */ |
1171 | | /* The WIN32 CreateThread() call requires an entry point that */ |
1172 | | /* has __stdcall conventions, so we provide a jacket function */ |
1173 | | /* to supply that. */ |
1174 | | /************************************************************************/ |
1175 | | |
1176 | | int CPLCreateThread(CPLThreadFunc pfnMain, void *pThreadArg) |
1177 | | |
1178 | | { |
1179 | | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>( |
1180 | | CPLCalloc(sizeof(CPLStdCallThreadInfo), 1)); |
1181 | | psInfo->pAppData = pThreadArg; |
1182 | | psInfo->pfnMain = pfnMain; |
1183 | | psInfo->hThread = nullptr; |
1184 | | |
1185 | | DWORD nThreadId = 0; |
1186 | | HANDLE hThread = |
1187 | | CreateThread(nullptr, 0, CPLStdCallThreadJacket, psInfo, 0, &nThreadId); |
1188 | | |
1189 | | if (hThread == nullptr) |
1190 | | return -1; |
1191 | | |
1192 | | CloseHandle(hThread); |
1193 | | |
1194 | | return nThreadId; |
1195 | | } |
1196 | | |
1197 | | /************************************************************************/ |
1198 | | /* CPLCreateJoinableThread() */ |
1199 | | /************************************************************************/ |
1200 | | |
1201 | | CPLJoinableThread *CPLCreateJoinableThread(CPLThreadFunc pfnMain, |
1202 | | void *pThreadArg) |
1203 | | |
1204 | | { |
1205 | | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>( |
1206 | | CPLCalloc(sizeof(CPLStdCallThreadInfo), 1)); |
1207 | | psInfo->pAppData = pThreadArg; |
1208 | | psInfo->pfnMain = pfnMain; |
1209 | | |
1210 | | DWORD nThreadId = 0; |
1211 | | HANDLE hThread = |
1212 | | CreateThread(nullptr, 0, CPLStdCallThreadJacket, psInfo, 0, &nThreadId); |
1213 | | |
1214 | | if (hThread == nullptr) |
1215 | | return nullptr; |
1216 | | |
1217 | | psInfo->hThread = hThread; |
1218 | | return reinterpret_cast<CPLJoinableThread *>(psInfo); |
1219 | | } |
1220 | | |
1221 | | /************************************************************************/ |
1222 | | /* CPLJoinThread() */ |
1223 | | /************************************************************************/ |
1224 | | |
1225 | | void CPLJoinThread(CPLJoinableThread *hJoinableThread) |
1226 | | { |
1227 | | CPLStdCallThreadInfo *psInfo = |
1228 | | reinterpret_cast<CPLStdCallThreadInfo *>(hJoinableThread); |
1229 | | |
1230 | | WaitForSingleObject(psInfo->hThread, INFINITE); |
1231 | | CloseHandle(psInfo->hThread); |
1232 | | CPLFree(psInfo); |
1233 | | } |
1234 | | |
1235 | | /************************************************************************/ |
1236 | | /* CPLSleep() */ |
1237 | | /************************************************************************/ |
1238 | | |
1239 | | void CPLSleep(double dfWaitInSeconds) |
1240 | | |
1241 | | { |
1242 | | Sleep(static_cast<DWORD>(dfWaitInSeconds * 1000.0)); |
1243 | | } |
1244 | | |
1245 | | static bool bTLSKeySetup = false; |
1246 | | static DWORD nTLSKey = 0; |
1247 | | |
1248 | | /************************************************************************/ |
1249 | | /* CPLGetTLSList() */ |
1250 | | /************************************************************************/ |
1251 | | |
1252 | | static void **CPLGetTLSList(int *pbMemoryErrorOccurred) |
1253 | | |
1254 | | { |
1255 | | void **papTLSList = nullptr; |
1256 | | |
1257 | | if (pbMemoryErrorOccurred) |
1258 | | *pbMemoryErrorOccurred = FALSE; |
1259 | | if (!bTLSKeySetup) |
1260 | | { |
1261 | | nTLSKey = TlsAlloc(); |
1262 | | if (nTLSKey == TLS_OUT_OF_INDEXES) |
1263 | | { |
1264 | | if (pbMemoryErrorOccurred) |
1265 | | { |
1266 | | *pbMemoryErrorOccurred = TRUE; |
1267 | | fprintf(stderr, "CPLGetTLSList(): TlsAlloc() failed!\n"); |
1268 | | return nullptr; |
1269 | | } |
1270 | | CPLEmergencyError("CPLGetTLSList(): TlsAlloc() failed!"); |
1271 | | } |
1272 | | bTLSKeySetup = true; |
1273 | | } |
1274 | | |
1275 | | papTLSList = static_cast<void **>(TlsGetValue(nTLSKey)); |
1276 | | if (papTLSList == nullptr) |
1277 | | { |
1278 | | papTLSList = |
1279 | | static_cast<void **>(VSICalloc(sizeof(void *), CTLS_MAX * 2)); |
1280 | | if (papTLSList == nullptr) |
1281 | | { |
1282 | | if (pbMemoryErrorOccurred) |
1283 | | { |
1284 | | *pbMemoryErrorOccurred = TRUE; |
1285 | | fprintf(stderr, |
1286 | | "CPLGetTLSList() failed to allocate TLS list!\n"); |
1287 | | return nullptr; |
1288 | | } |
1289 | | CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); |
1290 | | } |
1291 | | if (TlsSetValue(nTLSKey, papTLSList) == 0) |
1292 | | { |
1293 | | if (pbMemoryErrorOccurred) |
1294 | | { |
1295 | | *pbMemoryErrorOccurred = TRUE; |
1296 | | fprintf(stderr, "CPLGetTLSList(): TlsSetValue() failed!\n"); |
1297 | | return nullptr; |
1298 | | } |
1299 | | CPLEmergencyError("CPLGetTLSList(): TlsSetValue() failed!"); |
1300 | | } |
1301 | | } |
1302 | | |
1303 | | return papTLSList; |
1304 | | } |
1305 | | |
1306 | | /************************************************************************/ |
1307 | | /* CPLFinalizeTLS() */ |
1308 | | /************************************************************************/ |
1309 | | |
1310 | | void CPLFinalizeTLS() |
1311 | | { |
1312 | | CPLCleanupTLS(); |
1313 | | } |
1314 | | |
1315 | | /************************************************************************/ |
1316 | | /* CPLCleanupTLS() */ |
1317 | | /************************************************************************/ |
1318 | | |
1319 | | void CPLCleanupTLS() |
1320 | | |
1321 | | { |
1322 | | if (!bTLSKeySetup) |
1323 | | return; |
1324 | | |
1325 | | void **papTLSList = static_cast<void **>(TlsGetValue(nTLSKey)); |
1326 | | if (papTLSList == nullptr) |
1327 | | return; |
1328 | | |
1329 | | TlsSetValue(nTLSKey, nullptr); |
1330 | | |
1331 | | CPLCleanupTLSList(papTLSList); |
1332 | | } |
1333 | | |
1334 | | // endif CPL_MULTIPROC_WIN32 |
1335 | | |
1336 | | #elif defined(CPL_MULTIPROC_PTHREAD) |
1337 | | |
1338 | | #include <pthread.h> |
1339 | | #include <time.h> |
1340 | | #include <unistd.h> |
1341 | | #include <sys/time.h> |
1342 | | |
1343 | | #ifdef HAVE_SCHED_GETAFFINITY |
1344 | | #include <sched.h> |
1345 | | #endif |
1346 | | |
1347 | | /************************************************************************/ |
1348 | | /* ==================================================================== */ |
1349 | | /* CPL_MULTIPROC_PTHREAD */ |
1350 | | /* */ |
1351 | | /* PTHREAD Implementation of multiprocessing functions. */ |
1352 | | /* ==================================================================== */ |
1353 | | /************************************************************************/ |
1354 | | |
1355 | | /************************************************************************/ |
1356 | | /* CPLGetNumCPUs() */ |
1357 | | /************************************************************************/ |
1358 | | |
1359 | | int CPLGetNumCPUs() |
1360 | 0 | { |
1361 | 0 | int nCPUs; |
1362 | 0 | #ifdef _SC_NPROCESSORS_ONLN |
1363 | 0 | nCPUs = static_cast<int>(sysconf(_SC_NPROCESSORS_ONLN)); |
1364 | | #else |
1365 | | nCPUs = 1; |
1366 | | #endif |
1367 | |
|
1368 | 0 | #ifdef HAVE_SCHED_GETAFFINITY |
1369 | 0 | if (nCPUs > 1) |
1370 | 0 | { |
1371 | 0 | cpu_set_t *set = CPU_ALLOC(nCPUs); |
1372 | 0 | if (set) |
1373 | 0 | { |
1374 | 0 | size_t sizeof_set = CPU_ALLOC_SIZE(nCPUs); |
1375 | 0 | CPU_ZERO_S(sizeof_set, set); |
1376 | 0 | if (sched_getaffinity(getpid(), sizeof_set, set) == 0) |
1377 | 0 | nCPUs = CPU_COUNT_S(sizeof_set, set); |
1378 | 0 | else |
1379 | 0 | CPLDebug("CPL", "sched_getaffinity() failed"); |
1380 | 0 | CPU_FREE(set); |
1381 | 0 | } |
1382 | 0 | } |
1383 | 0 | #endif |
1384 | |
|
1385 | 0 | return nCPUs; |
1386 | 0 | } |
1387 | | |
1388 | | /************************************************************************/ |
1389 | | /* CPLCreateOrAcquireMutex() */ |
1390 | | /************************************************************************/ |
1391 | | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1392 | | #pragma GCC diagnostic push |
1393 | | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" |
1394 | | #endif |
1395 | | static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; |
1396 | | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1397 | | #pragma GCC diagnostic pop |
1398 | | #endif |
1399 | | |
1400 | | static CPLMutex *CPLCreateMutexInternal(bool bAlreadyInGlobalLock, |
1401 | | int nOptions); |
1402 | | |
1403 | | int CPLCreateOrAcquireMutexEx(CPLMutex **phMutex, double dfWaitInSeconds, |
1404 | | int nOptions) |
1405 | | |
1406 | 267k | { |
1407 | 267k | pthread_mutex_lock(&global_mutex); |
1408 | 267k | if (*phMutex == nullptr) |
1409 | 9 | { |
1410 | 9 | *phMutex = CPLCreateMutexInternal(true, nOptions); |
1411 | 9 | const bool bSuccess = *phMutex != nullptr; |
1412 | 9 | pthread_mutex_unlock(&global_mutex); |
1413 | 9 | if (!bSuccess) |
1414 | 0 | return false; |
1415 | 9 | } |
1416 | 267k | else |
1417 | 267k | { |
1418 | 267k | pthread_mutex_unlock(&global_mutex); |
1419 | 267k | } |
1420 | | |
1421 | 267k | return CPL_TO_BOOL(CPLAcquireMutex(*phMutex, dfWaitInSeconds)); |
1422 | 267k | } |
1423 | | |
1424 | | /************************************************************************/ |
1425 | | /* CPLCreateOrAcquireMutexInternal() */ |
1426 | | /************************************************************************/ |
1427 | | |
1428 | | static bool CPLCreateOrAcquireMutexInternal(CPLLock **phLock, |
1429 | | double dfWaitInSeconds, |
1430 | | CPLLockType eType) |
1431 | 0 | { |
1432 | 0 | pthread_mutex_lock(&global_mutex); |
1433 | 0 | if (*phLock == nullptr) |
1434 | 0 | { |
1435 | 0 | *phLock = static_cast<CPLLock *>(calloc(1, sizeof(CPLLock))); |
1436 | 0 | if (*phLock) |
1437 | 0 | { |
1438 | 0 | (*phLock)->eType = eType; |
1439 | 0 | (*phLock)->u.hMutex = CPLCreateMutexInternal( |
1440 | 0 | true, eType == LOCK_RECURSIVE_MUTEX ? CPL_MUTEX_RECURSIVE |
1441 | 0 | : CPL_MUTEX_ADAPTIVE); |
1442 | 0 | if ((*phLock)->u.hMutex == nullptr) |
1443 | 0 | { |
1444 | 0 | free(*phLock); |
1445 | 0 | *phLock = nullptr; |
1446 | 0 | } |
1447 | 0 | } |
1448 | 0 | const bool bSuccess = *phLock != nullptr; |
1449 | 0 | pthread_mutex_unlock(&global_mutex); |
1450 | 0 | if (!bSuccess) |
1451 | 0 | return false; |
1452 | 0 | } |
1453 | 0 | else |
1454 | 0 | { |
1455 | 0 | pthread_mutex_unlock(&global_mutex); |
1456 | 0 | } |
1457 | | |
1458 | 0 | return CPL_TO_BOOL(CPLAcquireMutex((*phLock)->u.hMutex, dfWaitInSeconds)); |
1459 | 0 | } |
1460 | | |
1461 | | /************************************************************************/ |
1462 | | /* CPLGetThreadingModel() */ |
1463 | | /************************************************************************/ |
1464 | | |
1465 | | const char *CPLGetThreadingModel() |
1466 | | |
1467 | 0 | { |
1468 | 0 | return "pthread"; |
1469 | 0 | } |
1470 | | |
1471 | | /************************************************************************/ |
1472 | | /* CPLCreateMutex() */ |
1473 | | /************************************************************************/ |
1474 | | |
1475 | | typedef struct _MutexLinkedElt MutexLinkedElt; |
1476 | | |
1477 | | struct _MutexLinkedElt |
1478 | | { |
1479 | | pthread_mutex_t sMutex; |
1480 | | int nOptions; |
1481 | | _MutexLinkedElt *psPrev; |
1482 | | _MutexLinkedElt *psNext; |
1483 | | }; |
1484 | | |
1485 | | static MutexLinkedElt *psMutexList = nullptr; |
1486 | | |
1487 | | static void CPLInitMutex(MutexLinkedElt *psItem) |
1488 | 9 | { |
1489 | 9 | if (psItem->nOptions == CPL_MUTEX_REGULAR) |
1490 | 0 | { |
1491 | 0 | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1492 | 0 | #pragma GCC diagnostic push |
1493 | 0 | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" |
1494 | 0 | #endif |
1495 | 0 | pthread_mutex_t tmp_mutex = PTHREAD_MUTEX_INITIALIZER; |
1496 | 0 | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1497 | 0 | #pragma GCC diagnostic pop |
1498 | 0 | #endif |
1499 | 0 | psItem->sMutex = tmp_mutex; |
1500 | 0 | return; |
1501 | 0 | } |
1502 | | |
1503 | | // When an adaptive mutex is required, we can safely fallback to regular |
1504 | | // mutex if we don't have HAVE_PTHREAD_MUTEX_ADAPTIVE_NP. |
1505 | 9 | if (psItem->nOptions == CPL_MUTEX_ADAPTIVE) |
1506 | 0 | { |
1507 | 0 | #if defined(HAVE_PTHREAD_MUTEX_ADAPTIVE_NP) |
1508 | 0 | pthread_mutexattr_t attr; |
1509 | 0 | pthread_mutexattr_init(&attr); |
1510 | 0 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); |
1511 | 0 | pthread_mutex_init(&(psItem->sMutex), &attr); |
1512 | | #else |
1513 | | pthread_mutex_t tmp_mutex = PTHREAD_MUTEX_INITIALIZER; |
1514 | | psItem->sMutex = tmp_mutex; |
1515 | | #endif |
1516 | 0 | return; |
1517 | 0 | } |
1518 | | |
1519 | 9 | #if defined(PTHREAD_MUTEX_RECURSIVE) || defined(HAVE_PTHREAD_MUTEX_RECURSIVE) |
1520 | 9 | { |
1521 | 9 | pthread_mutexattr_t attr; |
1522 | 9 | pthread_mutexattr_init(&attr); |
1523 | 9 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
1524 | 9 | pthread_mutex_init(&(psItem->sMutex), &attr); |
1525 | 9 | } |
1526 | | // BSDs have PTHREAD_MUTEX_RECURSIVE as an enum, not a define. |
1527 | | // But they have #define MUTEX_TYPE_COUNTING_FAST PTHREAD_MUTEX_RECURSIVE |
1528 | | #elif defined(MUTEX_TYPE_COUNTING_FAST) |
1529 | | { |
1530 | | pthread_mutexattr_t attr; |
1531 | | pthread_mutexattr_init(&attr); |
1532 | | pthread_mutexattr_settype(&attr, MUTEX_TYPE_COUNTING_FAST); |
1533 | | pthread_mutex_init(&(psItem->sMutex), &attr); |
1534 | | } |
1535 | | #elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) |
1536 | | pthread_mutex_t tmp_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; |
1537 | | psItem->sMutex = tmp_mutex; |
1538 | | #else |
1539 | | #error "Recursive mutexes apparently unsupported, configure --without-threads" |
1540 | | #endif |
1541 | 9 | } |
1542 | | |
1543 | | static CPLMutex *CPLCreateMutexInternal(bool bAlreadyInGlobalLock, int nOptions) |
1544 | 9 | { |
1545 | 9 | MutexLinkedElt *psItem = |
1546 | 9 | static_cast<MutexLinkedElt *>(malloc(sizeof(MutexLinkedElt))); |
1547 | 9 | if (psItem == nullptr) |
1548 | 0 | { |
1549 | 0 | fprintf(stderr, "CPLCreateMutexInternal() failed.\n"); |
1550 | 0 | return nullptr; |
1551 | 0 | } |
1552 | | |
1553 | 9 | if (!bAlreadyInGlobalLock) |
1554 | 0 | pthread_mutex_lock(&global_mutex); |
1555 | 9 | psItem->psPrev = nullptr; |
1556 | 9 | psItem->psNext = psMutexList; |
1557 | 9 | if (psMutexList) |
1558 | 7 | psMutexList->psPrev = psItem; |
1559 | 9 | psMutexList = psItem; |
1560 | 9 | if (!bAlreadyInGlobalLock) |
1561 | 0 | pthread_mutex_unlock(&global_mutex); |
1562 | | |
1563 | 9 | psItem->nOptions = nOptions; |
1564 | 9 | CPLInitMutex(psItem); |
1565 | | |
1566 | 9 | return reinterpret_cast<CPLMutex *>(psItem); |
1567 | 9 | } |
1568 | | |
1569 | | CPLMutex *CPLCreateMutex() |
1570 | 0 | { |
1571 | 0 | CPLMutex *mutex = CPLCreateMutexInternal(false, CPL_MUTEX_RECURSIVE); |
1572 | 0 | if (mutex) |
1573 | 0 | CPLAcquireMutex(mutex, 0); |
1574 | 0 | return mutex; |
1575 | 0 | } |
1576 | | |
1577 | | CPLMutex *CPLCreateMutexEx(int nOptions) |
1578 | 0 | { |
1579 | 0 | CPLMutex *mutex = CPLCreateMutexInternal(false, nOptions); |
1580 | 0 | if (mutex) |
1581 | 0 | CPLAcquireMutex(mutex, 0); |
1582 | 0 | return mutex; |
1583 | 0 | } |
1584 | | |
1585 | | /************************************************************************/ |
1586 | | /* CPLAcquireMutex() */ |
1587 | | /************************************************************************/ |
1588 | | |
1589 | | int CPLAcquireMutex(CPLMutex *hMutexIn, double /* dfWaitInSeconds */) |
1590 | 267k | { |
1591 | | // TODO: Need to add timeout support. |
1592 | 267k | MutexLinkedElt *psItem = reinterpret_cast<MutexLinkedElt *>(hMutexIn); |
1593 | 267k | const int err = pthread_mutex_lock(&(psItem->sMutex)); |
1594 | | |
1595 | 267k | if (err != 0) |
1596 | 0 | { |
1597 | 0 | if (err == EDEADLK) |
1598 | 0 | fprintf(stderr, "CPLAcquireMutex: Error = %d/EDEADLK\n", err); |
1599 | 0 | else |
1600 | 0 | fprintf(stderr, "CPLAcquireMutex: Error = %d (%s)\n", err, |
1601 | 0 | strerror(err)); |
1602 | |
|
1603 | 0 | return FALSE; |
1604 | 0 | } |
1605 | | |
1606 | 267k | return TRUE; |
1607 | 267k | } |
1608 | | |
1609 | | /************************************************************************/ |
1610 | | /* CPLReleaseMutex() */ |
1611 | | /************************************************************************/ |
1612 | | |
1613 | | void CPLReleaseMutex(CPLMutex *hMutexIn) |
1614 | | |
1615 | 267k | { |
1616 | 267k | MutexLinkedElt *psItem = reinterpret_cast<MutexLinkedElt *>(hMutexIn); |
1617 | 267k | const int err = pthread_mutex_unlock(&(psItem->sMutex)); |
1618 | 267k | if (err != 0) |
1619 | 0 | { |
1620 | 0 | fprintf(stderr, "CPLReleaseMutex: Error = %d (%s)\n", err, |
1621 | 0 | strerror(err)); |
1622 | 0 | } |
1623 | 267k | } |
1624 | | |
1625 | | /************************************************************************/ |
1626 | | /* CPLDestroyMutex() */ |
1627 | | /************************************************************************/ |
1628 | | |
1629 | | void CPLDestroyMutex(CPLMutex *hMutexIn) |
1630 | | |
1631 | 0 | { |
1632 | 0 | MutexLinkedElt *psItem = reinterpret_cast<MutexLinkedElt *>(hMutexIn); |
1633 | 0 | const int err = pthread_mutex_destroy(&(psItem->sMutex)); |
1634 | 0 | if (err != 0) |
1635 | 0 | { |
1636 | 0 | fprintf(stderr, "CPLDestroyMutex: Error = %d (%s)\n", err, |
1637 | 0 | strerror(err)); |
1638 | 0 | } |
1639 | 0 | pthread_mutex_lock(&global_mutex); |
1640 | 0 | if (psItem->psPrev) |
1641 | 0 | psItem->psPrev->psNext = psItem->psNext; |
1642 | 0 | if (psItem->psNext) |
1643 | 0 | psItem->psNext->psPrev = psItem->psPrev; |
1644 | 0 | if (psItem == psMutexList) |
1645 | 0 | psMutexList = psItem->psNext; |
1646 | 0 | pthread_mutex_unlock(&global_mutex); |
1647 | 0 | free(hMutexIn); |
1648 | 0 | } |
1649 | | |
1650 | | /************************************************************************/ |
1651 | | /* CPLReinitAllMutex() */ |
1652 | | /************************************************************************/ |
1653 | | |
1654 | | // Used by gdalclientserver.cpp just after forking, to avoid |
1655 | | // deadlocks while mixing threads with fork. |
1656 | | void CPLReinitAllMutex(); // TODO(schwehr): Put this in a header. |
1657 | | |
1658 | | void CPLReinitAllMutex() |
1659 | 0 | { |
1660 | 0 | MutexLinkedElt *psItem = psMutexList; |
1661 | 0 | while (psItem != nullptr) |
1662 | 0 | { |
1663 | 0 | CPLInitMutex(psItem); |
1664 | 0 | psItem = psItem->psNext; |
1665 | 0 | } |
1666 | 0 | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1667 | 0 | #pragma GCC diagnostic push |
1668 | 0 | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" |
1669 | 0 | #endif |
1670 | 0 | pthread_mutex_t tmp_global_mutex = PTHREAD_MUTEX_INITIALIZER; |
1671 | 0 | #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT |
1672 | 0 | #pragma GCC diagnostic pop |
1673 | 0 | #endif |
1674 | 0 | global_mutex = tmp_global_mutex; |
1675 | 0 | } |
1676 | | |
1677 | | /************************************************************************/ |
1678 | | /* CPLCreateCond() */ |
1679 | | /************************************************************************/ |
1680 | | |
1681 | | CPLCond *CPLCreateCond() |
1682 | 0 | { |
1683 | 0 | pthread_cond_t *pCond = |
1684 | 0 | static_cast<pthread_cond_t *>(malloc(sizeof(pthread_cond_t))); |
1685 | 0 | if (pCond && pthread_cond_init(pCond, nullptr) == 0) |
1686 | 0 | return reinterpret_cast<CPLCond *>(pCond); |
1687 | 0 | fprintf(stderr, "CPLCreateCond() failed.\n"); |
1688 | 0 | free(pCond); |
1689 | 0 | return nullptr; |
1690 | 0 | } |
1691 | | |
1692 | | /************************************************************************/ |
1693 | | /* CPLCondWait() */ |
1694 | | /************************************************************************/ |
1695 | | |
1696 | | void CPLCondWait(CPLCond *hCond, CPLMutex *hMutex) |
1697 | 0 | { |
1698 | 0 | pthread_cond_t *pCond = reinterpret_cast<pthread_cond_t *>(hCond); |
1699 | 0 | MutexLinkedElt *psItem = reinterpret_cast<MutexLinkedElt *>(hMutex); |
1700 | 0 | pthread_mutex_t *pMutex = &(psItem->sMutex); |
1701 | 0 | pthread_cond_wait(pCond, pMutex); |
1702 | 0 | } |
1703 | | |
1704 | | /************************************************************************/ |
1705 | | /* CPLCondTimedWait() */ |
1706 | | /************************************************************************/ |
1707 | | |
1708 | | CPLCondTimedWaitReason CPLCondTimedWait(CPLCond *hCond, CPLMutex *hMutex, |
1709 | | double dfWaitInSeconds) |
1710 | 0 | { |
1711 | 0 | pthread_cond_t *pCond = reinterpret_cast<pthread_cond_t *>(hCond); |
1712 | 0 | MutexLinkedElt *psItem = reinterpret_cast<MutexLinkedElt *>(hMutex); |
1713 | 0 | pthread_mutex_t *pMutex = &(psItem->sMutex); |
1714 | 0 | struct timeval tv; |
1715 | 0 | struct timespec ts; |
1716 | |
|
1717 | 0 | gettimeofday(&tv, nullptr); |
1718 | 0 | ts.tv_sec = time(nullptr) + static_cast<int>(dfWaitInSeconds); |
1719 | 0 | ts.tv_nsec = |
1720 | 0 | static_cast<int>(tv.tv_usec) * 1000 + |
1721 | 0 | static_cast<int>(1000 * 1000 * 1000 * fmod(dfWaitInSeconds, 1)); |
1722 | 0 | ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); |
1723 | 0 | ts.tv_nsec %= (1000 * 1000 * 1000); |
1724 | 0 | int ret = pthread_cond_timedwait(pCond, pMutex, &ts); |
1725 | 0 | if (ret == 0) |
1726 | 0 | return COND_TIMED_WAIT_COND; |
1727 | 0 | else if (ret == ETIMEDOUT) |
1728 | 0 | return COND_TIMED_WAIT_TIME_OUT; |
1729 | 0 | else |
1730 | 0 | return COND_TIMED_WAIT_OTHER; |
1731 | 0 | } |
1732 | | |
1733 | | /************************************************************************/ |
1734 | | /* CPLCondSignal() */ |
1735 | | /************************************************************************/ |
1736 | | |
1737 | | void CPLCondSignal(CPLCond *hCond) |
1738 | 0 | { |
1739 | 0 | pthread_cond_t *pCond = reinterpret_cast<pthread_cond_t *>(hCond); |
1740 | 0 | pthread_cond_signal(pCond); |
1741 | 0 | } |
1742 | | |
1743 | | /************************************************************************/ |
1744 | | /* CPLCondBroadcast() */ |
1745 | | /************************************************************************/ |
1746 | | |
1747 | | void CPLCondBroadcast(CPLCond *hCond) |
1748 | 0 | { |
1749 | 0 | pthread_cond_t *pCond = reinterpret_cast<pthread_cond_t *>(hCond); |
1750 | 0 | pthread_cond_broadcast(pCond); |
1751 | 0 | } |
1752 | | |
1753 | | /************************************************************************/ |
1754 | | /* CPLDestroyCond() */ |
1755 | | /************************************************************************/ |
1756 | | |
1757 | | void CPLDestroyCond(CPLCond *hCond) |
1758 | 0 | { |
1759 | 0 | pthread_cond_t *pCond = reinterpret_cast<pthread_cond_t *>(hCond); |
1760 | 0 | pthread_cond_destroy(pCond); |
1761 | 0 | free(hCond); |
1762 | 0 | } |
1763 | | |
1764 | | /************************************************************************/ |
1765 | | /* CPLLockFile() */ |
1766 | | /* */ |
1767 | | /* This is really a stub implementation, see first */ |
1768 | | /* CPLLockFile() for caveats. */ |
1769 | | /************************************************************************/ |
1770 | | |
1771 | | void *CPLLockFile(const char *pszPath, double dfWaitInSeconds) |
1772 | | |
1773 | 0 | { |
1774 | | /* -------------------------------------------------------------------- */ |
1775 | | /* We use a lock file with a name derived from the file we want */ |
1776 | | /* to lock to represent the file being locked. Note that for */ |
1777 | | /* the stub implementation the target file does not even need */ |
1778 | | /* to exist to be locked. */ |
1779 | | /* -------------------------------------------------------------------- */ |
1780 | 0 | const size_t nLen = strlen(pszPath) + 30; |
1781 | 0 | char *pszLockFilename = static_cast<char *>(CPLMalloc(nLen)); |
1782 | 0 | snprintf(pszLockFilename, nLen, "%s.lock", pszPath); |
1783 | |
|
1784 | 0 | FILE *fpLock = fopen(pszLockFilename, "r"); |
1785 | 0 | while (fpLock != nullptr && dfWaitInSeconds > 0.0) |
1786 | 0 | { |
1787 | 0 | fclose(fpLock); |
1788 | 0 | CPLSleep(std::min(dfWaitInSeconds, 0.5)); |
1789 | 0 | dfWaitInSeconds -= 0.5; |
1790 | |
|
1791 | 0 | fpLock = fopen(pszLockFilename, "r"); |
1792 | 0 | } |
1793 | |
|
1794 | 0 | if (fpLock != nullptr) |
1795 | 0 | { |
1796 | 0 | fclose(fpLock); |
1797 | 0 | CPLFree(pszLockFilename); |
1798 | 0 | return nullptr; |
1799 | 0 | } |
1800 | | |
1801 | 0 | fpLock = fopen(pszLockFilename, "w"); |
1802 | |
|
1803 | 0 | if (fpLock == nullptr) |
1804 | 0 | { |
1805 | 0 | CPLFree(pszLockFilename); |
1806 | 0 | return nullptr; |
1807 | 0 | } |
1808 | | |
1809 | 0 | fwrite("held\n", 1, 5, fpLock); |
1810 | 0 | fclose(fpLock); |
1811 | |
|
1812 | 0 | return pszLockFilename; |
1813 | 0 | } |
1814 | | |
1815 | | /************************************************************************/ |
1816 | | /* CPLUnlockFile() */ |
1817 | | /************************************************************************/ |
1818 | | |
1819 | | void CPLUnlockFile(void *hLock) |
1820 | | |
1821 | 0 | { |
1822 | 0 | char *pszLockFilename = static_cast<char *>(hLock); |
1823 | |
|
1824 | 0 | if (hLock == nullptr) |
1825 | 0 | return; |
1826 | | |
1827 | 0 | VSIUnlink(pszLockFilename); |
1828 | |
|
1829 | 0 | CPLFree(pszLockFilename); |
1830 | 0 | } |
1831 | | |
1832 | | /************************************************************************/ |
1833 | | /* CPLGetPID() */ |
1834 | | /************************************************************************/ |
1835 | | |
1836 | | GIntBig CPLGetPID() |
1837 | | |
1838 | 5.10k | { |
1839 | 5.10k | return reinterpret_cast<GIntBig>(reinterpret_cast<void *>(pthread_self())); |
1840 | 5.10k | } |
1841 | | |
1842 | | static pthread_key_t oTLSKey; |
1843 | | static pthread_once_t oTLSKeySetup = PTHREAD_ONCE_INIT; |
1844 | | |
1845 | | /************************************************************************/ |
1846 | | /* CPLMake_key() */ |
1847 | | /************************************************************************/ |
1848 | | |
1849 | | static void CPLMake_key() |
1850 | | |
1851 | 2 | { |
1852 | 2 | if (pthread_key_create(&oTLSKey, reinterpret_cast<void (*)(void *)>( |
1853 | 2 | CPLCleanupTLSList)) != 0) |
1854 | 0 | { |
1855 | 0 | CPLError(CE_Fatal, CPLE_AppDefined, "pthread_key_create() failed!"); |
1856 | 0 | } |
1857 | 2 | } |
1858 | | |
1859 | | /************************************************************************/ |
1860 | | /* CPLGetTLSList() */ |
1861 | | /************************************************************************/ |
1862 | | |
1863 | | static void **CPLGetTLSList(int *pbMemoryErrorOccurred) |
1864 | | |
1865 | 227k | { |
1866 | 227k | if (pbMemoryErrorOccurred) |
1867 | 205k | *pbMemoryErrorOccurred = FALSE; |
1868 | | |
1869 | 227k | if (pthread_once(&oTLSKeySetup, CPLMake_key) != 0) |
1870 | 0 | { |
1871 | 0 | if (pbMemoryErrorOccurred) |
1872 | 0 | { |
1873 | 0 | fprintf(stderr, "CPLGetTLSList(): pthread_once() failed!\n"); |
1874 | 0 | *pbMemoryErrorOccurred = TRUE; |
1875 | 0 | return nullptr; |
1876 | 0 | } |
1877 | 0 | CPLEmergencyError("CPLGetTLSList(): pthread_once() failed!"); |
1878 | 0 | } |
1879 | | |
1880 | 227k | void **papTLSList = static_cast<void **>(pthread_getspecific(oTLSKey)); |
1881 | 227k | if (papTLSList == nullptr) |
1882 | 2 | { |
1883 | 2 | papTLSList = |
1884 | 2 | static_cast<void **>(VSICalloc(sizeof(void *), CTLS_MAX * 2)); |
1885 | 2 | if (papTLSList == nullptr) |
1886 | 0 | { |
1887 | 0 | if (pbMemoryErrorOccurred) |
1888 | 0 | { |
1889 | 0 | fprintf(stderr, |
1890 | 0 | "CPLGetTLSList() failed to allocate TLS list!\n"); |
1891 | 0 | *pbMemoryErrorOccurred = TRUE; |
1892 | 0 | return nullptr; |
1893 | 0 | } |
1894 | 0 | CPLEmergencyError("CPLGetTLSList() failed to allocate TLS list!"); |
1895 | 0 | } |
1896 | 2 | if (pthread_setspecific(oTLSKey, papTLSList) != 0) |
1897 | 0 | { |
1898 | 0 | if (pbMemoryErrorOccurred) |
1899 | 0 | { |
1900 | 0 | fprintf(stderr, |
1901 | 0 | "CPLGetTLSList(): pthread_setspecific() failed!\n"); |
1902 | 0 | *pbMemoryErrorOccurred = TRUE; |
1903 | 0 | return nullptr; |
1904 | 0 | } |
1905 | 0 | CPLEmergencyError("CPLGetTLSList(): pthread_setspecific() failed!"); |
1906 | 0 | } |
1907 | 2 | } |
1908 | | |
1909 | 227k | return papTLSList; |
1910 | 227k | } |
1911 | | |
1912 | | /************************************************************************/ |
1913 | | /* CPLStdCallThreadJacket() */ |
1914 | | /************************************************************************/ |
1915 | | |
1916 | | typedef struct |
1917 | | { |
1918 | | void *pAppData; |
1919 | | CPLThreadFunc pfnMain; |
1920 | | pthread_t hThread; |
1921 | | bool bJoinable; |
1922 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
1923 | | bool bInitSucceeded; |
1924 | | bool bInitDone; |
1925 | | pthread_mutex_t sMutex; |
1926 | | pthread_cond_t sCond; |
1927 | | #endif |
1928 | | } CPLStdCallThreadInfo; |
1929 | | |
1930 | | static void *CPLStdCallThreadJacket(void *pData) |
1931 | | |
1932 | 0 | { |
1933 | 0 | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>(pData); |
1934 | |
|
1935 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
1936 | | int bMemoryError = FALSE; |
1937 | | CPLGetTLSList(&bMemoryError); |
1938 | | if (bMemoryError) |
1939 | | goto error; |
1940 | | |
1941 | | assert(pthread_mutex_lock(&(psInfo->sMutex)) == 0); |
1942 | | psInfo->bInitDone = true; |
1943 | | assert(pthread_cond_signal(&(psInfo->sCond)) == 0); |
1944 | | assert(pthread_mutex_unlock(&(psInfo->sMutex)) == 0); |
1945 | | #endif |
1946 | |
|
1947 | 0 | psInfo->pfnMain(psInfo->pAppData); |
1948 | |
|
1949 | 0 | if (!psInfo->bJoinable) |
1950 | 0 | CPLFree(psInfo); |
1951 | |
|
1952 | 0 | return nullptr; |
1953 | |
|
1954 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
1955 | | error: |
1956 | | assert(pthread_mutex_lock(&(psInfo->sMutex)) == 0); |
1957 | | psInfo->bInitSucceeded = false; |
1958 | | psInfo->bInitDone = true; |
1959 | | assert(pthread_cond_signal(&(psInfo->sCond)) == 0); |
1960 | | assert(pthread_mutex_unlock(&(psInfo->sMutex)) == 0); |
1961 | | return nullptr; |
1962 | | #endif |
1963 | 0 | } |
1964 | | |
1965 | | /************************************************************************/ |
1966 | | /* CPLCreateThread() */ |
1967 | | /* */ |
1968 | | /* The WIN32 CreateThread() call requires an entry point that */ |
1969 | | /* has __stdcall conventions, so we provide a jacket function */ |
1970 | | /* to supply that. */ |
1971 | | /************************************************************************/ |
1972 | | |
1973 | | int CPLCreateThread(CPLThreadFunc pfnMain, void *pThreadArg) |
1974 | | |
1975 | 0 | { |
1976 | 0 | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>( |
1977 | 0 | VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo), 1)); |
1978 | 0 | if (psInfo == nullptr) |
1979 | 0 | return -1; |
1980 | 0 | psInfo->pAppData = pThreadArg; |
1981 | 0 | psInfo->pfnMain = pfnMain; |
1982 | 0 | psInfo->bJoinable = false; |
1983 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
1984 | | psInfo->bInitSucceeded = true; |
1985 | | psInfo->bInitDone = false; |
1986 | | pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; |
1987 | | psInfo->sMutex = sMutex; |
1988 | | if (pthread_cond_init(&(psInfo->sCond), nullptr) != 0) |
1989 | | { |
1990 | | CPLFree(psInfo); |
1991 | | fprintf(stderr, "CPLCreateThread() failed.\n"); |
1992 | | return -1; |
1993 | | } |
1994 | | #endif |
1995 | |
|
1996 | 0 | pthread_attr_t hThreadAttr; |
1997 | 0 | pthread_attr_init(&hThreadAttr); |
1998 | 0 | pthread_attr_setdetachstate(&hThreadAttr, PTHREAD_CREATE_DETACHED); |
1999 | 0 | if (pthread_create(&(psInfo->hThread), &hThreadAttr, CPLStdCallThreadJacket, |
2000 | 0 | static_cast<void *>(psInfo)) != 0) |
2001 | 0 | { |
2002 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
2003 | | pthread_cond_destroy(&(psInfo->sCond)); |
2004 | | #endif |
2005 | 0 | CPLFree(psInfo); |
2006 | 0 | fprintf(stderr, "CPLCreateThread() failed.\n"); |
2007 | 0 | return -1; |
2008 | 0 | } |
2009 | | |
2010 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
2011 | | bool bInitSucceeded; |
2012 | | while (true) |
2013 | | { |
2014 | | assert(pthread_mutex_lock(&(psInfo->sMutex)) == 0); |
2015 | | bool bInitDone = psInfo->bInitDone; |
2016 | | if (!bInitDone) |
2017 | | assert(pthread_cond_wait(&(psInfo->sCond), &(psInfo->sMutex)) == 0); |
2018 | | bInitSucceeded = psInfo->bInitSucceeded; |
2019 | | assert(pthread_mutex_unlock(&(psInfo->sMutex)) == 0); |
2020 | | if (bInitDone) |
2021 | | break; |
2022 | | } |
2023 | | |
2024 | | pthread_cond_destroy(&(psInfo->sCond)); |
2025 | | |
2026 | | if (!bInitSucceeded) |
2027 | | { |
2028 | | CPLFree(psInfo); |
2029 | | fprintf(stderr, "CPLCreateThread() failed.\n"); |
2030 | | return -1; |
2031 | | } |
2032 | | #endif |
2033 | | |
2034 | 0 | return 1; // Can we return the actual thread pid? |
2035 | 0 | } |
2036 | | |
2037 | | /************************************************************************/ |
2038 | | /* CPLCreateJoinableThread() */ |
2039 | | /************************************************************************/ |
2040 | | |
2041 | | CPLJoinableThread *CPLCreateJoinableThread(CPLThreadFunc pfnMain, |
2042 | | void *pThreadArg) |
2043 | | |
2044 | 0 | { |
2045 | 0 | CPLStdCallThreadInfo *psInfo = static_cast<CPLStdCallThreadInfo *>( |
2046 | 0 | VSI_CALLOC_VERBOSE(sizeof(CPLStdCallThreadInfo), 1)); |
2047 | 0 | if (psInfo == nullptr) |
2048 | 0 | return nullptr; |
2049 | 0 | psInfo->pAppData = pThreadArg; |
2050 | 0 | psInfo->pfnMain = pfnMain; |
2051 | 0 | psInfo->bJoinable = true; |
2052 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
2053 | | psInfo->bInitSucceeded = true; |
2054 | | psInfo->bInitDone = false; |
2055 | | pthread_mutex_t sMutex = PTHREAD_MUTEX_INITIALIZER; |
2056 | | psInfo->sMutex = sMutex; |
2057 | | { |
2058 | | int err = pthread_cond_init(&(psInfo->sCond), nullptr); |
2059 | | if (err != 0) |
2060 | | { |
2061 | | CPLFree(psInfo); |
2062 | | fprintf(stderr, "CPLCreateJoinableThread() failed: %s.\n", |
2063 | | strerror(err)); |
2064 | | return nullptr; |
2065 | | } |
2066 | | } |
2067 | | #endif |
2068 | |
|
2069 | 0 | pthread_attr_t hThreadAttr; |
2070 | 0 | pthread_attr_init(&hThreadAttr); |
2071 | 0 | pthread_attr_setdetachstate(&hThreadAttr, PTHREAD_CREATE_JOINABLE); |
2072 | 0 | int err = |
2073 | 0 | pthread_create(&(psInfo->hThread), &hThreadAttr, CPLStdCallThreadJacket, |
2074 | 0 | static_cast<void *>(psInfo)); |
2075 | 0 | if (err != 0) |
2076 | 0 | { |
2077 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
2078 | | pthread_cond_destroy(&(psInfo->sCond)); |
2079 | | #endif |
2080 | 0 | CPLFree(psInfo); |
2081 | 0 | fprintf(stderr, "CPLCreateJoinableThread() failed: %s.\n", |
2082 | 0 | strerror(err)); |
2083 | 0 | return nullptr; |
2084 | 0 | } |
2085 | | |
2086 | | #ifdef CHECK_THREAD_CAN_ALLOCATE_TLS |
2087 | | bool bInitSucceeded; |
2088 | | while (true) |
2089 | | { |
2090 | | assert(pthread_mutex_lock(&(psInfo->sMutex)) == 0); |
2091 | | bool bInitDone = psInfo->bInitDone; |
2092 | | if (!bInitDone) |
2093 | | assert(pthread_cond_wait(&(psInfo->sCond), &(psInfo->sMutex)) == 0); |
2094 | | bInitSucceeded = psInfo->bInitSucceeded; |
2095 | | assert(pthread_mutex_unlock(&(psInfo->sMutex)) == 0); |
2096 | | if (bInitDone) |
2097 | | break; |
2098 | | } |
2099 | | |
2100 | | pthread_cond_destroy(&(psInfo->sCond)); |
2101 | | |
2102 | | if (!bInitSucceeded) |
2103 | | { |
2104 | | void *status; |
2105 | | pthread_join(psInfo->hThread, &status); |
2106 | | CPLFree(psInfo); |
2107 | | fprintf(stderr, "CPLCreateJoinableThread() failed.\n"); |
2108 | | return nullptr; |
2109 | | } |
2110 | | #endif |
2111 | | |
2112 | 0 | return reinterpret_cast<CPLJoinableThread *>(psInfo); |
2113 | 0 | } |
2114 | | |
2115 | | /************************************************************************/ |
2116 | | /* CPLJoinThread() */ |
2117 | | /************************************************************************/ |
2118 | | |
2119 | | void CPLJoinThread(CPLJoinableThread *hJoinableThread) |
2120 | 0 | { |
2121 | 0 | CPLStdCallThreadInfo *psInfo = |
2122 | 0 | reinterpret_cast<CPLStdCallThreadInfo *>(hJoinableThread); |
2123 | 0 | if (psInfo == nullptr) |
2124 | 0 | return; |
2125 | | |
2126 | 0 | void *status; |
2127 | 0 | pthread_join(psInfo->hThread, &status); |
2128 | |
|
2129 | 0 | CPLFree(psInfo); |
2130 | 0 | } |
2131 | | |
2132 | | /************************************************************************/ |
2133 | | /* CPLSleep() */ |
2134 | | /************************************************************************/ |
2135 | | |
2136 | | void CPLSleep(double dfWaitInSeconds) |
2137 | | |
2138 | 0 | { |
2139 | 0 | struct timespec sRequest; |
2140 | 0 | struct timespec sRemain; |
2141 | |
|
2142 | 0 | sRequest.tv_sec = static_cast<int>(floor(dfWaitInSeconds)); |
2143 | 0 | sRequest.tv_nsec = |
2144 | 0 | static_cast<int>((dfWaitInSeconds - sRequest.tv_sec) * 1000000000); |
2145 | 0 | nanosleep(&sRequest, &sRemain); |
2146 | 0 | } |
2147 | | |
2148 | | /************************************************************************/ |
2149 | | /* CPLFinalizeTLS() */ |
2150 | | /************************************************************************/ |
2151 | | |
2152 | | void CPLFinalizeTLS() |
2153 | 0 | { |
2154 | 0 | CPLCleanupTLS(); |
2155 | | // See #5509 for the explanation why this may be needed. |
2156 | 0 | pthread_key_delete(oTLSKey); |
2157 | 0 | } |
2158 | | |
2159 | | /************************************************************************/ |
2160 | | /* CPLCleanupTLS() */ |
2161 | | /************************************************************************/ |
2162 | | |
2163 | | void CPLCleanupTLS() |
2164 | | |
2165 | 0 | { |
2166 | 0 | void **papTLSList = static_cast<void **>(pthread_getspecific(oTLSKey)); |
2167 | 0 | if (papTLSList == nullptr) |
2168 | 0 | return; |
2169 | | |
2170 | 0 | pthread_setspecific(oTLSKey, nullptr); |
2171 | |
|
2172 | 0 | CPLCleanupTLSList(papTLSList); |
2173 | 0 | } |
2174 | | |
2175 | | /************************************************************************/ |
2176 | | /* CPLCreateSpinLock() */ |
2177 | | /************************************************************************/ |
2178 | | |
2179 | | #if defined(HAVE_PTHREAD_SPIN_LOCK) |
2180 | | #define HAVE_SPINLOCK_IMPL |
2181 | | |
2182 | | struct _CPLSpinLock |
2183 | | { |
2184 | | pthread_spinlock_t spin; |
2185 | | }; |
2186 | | |
2187 | | CPLSpinLock *CPLCreateSpinLock() |
2188 | 0 | { |
2189 | 0 | CPLSpinLock *psSpin = |
2190 | 0 | static_cast<CPLSpinLock *>(malloc(sizeof(CPLSpinLock))); |
2191 | 0 | if (psSpin != nullptr && |
2192 | 0 | pthread_spin_init(&(psSpin->spin), PTHREAD_PROCESS_PRIVATE) == 0) |
2193 | 0 | { |
2194 | 0 | return psSpin; |
2195 | 0 | } |
2196 | 0 | else |
2197 | 0 | { |
2198 | 0 | fprintf(stderr, "CPLCreateSpinLock() failed.\n"); |
2199 | 0 | free(psSpin); |
2200 | 0 | return nullptr; |
2201 | 0 | } |
2202 | 0 | } |
2203 | | |
2204 | | /************************************************************************/ |
2205 | | /* CPLAcquireSpinLock() */ |
2206 | | /************************************************************************/ |
2207 | | |
2208 | | int CPLAcquireSpinLock(CPLSpinLock *psSpin) |
2209 | 0 | { |
2210 | 0 | return pthread_spin_lock(&(psSpin->spin)) == 0; |
2211 | 0 | } |
2212 | | |
2213 | | /************************************************************************/ |
2214 | | /* CPLCreateOrAcquireSpinLockInternal() */ |
2215 | | /************************************************************************/ |
2216 | | |
2217 | | int CPLCreateOrAcquireSpinLockInternal(CPLLock **ppsLock) |
2218 | 0 | { |
2219 | 0 | pthread_mutex_lock(&global_mutex); |
2220 | 0 | if (*ppsLock == nullptr) |
2221 | 0 | { |
2222 | 0 | *ppsLock = static_cast<CPLLock *>(calloc(1, sizeof(CPLLock))); |
2223 | 0 | if (*ppsLock != nullptr) |
2224 | 0 | { |
2225 | 0 | (*ppsLock)->eType = LOCK_SPIN; |
2226 | 0 | (*ppsLock)->u.hSpinLock = CPLCreateSpinLock(); |
2227 | 0 | if ((*ppsLock)->u.hSpinLock == nullptr) |
2228 | 0 | { |
2229 | 0 | free(*ppsLock); |
2230 | 0 | *ppsLock = nullptr; |
2231 | 0 | } |
2232 | 0 | } |
2233 | 0 | } |
2234 | 0 | pthread_mutex_unlock(&global_mutex); |
2235 | | // coverity[missing_unlock] |
2236 | 0 | return (*ppsLock != nullptr && CPLAcquireSpinLock((*ppsLock)->u.hSpinLock)); |
2237 | 0 | } |
2238 | | |
2239 | | /************************************************************************/ |
2240 | | /* CPLReleaseSpinLock() */ |
2241 | | /************************************************************************/ |
2242 | | |
2243 | | void CPLReleaseSpinLock(CPLSpinLock *psSpin) |
2244 | 0 | { |
2245 | 0 | pthread_spin_unlock(&(psSpin->spin)); |
2246 | 0 | } |
2247 | | |
2248 | | /************************************************************************/ |
2249 | | /* CPLDestroySpinLock() */ |
2250 | | /************************************************************************/ |
2251 | | |
2252 | | void CPLDestroySpinLock(CPLSpinLock *psSpin) |
2253 | 0 | { |
2254 | 0 | pthread_spin_destroy(&(psSpin->spin)); |
2255 | 0 | free(psSpin); |
2256 | 0 | } |
2257 | | #endif // HAVE_PTHREAD_SPIN_LOCK |
2258 | | |
2259 | | #endif // def CPL_MULTIPROC_PTHREAD |
2260 | | |
2261 | | /************************************************************************/ |
2262 | | /* CPLGetTLS() */ |
2263 | | /************************************************************************/ |
2264 | | |
2265 | | void *CPLGetTLS(int nIndex) |
2266 | | |
2267 | 22.0k | { |
2268 | 22.0k | void **l_papTLSList = CPLGetTLSList(nullptr); |
2269 | | |
2270 | 22.0k | CPLAssert(nIndex >= 0 && nIndex < CTLS_MAX); |
2271 | | |
2272 | 22.0k | return l_papTLSList[nIndex]; |
2273 | 22.0k | } |
2274 | | |
2275 | | /************************************************************************/ |
2276 | | /* CPLGetTLSEx() */ |
2277 | | /************************************************************************/ |
2278 | | |
2279 | | void *CPLGetTLSEx(int nIndex, int *pbMemoryErrorOccurred) |
2280 | | |
2281 | 205k | { |
2282 | 205k | void **l_papTLSList = CPLGetTLSList(pbMemoryErrorOccurred); |
2283 | 205k | if (l_papTLSList == nullptr) |
2284 | 0 | return nullptr; |
2285 | | |
2286 | 205k | CPLAssert(nIndex >= 0 && nIndex < CTLS_MAX); |
2287 | | |
2288 | 205k | return l_papTLSList[nIndex]; |
2289 | 205k | } |
2290 | | |
2291 | | /************************************************************************/ |
2292 | | /* CPLSetTLS() */ |
2293 | | /************************************************************************/ |
2294 | | |
2295 | | void CPLSetTLS(int nIndex, void *pData, int bFreeOnExit) |
2296 | | |
2297 | 7 | { |
2298 | 7 | CPLSetTLSWithFreeFunc(nIndex, pData, (bFreeOnExit) ? CPLFree : nullptr); |
2299 | 7 | } |
2300 | | |
2301 | | /************************************************************************/ |
2302 | | /* CPLSetTLSWithFreeFunc() */ |
2303 | | /************************************************************************/ |
2304 | | |
2305 | | // Warning: The CPLTLSFreeFunc must not in any case directly or indirectly |
2306 | | // use or fetch any TLS data, or a terminating thread will hang! |
2307 | | void CPLSetTLSWithFreeFunc(int nIndex, void *pData, CPLTLSFreeFunc pfnFree) |
2308 | | |
2309 | 7 | { |
2310 | 7 | void **l_papTLSList = CPLGetTLSList(nullptr); |
2311 | | |
2312 | 7 | CPLAssert(nIndex >= 0 && nIndex < CTLS_MAX); |
2313 | | |
2314 | 7 | l_papTLSList[nIndex] = pData; |
2315 | 7 | l_papTLSList[CTLS_MAX + nIndex] = reinterpret_cast<void *>(pfnFree); |
2316 | 7 | } |
2317 | | |
2318 | | /************************************************************************/ |
2319 | | /* CPLSetTLSWithFreeFuncEx() */ |
2320 | | /************************************************************************/ |
2321 | | |
2322 | | // Warning: the CPLTLSFreeFunc must not in any case directly or indirectly |
2323 | | // use or fetch any TLS data, or a terminating thread will hang! |
2324 | | void CPLSetTLSWithFreeFuncEx(int nIndex, void *pData, CPLTLSFreeFunc pfnFree, |
2325 | | int *pbMemoryErrorOccurred) |
2326 | | |
2327 | 0 | { |
2328 | 0 | void **l_papTLSList = CPLGetTLSList(pbMemoryErrorOccurred); |
2329 | |
|
2330 | 0 | CPLAssert(nIndex >= 0 && nIndex < CTLS_MAX); |
2331 | | |
2332 | 0 | l_papTLSList[nIndex] = pData; |
2333 | 0 | l_papTLSList[CTLS_MAX + nIndex] = reinterpret_cast<void *>(pfnFree); |
2334 | 0 | } |
2335 | | #ifndef HAVE_SPINLOCK_IMPL |
2336 | | |
2337 | | // No spinlock specific API? Fallback to mutex. |
2338 | | |
2339 | | /************************************************************************/ |
2340 | | /* CPLCreateSpinLock() */ |
2341 | | /************************************************************************/ |
2342 | | |
2343 | | CPLSpinLock *CPLCreateSpinLock(void) |
2344 | | { |
2345 | | CPLSpinLock *psSpin = reinterpret_cast<CPLSpinLock *>(CPLCreateMutex()); |
2346 | | if (psSpin) |
2347 | | CPLReleaseSpinLock(psSpin); |
2348 | | return psSpin; |
2349 | | } |
2350 | | |
2351 | | /************************************************************************/ |
2352 | | /* CPLCreateOrAcquireSpinLock() */ |
2353 | | /************************************************************************/ |
2354 | | |
2355 | | int CPLCreateOrAcquireSpinLockInternal(CPLLock **ppsLock) |
2356 | | { |
2357 | | return CPLCreateOrAcquireMutexInternal(ppsLock, 1000, LOCK_ADAPTIVE_MUTEX); |
2358 | | } |
2359 | | |
2360 | | /************************************************************************/ |
2361 | | /* CPLAcquireSpinLock() */ |
2362 | | /************************************************************************/ |
2363 | | |
2364 | | int CPLAcquireSpinLock(CPLSpinLock *psSpin) |
2365 | | { |
2366 | | return CPLAcquireMutex(reinterpret_cast<CPLMutex *>(psSpin), 1000); |
2367 | | } |
2368 | | |
2369 | | /************************************************************************/ |
2370 | | /* CPLReleaseSpinLock() */ |
2371 | | /************************************************************************/ |
2372 | | |
2373 | | void CPLReleaseSpinLock(CPLSpinLock *psSpin) |
2374 | | { |
2375 | | CPLReleaseMutex(reinterpret_cast<CPLMutex *>(psSpin)); |
2376 | | } |
2377 | | |
2378 | | /************************************************************************/ |
2379 | | /* CPLDestroySpinLock() */ |
2380 | | /************************************************************************/ |
2381 | | |
2382 | | void CPLDestroySpinLock(CPLSpinLock *psSpin) |
2383 | | { |
2384 | | CPLDestroyMutex(reinterpret_cast<CPLMutex *>(psSpin)); |
2385 | | } |
2386 | | |
2387 | | #endif // HAVE_SPINLOCK_IMPL |
2388 | | |
2389 | | /************************************************************************/ |
2390 | | /* CPLCreateLock() */ |
2391 | | /************************************************************************/ |
2392 | | |
2393 | | CPLLock *CPLCreateLock(CPLLockType eType) |
2394 | 0 | { |
2395 | 0 | switch (eType) |
2396 | 0 | { |
2397 | 0 | case LOCK_RECURSIVE_MUTEX: |
2398 | 0 | case LOCK_ADAPTIVE_MUTEX: |
2399 | 0 | { |
2400 | 0 | CPLMutex *hMutex = CPLCreateMutexEx(eType == LOCK_RECURSIVE_MUTEX |
2401 | 0 | ? CPL_MUTEX_RECURSIVE |
2402 | 0 | : CPL_MUTEX_ADAPTIVE); |
2403 | 0 | if (!hMutex) |
2404 | 0 | return nullptr; |
2405 | 0 | CPLReleaseMutex(hMutex); |
2406 | 0 | CPLLock *psLock = static_cast<CPLLock *>(malloc(sizeof(CPLLock))); |
2407 | 0 | if (psLock == nullptr) |
2408 | 0 | { |
2409 | 0 | fprintf(stderr, "CPLCreateLock() failed.\n"); |
2410 | 0 | CPLDestroyMutex(hMutex); |
2411 | 0 | return nullptr; |
2412 | 0 | } |
2413 | 0 | psLock->eType = eType; |
2414 | 0 | psLock->u.hMutex = hMutex; |
2415 | 0 | #ifdef DEBUG_CONTENTION |
2416 | 0 | psLock->bDebugPerf = false; |
2417 | 0 | psLock->bDebugPerfAsked = false; |
2418 | 0 | psLock->nCurrentHolders = 0; |
2419 | 0 | psLock->nStartTime = 0; |
2420 | 0 | #endif |
2421 | 0 | return psLock; |
2422 | 0 | } |
2423 | 0 | case LOCK_SPIN: |
2424 | 0 | { |
2425 | 0 | CPLSpinLock *hSpinLock = CPLCreateSpinLock(); |
2426 | 0 | if (!hSpinLock) |
2427 | 0 | return nullptr; |
2428 | 0 | CPLLock *psLock = static_cast<CPLLock *>(malloc(sizeof(CPLLock))); |
2429 | 0 | if (psLock == nullptr) |
2430 | 0 | { |
2431 | 0 | fprintf(stderr, "CPLCreateLock() failed.\n"); |
2432 | 0 | CPLDestroySpinLock(hSpinLock); |
2433 | 0 | return nullptr; |
2434 | 0 | } |
2435 | 0 | psLock->eType = eType; |
2436 | 0 | psLock->u.hSpinLock = hSpinLock; |
2437 | 0 | #ifdef DEBUG_CONTENTION |
2438 | 0 | psLock->bDebugPerf = false; |
2439 | 0 | psLock->bDebugPerfAsked = false; |
2440 | 0 | psLock->nCurrentHolders = 0; |
2441 | 0 | psLock->nStartTime = 0; |
2442 | 0 | #endif |
2443 | 0 | return psLock; |
2444 | 0 | } |
2445 | 0 | default: |
2446 | 0 | CPLAssert(false); |
2447 | 0 | return nullptr; |
2448 | 0 | } |
2449 | 0 | } |
2450 | | |
2451 | | /************************************************************************/ |
2452 | | /* CPLCreateOrAcquireLock() */ |
2453 | | /************************************************************************/ |
2454 | | |
2455 | | int CPLCreateOrAcquireLock(CPLLock **ppsLock, CPLLockType eType) |
2456 | 0 | { |
2457 | 0 | #ifdef DEBUG_CONTENTION |
2458 | 0 | GUIntBig nStartTime = 0; |
2459 | 0 | if ((*ppsLock) && (*ppsLock)->bDebugPerfAsked) |
2460 | 0 | nStartTime = CPLrdtsc(); |
2461 | 0 | #endif |
2462 | 0 | int ret = 0; |
2463 | |
|
2464 | 0 | switch (eType) |
2465 | 0 | { |
2466 | 0 | case LOCK_RECURSIVE_MUTEX: |
2467 | 0 | case LOCK_ADAPTIVE_MUTEX: |
2468 | 0 | { |
2469 | 0 | ret = CPLCreateOrAcquireMutexInternal(ppsLock, 1000, eType); |
2470 | 0 | break; |
2471 | 0 | } |
2472 | 0 | case LOCK_SPIN: |
2473 | 0 | { |
2474 | 0 | ret = CPLCreateOrAcquireSpinLockInternal(ppsLock); |
2475 | 0 | break; |
2476 | 0 | } |
2477 | 0 | default: |
2478 | 0 | CPLAssert(false); |
2479 | 0 | return FALSE; |
2480 | 0 | } |
2481 | 0 | #ifdef DEBUG_CONTENTION |
2482 | 0 | if (ret && (*ppsLock)->bDebugPerfAsked && |
2483 | 0 | CPLAtomicInc(&((*ppsLock)->nCurrentHolders)) == 1) |
2484 | 0 | { |
2485 | 0 | (*ppsLock)->bDebugPerf = true; |
2486 | 0 | (*ppsLock)->nStartTime = nStartTime; |
2487 | 0 | } |
2488 | 0 | #endif |
2489 | 0 | return ret; |
2490 | 0 | } |
2491 | | |
2492 | | /************************************************************************/ |
2493 | | /* CPLAcquireLock() */ |
2494 | | /************************************************************************/ |
2495 | | |
2496 | | int CPLAcquireLock(CPLLock *psLock) |
2497 | 0 | { |
2498 | 0 | #ifdef DEBUG_CONTENTION |
2499 | 0 | GUIntBig nStartTime = 0; |
2500 | 0 | if (psLock->bDebugPerfAsked) |
2501 | 0 | nStartTime = CPLrdtsc(); |
2502 | 0 | #endif |
2503 | 0 | int ret; |
2504 | 0 | if (psLock->eType == LOCK_SPIN) |
2505 | 0 | ret = CPLAcquireSpinLock(psLock->u.hSpinLock); |
2506 | 0 | else |
2507 | 0 | ret = CPLAcquireMutex(psLock->u.hMutex, 1000); |
2508 | 0 | #ifdef DEBUG_CONTENTION |
2509 | 0 | if (ret && psLock->bDebugPerfAsked && |
2510 | 0 | CPLAtomicInc(&(psLock->nCurrentHolders)) == 1) |
2511 | 0 | { |
2512 | 0 | psLock->bDebugPerf = true; |
2513 | 0 | psLock->nStartTime = nStartTime; |
2514 | 0 | } |
2515 | 0 | #endif |
2516 | 0 | return ret; |
2517 | 0 | } |
2518 | | |
2519 | | /************************************************************************/ |
2520 | | /* CPLReleaseLock() */ |
2521 | | /************************************************************************/ |
2522 | | |
2523 | | void CPLReleaseLock(CPLLock *psLock) |
2524 | 0 | { |
2525 | 0 | #ifdef DEBUG_CONTENTION |
2526 | 0 | bool bHitMaxDiff = false; |
2527 | 0 | GIntBig nMaxDiff = 0; |
2528 | 0 | double dfAvgDiff = 0; |
2529 | 0 | if (psLock->bDebugPerf && CPLAtomicDec(&(psLock->nCurrentHolders)) == 0) |
2530 | 0 | { |
2531 | 0 | const GUIntBig nStopTime = CPLrdtscp(); |
2532 | | // coverity[missing_lock:FALSE] |
2533 | 0 | const GIntBig nDiffTime = |
2534 | 0 | static_cast<GIntBig>(nStopTime - psLock->nStartTime); |
2535 | 0 | if (nDiffTime > psLock->nMaxDiff) |
2536 | 0 | { |
2537 | 0 | bHitMaxDiff = true; |
2538 | 0 | psLock->nMaxDiff = nDiffTime; |
2539 | 0 | } |
2540 | 0 | nMaxDiff = psLock->nMaxDiff; |
2541 | 0 | psLock->nIters++; |
2542 | 0 | psLock->dfAvgDiff += (nDiffTime - psLock->dfAvgDiff) / psLock->nIters; |
2543 | 0 | dfAvgDiff = psLock->dfAvgDiff; |
2544 | 0 | } |
2545 | 0 | #endif |
2546 | 0 | if (psLock->eType == LOCK_SPIN) |
2547 | 0 | CPLReleaseSpinLock(psLock->u.hSpinLock); |
2548 | 0 | else |
2549 | 0 | CPLReleaseMutex(psLock->u.hMutex); |
2550 | 0 | #ifdef DEBUG_CONTENTION |
2551 | 0 | if (psLock->bDebugPerf && |
2552 | 0 | (bHitMaxDiff || (psLock->nIters % 1000000) == (1000000 - 1))) |
2553 | 0 | { |
2554 | 0 | CPLDebug("LOCK", "Lock contention : max = " CPL_FRMT_GIB ", avg = %.0f", |
2555 | 0 | nMaxDiff, dfAvgDiff); |
2556 | 0 | } |
2557 | 0 | #endif |
2558 | 0 | } |
2559 | | |
2560 | | /************************************************************************/ |
2561 | | /* CPLDestroyLock() */ |
2562 | | /************************************************************************/ |
2563 | | |
2564 | | void CPLDestroyLock(CPLLock *psLock) |
2565 | 0 | { |
2566 | 0 | if (psLock->eType == LOCK_SPIN) |
2567 | 0 | CPLDestroySpinLock(psLock->u.hSpinLock); |
2568 | 0 | else |
2569 | 0 | CPLDestroyMutex(psLock->u.hMutex); |
2570 | 0 | free(psLock); |
2571 | 0 | } |
2572 | | |
2573 | | /************************************************************************/ |
2574 | | /* CPLLockSetDebugPerf() */ |
2575 | | /************************************************************************/ |
2576 | | |
2577 | | #ifdef DEBUG_CONTENTION |
2578 | | void CPLLockSetDebugPerf(CPLLock *psLock, int bEnableIn) |
2579 | 0 | { |
2580 | 0 | psLock->bDebugPerfAsked = CPL_TO_BOOL(bEnableIn); |
2581 | 0 | } |
2582 | | #else |
2583 | | void CPLLockSetDebugPerf(CPLLock * /* psLock */, int bEnableIn) |
2584 | | { |
2585 | | if (!bEnableIn) |
2586 | | return; |
2587 | | |
2588 | | static bool bOnce = false; |
2589 | | if (!bOnce) |
2590 | | { |
2591 | | bOnce = true; |
2592 | | CPLDebug("LOCK", "DEBUG_CONTENTION not available"); |
2593 | | } |
2594 | | } |
2595 | | #endif |
2596 | | |
2597 | | /************************************************************************/ |
2598 | | /* CPLLockHolder() */ |
2599 | | /************************************************************************/ |
2600 | | |
2601 | | CPLLockHolder::CPLLockHolder(CPLLock **phLock, CPLLockType eType, |
2602 | | const char *pszFileIn, int nLineIn) |
2603 | | |
2604 | 0 | { |
2605 | 0 | #ifndef MUTEX_NONE |
2606 | 0 | pszFile = pszFileIn; |
2607 | 0 | nLine = nLineIn; |
2608 | |
|
2609 | | #ifdef DEBUG_MUTEX |
2610 | | // XXX: There is no way to use CPLDebug() here because it works with |
2611 | | // mutexes itself so we will fall in infinite recursion. Good old |
2612 | | // fprintf() will do the job right. |
2613 | | fprintf(stderr, "CPLLockHolder: Request %p for pid %ld at %d/%s.\n", |
2614 | | *phLock, static_cast<long>(CPLGetPID()), nLine, pszFile); |
2615 | | #endif |
2616 | |
|
2617 | 0 | if (!CPLCreateOrAcquireLock(phLock, eType)) |
2618 | 0 | { |
2619 | 0 | fprintf(stderr, "CPLLockHolder: Failed to acquire lock!\n"); |
2620 | 0 | hLock = nullptr; |
2621 | 0 | } |
2622 | 0 | else |
2623 | 0 | { |
2624 | | #ifdef DEBUG_MUTEX |
2625 | | fprintf(stderr, "CPLLockHolder: Acquired %p for pid %ld at %d/%s.\n", |
2626 | | *phLock, static_cast<long>(CPLGetPID()), nLine, pszFile); |
2627 | | #endif |
2628 | |
|
2629 | 0 | hLock = *phLock; |
2630 | 0 | } |
2631 | 0 | #endif // ndef MUTEX_NONE |
2632 | 0 | } |
2633 | | |
2634 | | /************************************************************************/ |
2635 | | /* CPLLockHolder() */ |
2636 | | /************************************************************************/ |
2637 | | |
2638 | | CPLLockHolder::CPLLockHolder(CPLLock *hLockIn, const char *pszFileIn, |
2639 | | int nLineIn) |
2640 | | |
2641 | 0 | { |
2642 | 0 | #ifndef MUTEX_NONE |
2643 | 0 | pszFile = pszFileIn; |
2644 | 0 | nLine = nLineIn; |
2645 | 0 | hLock = hLockIn; |
2646 | |
|
2647 | 0 | if (hLock != nullptr) |
2648 | 0 | { |
2649 | 0 | if (!CPLAcquireLock(hLock)) |
2650 | 0 | { |
2651 | 0 | fprintf(stderr, "CPLLockHolder: Failed to acquire lock!\n"); |
2652 | 0 | hLock = nullptr; |
2653 | 0 | } |
2654 | 0 | } |
2655 | 0 | #endif // ndef MUTEX_NONE |
2656 | 0 | } |
2657 | | |
2658 | | /************************************************************************/ |
2659 | | /* ~CPLLockHolder() */ |
2660 | | /************************************************************************/ |
2661 | | |
2662 | | CPLLockHolder::~CPLLockHolder() |
2663 | | |
2664 | 0 | { |
2665 | 0 | #ifndef MUTEX_NONE |
2666 | 0 | if (hLock != nullptr) |
2667 | 0 | { |
2668 | | #ifdef DEBUG_MUTEX |
2669 | | fprintf(stderr, "~CPLLockHolder: Release %p for pid %ld at %d/%s.\n", |
2670 | | hLock, static_cast<long>(CPLGetPID()), nLine, pszFile); |
2671 | | #endif |
2672 | 0 | CPLReleaseLock(hLock); |
2673 | 0 | } |
2674 | 0 | #endif // ndef MUTEX_NONE |
2675 | 0 | } |
2676 | | |
2677 | | /************************************************************************/ |
2678 | | /* CPLGetCurrentProcessID() */ |
2679 | | /************************************************************************/ |
2680 | | |
2681 | | #ifdef CPL_MULTIPROC_WIN32 |
2682 | | |
2683 | | int CPLGetCurrentProcessID() |
2684 | | { |
2685 | | return GetCurrentProcessId(); |
2686 | | } |
2687 | | |
2688 | | #else |
2689 | | |
2690 | | #include <sys/types.h> |
2691 | | #include <unistd.h> |
2692 | | |
2693 | | int CPLGetCurrentProcessID() |
2694 | 16.3k | { |
2695 | 16.3k | return getpid(); |
2696 | 16.3k | } |
2697 | | |
2698 | | #endif |