Chromium Code Reviews| 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 |