OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2013 Google Inc. | 2 * Copyright 2013 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #ifndef SkMutex_pthread_DEFINED | 8 #ifndef SkMutex_pthread_DEFINED |
9 #define SkMutex_pthread_DEFINED | 9 #define SkMutex_pthread_DEFINED |
10 | 10 |
11 /** Posix pthread_mutex based mutex. */ | 11 /** Posix pthread_mutex based mutex. */ |
12 | 12 |
13 #include "SkTypes.h" | |
13 #include <errno.h> | 14 #include <errno.h> |
14 #include <pthread.h> | 15 #include <pthread.h> |
15 | 16 |
17 // We use error-checking mutexes in Debug builds or normal fast mutexes in Relea se builds. | |
18 // Debug builds get these checks for free: | |
19 // - a double acquire() from the same thread fails immediately instead of dead locking; | |
20 // - release() checks that the mutex is being unlocked by its owner thread. | |
21 // I don't see a built-in way to implement assertHeld(), so we track that with a n fOwner field. | |
22 | |
16 // This isn't technically portable, but on Linux and Android pthread_t is some s ort of int, and | 23 // This isn't technically portable, but on Linux and Android pthread_t is some s ort of int, and |
17 // on Darwin it's a pointer. So assuming pthread_self() never returns 0, it wor ks as a sentinel. | 24 // on Darwin it's a pointer. So assuming pthread_self() never returns 0, it wor ks as a sentinel. |
18 SkDEBUGCODE(static const pthread_t kNoOwner = 0;) | 25 SkDEBUGCODE(static const pthread_t kNoOwner = 0;) |
19 | 26 |
20 // A SkBaseMutex is a POD structure that can be directly initialized | 27 // An SkBaseMutex is a POD structure that can be directly initialized at declara tion time with |
21 // at declaration time with SK_DECLARE_STATIC/GLOBAL_MUTEX. This avoids the | 28 // SK_DECLARE_STATIC_MUTEX. This avoids the generation of a static initializer i n the final |
22 // generation of a static initializer in the final machine code (and | 29 // machine code (and a corresponding static finalizer). |
23 // a corresponding static finalizer). | |
24 struct SkBaseMutex { | 30 struct SkBaseMutex { |
25 void acquire() { | 31 void acquire() { |
26 SkASSERT(0 == pthread_equal(fOwner, pthread_self())); // SkMutex is not re-entrant | 32 SkDEBUGCODE(int rc = ) pthread_mutex_lock(&fMutex); |
27 pthread_mutex_lock(&fMutex); | 33 SkASSERT(0 == rc); |
Jeffrey Yasskin
2015/01/09 16:31:55
It would be nice for debugging if there were some
mtklein
2015/01/09 16:39:25
Agreed, though in practice these rarely trigger.
| |
28 SkDEBUGCODE(fOwner = pthread_self();) | 34 SkDEBUGCODE(fOwner = pthread_self();) |
29 } | 35 } |
30 void release() { | 36 void release() { |
31 this->assertHeld(); | 37 this->assertHeld(); // Usually redundant, but not for static mutexes on Macs (see below). |
32 SkDEBUGCODE(fOwner = kNoOwner;) | 38 SkDEBUGCODE(fOwner = kNoOwner;) |
33 pthread_mutex_unlock(&fMutex); | 39 SkDEBUGCODE(int rc = ) pthread_mutex_unlock(&fMutex); |
40 SkASSERT(0 == rc); | |
34 } | 41 } |
35 void assertHeld() { | 42 void assertHeld() { |
36 SkASSERT(0 != pthread_equal(fOwner, pthread_self())); | 43 SkASSERT(0 != pthread_equal(fOwner, pthread_self())); |
37 } | 44 } |
38 | 45 |
39 pthread_mutex_t fMutex; | 46 pthread_mutex_t fMutex; |
40 SkDEBUGCODE(pthread_t fOwner;) | 47 SkDEBUGCODE(pthread_t fOwner;) // Read and write only when holding fMutex. |
41 }; | 48 }; |
42 | 49 |
43 // A normal mutex that requires to be initialized through normal C++ constructio n, | 50 // A normal mutex that's required to be initialized through normal C++ construct ion, |
44 // i.e. when it's a member of another class, or allocated on the heap. | 51 // i.e. when it's a member of another class, or allocated on the heap. |
45 class SkMutex : public SkBaseMutex { | 52 class SkMutex : public SkBaseMutex { |
46 public: | 53 public: |
47 SkMutex() { | 54 SkMutex() { |
48 SkDEBUGCODE(int status = )pthread_mutex_init(&fMutex, NULL); | 55 #ifdef SK_DEBUG |
49 SkDEBUGCODE( | 56 pthread_mutexattr_t attr; |
50 if (status != 0) { | 57 SkASSERT(0 == pthread_mutexattr_init(&attr)); |
51 print_pthread_error(status); | 58 SkASSERT(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK) ); |
52 SkASSERT(0 == status); | 59 SkASSERT(0 == pthread_mutex_init(&fMutex, &attr)); |
53 } | 60 SkASSERT(0 == pthread_mutexattr_destroy(&attr)); |
54 fOwner = kNoOwner; | 61 fOwner = kNoOwner; |
55 ) | 62 #else |
63 (void)pthread_mutex_init(&fMutex, NULL); | |
64 #endif | |
56 } | 65 } |
57 | 66 |
58 ~SkMutex() { | 67 ~SkMutex() { |
59 SkDEBUGCODE(int status = )pthread_mutex_destroy(&fMutex); | 68 SkDEBUGCODE(int rc = )pthread_mutex_destroy(&fMutex); |
60 SkDEBUGCODE( | 69 SkASSERT(0 == rc); |
61 if (status != 0) { | |
62 print_pthread_error(status); | |
63 SkASSERT(0 == status); | |
64 } | |
65 ) | |
66 } | 70 } |
67 | 71 |
68 private: | 72 private: |
69 SkMutex(const SkMutex&); | 73 SkMutex(const SkMutex&); |
70 SkMutex& operator=(const SkMutex&); | 74 SkMutex& operator=(const SkMutex&); |
71 | |
72 static void print_pthread_error(int status) { | |
73 switch (status) { | |
74 case 0: // success | |
75 break; | |
76 case EINVAL: | |
77 SkDebugf("pthread error [%d] EINVAL\n", status); | |
78 break; | |
79 case EBUSY: | |
80 SkDebugf("pthread error [%d] EBUSY\n", status); | |
81 break; | |
82 default: | |
83 SkDebugf("pthread error [%d] unknown\n", status); | |
84 break; | |
85 } | |
86 } | |
87 }; | 75 }; |
88 | 76 |
89 #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, SkDEBUGCODE(0) } | 77 #if defined(SK_DEBUG) && defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER) |
Jeffrey Yasskin
2015/01/09 16:31:55
Very nice. I had no idea PTHREAD_ERRORCHECK_MUTEX_
mtklein
2015/01/09 16:39:25
Yeah, me neither. I accidentally stumbled upon it
| |
78 // When possible we want to use error-check mutexes in Debug builds. See th e note at the top. | |
79 #define SK_BASE_MUTEX_INIT { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER, kNoOwner } | |
80 #elif defined(SK_DEBUG) | |
81 // Macs don't support PTHREAD_ERRORCHECK_MUTEX_INITIALIZER when targeting <1 0.7. We target 10.6. | |
82 #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER, kNoOwner } | |
83 #else | |
84 #define SK_BASE_MUTEX_INIT { PTHREAD_MUTEX_INITIALIZER } | |
85 #endif | |
90 | 86 |
91 // Using POD-style initialization prevents the generation of a static initialize r. | 87 // Using POD-style initialization prevents the generation of a static initialize r. |
92 // | 88 // |
93 // Without magic statics there are no thread safety guarantees on initialization | 89 // Without magic statics there are no thread safety guarantees on initialization |
94 // of local statics (even POD). As a result, it is illegal to use | 90 // of local statics (even POD). As a result, it is illegal to use |
95 // SK_DECLARE_STATIC_MUTEX in a function. | 91 // SK_DECLARE_STATIC_MUTEX in a function. |
96 // | 92 // |
97 // Because SkBaseMutex is not a primitive, a static SkBaseMutex cannot be | 93 // Because SkBaseMutex is not a primitive, a static SkBaseMutex cannot be |
98 // initialized in a class with this macro. | 94 // initialized in a class with this macro. |
99 #define SK_DECLARE_STATIC_MUTEX(name) namespace {} static SkBaseMutex name = SK_ BASE_MUTEX_INIT | 95 #define SK_DECLARE_STATIC_MUTEX(name) namespace {} static SkBaseMutex name = SK_ BASE_MUTEX_INIT |
100 | 96 |
101 #endif | 97 #endif |
OLD | NEW |