1 /* 2 * libjingle 3 * Copyright 2004, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #ifndef TALK_BASE_CRITICALSECTION_H__ 29 #define TALK_BASE_CRITICALSECTION_H__ 30 31 #include "talk/base/constructormagic.h" 32 33 #ifdef WIN32 34 #include "talk/base/win32.h" 35 #endif 36 37 #ifdef POSIX 38 #include <pthread.h> 39 #endif 40 41 #ifdef _DEBUG 42 #define CS_TRACK_OWNER 1 43 #endif // _DEBUG 44 45 #if CS_TRACK_OWNER 46 #define TRACK_OWNER(x) x 47 #else // !CS_TRACK_OWNER 48 #define TRACK_OWNER(x) 49 #endif // !CS_TRACK_OWNER 50 51 namespace talk_base { 52 53 #ifdef WIN32 54 class CriticalSection { 55 public: 56 CriticalSection() { 57 InitializeCriticalSection(&crit_); 58 // Windows docs say 0 is not a valid thread id 59 TRACK_OWNER(thread_ = 0); 60 } 61 ~CriticalSection() { 62 DeleteCriticalSection(&crit_); 63 } 64 void Enter() { 65 EnterCriticalSection(&crit_); 66 TRACK_OWNER(thread_ = GetCurrentThreadId()); 67 } 68 bool TryEnter() { 69 if (TryEnterCriticalSection(&crit_) != FALSE) { 70 TRACK_OWNER(thread_ = GetCurrentThreadId()); 71 return true; 72 } 73 return false; 74 } 75 void Leave() { 76 TRACK_OWNER(thread_ = 0); 77 LeaveCriticalSection(&crit_); 78 } 79 80 #if CS_TRACK_OWNER 81 bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); } 82 #endif // CS_TRACK_OWNER 83 84 private: 85 CRITICAL_SECTION crit_; 86 TRACK_OWNER(DWORD thread_); // The section's owning thread id 87 }; 88 #endif // WIN32 89 90 #ifdef POSIX 91 class CriticalSection { 92 public: 93 CriticalSection() { 94 pthread_mutexattr_t mutex_attribute; 95 pthread_mutexattr_init(&mutex_attribute); 96 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); 97 pthread_mutex_init(&mutex_, &mutex_attribute); 98 pthread_mutexattr_destroy(&mutex_attribute); 99 TRACK_OWNER(thread_ = 0); 100 } 101 ~CriticalSection() { 102 pthread_mutex_destroy(&mutex_); 103 } 104 void Enter() { 105 pthread_mutex_lock(&mutex_); 106 TRACK_OWNER(thread_ = pthread_self()); 107 } 108 bool TryEnter() { 109 if (pthread_mutex_trylock(&mutex_) == 0) { 110 TRACK_OWNER(thread_ = pthread_self()); 111 return true; 112 } 113 return false; 114 } 115 void Leave() { 116 TRACK_OWNER(thread_ = 0); 117 pthread_mutex_unlock(&mutex_); 118 } 119 120 #if CS_TRACK_OWNER 121 bool CurrentThreadIsOwner() const { return pthread_equal(thread_, pthread_self()); } 122 #endif // CS_TRACK_OWNER 123 124 private: 125 pthread_mutex_t mutex_; 126 TRACK_OWNER(pthread_t thread_); 127 }; 128 #endif // POSIX 129 130 // CritScope, for serializing execution through a scope. 131 class CritScope { 132 public: 133 explicit CritScope(CriticalSection *pcrit) { 134 pcrit_ = pcrit; 135 pcrit_->Enter(); 136 } 137 ~CritScope() { 138 pcrit_->Leave(); 139 } 140 private: 141 CriticalSection *pcrit_; 142 DISALLOW_COPY_AND_ASSIGN(CritScope); 143 }; 144 145 // Tries to lock a critical section on construction via 146 // CriticalSection::TryEnter, and unlocks on destruction if the 147 // lock was taken. Never blocks. 148 // 149 // IMPORTANT: Unlike CritScope, the lock may not be owned by this thread in 150 // subsequent code. Users *must* check locked() to determine if the 151 // lock was taken. If you're not calling locked(), you're doing it wrong! 152 class TryCritScope { 153 public: 154 explicit TryCritScope(CriticalSection *pcrit) { 155 pcrit_ = pcrit; 156 locked_ = pcrit_->TryEnter(); 157 } 158 ~TryCritScope() { 159 if (locked_) { 160 pcrit_->Leave(); 161 } 162 } 163 bool locked() const { 164 return locked_; 165 } 166 private: 167 CriticalSection *pcrit_; 168 bool locked_; 169 DISALLOW_COPY_AND_ASSIGN(TryCritScope); 170 }; 171 172 // TODO: Move this to atomicops.h, which can't be done easily because of 173 // complex compile rules. 174 class AtomicOps { 175 public: 176 #ifdef WIN32 177 // Assumes sizeof(int) == sizeof(LONG), which it is on Win32 and Win64. 178 static int Increment(int* i) { 179 return ::InterlockedIncrement(reinterpret_cast<LONG*>(i)); 180 } 181 static int Decrement(int* i) { 182 return ::InterlockedDecrement(reinterpret_cast<LONG*>(i)); 183 } 184 #else 185 static int Increment(int* i) { 186 return __sync_add_and_fetch(i, 1); 187 } 188 static int Decrement(int* i) { 189 return __sync_sub_and_fetch(i, 1); 190 } 191 #endif 192 }; 193 194 } // namespace talk_base 195 196 #endif // TALK_BASE_CRITICALSECTION_H__ 197