00001 /** 00002 Licensed to the Apache Software Foundation (ASF) under one 00003 or more contributor license agreements. See the NOTICE file 00004 distributed with this work for additional information 00005 regarding copyright ownership. The ASF licenses this file 00006 to you under the Apache License, Version 2.0 (the 00007 "License"); you may not use this file except in compliance 00008 with the License. You may obtain a copy of the License at 00009 00010 http://www.apache.org/licenses/LICENSE-2.0 00011 00012 Unless required by applicable law or agreed to in writing, software 00013 distributed under the License is distributed on an "AS IS" BASIS, 00014 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 See the License for the specific language governing permissions and 00016 limitations under the License. 00017 */ 00018 00019 /** 00020 * @file Mutex.h 00021 * @brief Contains Mutex related classes for creating a Mutex and locking a Mutex in a specific scope. 00022 */ 00023 00024 #pragma once 00025 #ifndef ATSCPPAPI_MUTEX_H_ 00026 #define ATSCPPAPI_MUTEX_H_ 00027 00028 #include <pthread.h> 00029 #include <atscppapi/noncopyable.h> 00030 #include <atscppapi/shared_ptr.h> 00031 00032 namespace atscppapi { 00033 00034 /** 00035 * @brief A mutex is mutual exclusion: a blocking lock. 00036 * 00037 * The Mutex class uses pthreads for its implmentation. 00038 * 00039 * @see ScopedMutexLock 00040 * @see ScopedMutexTryLock 00041 * @see ScopedSharedMutexLock 00042 * @see ScopedSharedMutexTryLock 00043 */ 00044 class Mutex: noncopyable { 00045 public: 00046 00047 /** 00048 * The available types of Mutexes. 00049 */ 00050 enum Type { 00051 TYPE_NORMAL = 0, /**< This type of Mutex will deadlock if locked by a thread already holding the lock */ 00052 TYPE_RECURSIVE, /**< This type of Mutex will allow a thread holding the lock to lock it again; however, it must be unlocked the same number of times */ 00053 TYPE_ERROR_CHECK /**< This type of Mutex will return errno = EDEADLCK if a thread would deadlock by taking the lock after it already holds it */ 00054 }; 00055 00056 /** 00057 * Create a mutex 00058 * 00059 * @param type The Type of Mutex to create, the default is TYPE_NORMAL. 00060 * @see Type 00061 */ 00062 Mutex(Type type = TYPE_NORMAL) { 00063 pthread_mutexattr_t attr; 00064 pthread_mutexattr_init(&attr); 00065 00066 switch(type) { 00067 case TYPE_RECURSIVE: 00068 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 00069 break; 00070 case TYPE_ERROR_CHECK: 00071 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 00072 break; 00073 case TYPE_NORMAL: 00074 default: 00075 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); 00076 break; 00077 } 00078 00079 pthread_mutex_init(&mutex, &attr); 00080 } 00081 00082 ~Mutex() { 00083 pthread_mutex_destroy(&mutex); 00084 } 00085 00086 /** 00087 * Try to take the lock, this call will NOT block if the mutex cannot be taken. 00088 * @return Returns true if the lock was taken, false if it was not. This call obviously will not block. 00089 */ 00090 bool tryLock() { 00091 return !pthread_mutex_trylock(&mutex); 00092 } 00093 00094 /** 00095 * Block until the lock is taken, when this call returns the thread will be holding the lock. 00096 */ 00097 void lock() { 00098 pthread_mutex_lock(&mutex); 00099 } 00100 00101 /** 00102 * Unlock the lock, this call is nonblocking. 00103 */ 00104 void unlock() { 00105 pthread_mutex_unlock(&mutex); 00106 } 00107 private: 00108 pthread_mutex_t mutex; /**< Internal mutex identifier */ 00109 }; 00110 00111 /** 00112 * @brief Take a Mutex reference and lock inside a scope and unlock when the scope is exited. 00113 * 00114 * This is an RAII implementation which will lock a mutex at the start of the 00115 * scope and unlock it when the scope is exited. 00116 * 00117 * @see Mutex 00118 */ 00119 class ScopedMutexLock: noncopyable { 00120 public: 00121 /** 00122 * Create the scoped mutex lock, once this object is constructed the lock will be held by the thread. 00123 * @param mutex a reference to a Mutex. 00124 */ 00125 explicit ScopedMutexLock(Mutex &mutex) : 00126 mutex_(mutex) { 00127 mutex_.lock(); 00128 } 00129 00130 /** 00131 * Unlock the mutex. 00132 */ 00133 ~ScopedMutexLock() { 00134 mutex_.unlock(); 00135 } 00136 private: 00137 Mutex &mutex_; 00138 }; 00139 00140 /** 00141 * @brief Take a shared_ptr to a Mutex and lock inside a scope and unlock when the scope is exited. 00142 * 00143 * This is an RAII implementation which will lock a mutex at the start of the 00144 * scope and unlock it when the scope is exited. 00145 * 00146 * @see Mutex 00147 */ 00148 class ScopedSharedMutexLock: noncopyable { 00149 public: 00150 /** 00151 * Create the scoped mutex lock, once this object is constructed the lock will be held by the thread. 00152 * @param mutex a shared pointer to a Mutex. 00153 */ 00154 explicit ScopedSharedMutexLock(shared_ptr<Mutex> mutex) : 00155 mutex_(mutex) { 00156 mutex_->lock(); 00157 } 00158 00159 /** 00160 * Unlock the mutex. 00161 */ 00162 ~ScopedSharedMutexLock() { 00163 mutex_->unlock(); 00164 } 00165 private: 00166 shared_ptr<Mutex> mutex_; 00167 }; 00168 00169 /** 00170 * @brief Take a Mutex reference and try to lock inside a scope and unlock when the scope is exited (if the lock was taken). 00171 * 00172 * This is an RAII implementation which will lock a mutex at the start of the 00173 * scope and unlock it when the scope is exited if the lock was taken. 00174 * 00175 * @see Mutex 00176 */ 00177 class ScopedMutexTryLock: noncopyable { 00178 public: 00179 /** 00180 * Try to create the scoped mutex lock, if you should check hasLock() to determine if this object was successfully able to take the lock. 00181 * @param mutex a shared pointer to a Mutex. 00182 */ 00183 explicit ScopedMutexTryLock(Mutex &mutex) : 00184 mutex_(mutex), has_lock_(false) { 00185 has_lock_ = mutex_.tryLock(); 00186 } 00187 00188 /** 00189 * Unlock the mutex (if we hold the lock) 00190 */ 00191 ~ScopedMutexTryLock() { 00192 if (has_lock_) { 00193 mutex_.unlock(); 00194 } 00195 } 00196 00197 /** 00198 * @return True if the lock was taken, False if it was not taken. 00199 */ 00200 bool hasLock() { 00201 return has_lock_; 00202 } 00203 private: 00204 Mutex &mutex_; 00205 bool has_lock_; 00206 }; 00207 00208 /** 00209 * @brief Take a shared_ptr to a Mutex and try to lock inside a scope and unlock when the scope is exited (if the lock was taken). 00210 * 00211 * This is an RAII implementation which will lock a mutex at the start of the 00212 * scope and unlock it when the scope is exited if the lock was taken. 00213 * 00214 * @see Mutex 00215 */ 00216 class ScopedSharedMutexTryLock: noncopyable { 00217 public: 00218 /** 00219 * Try to create the scoped mutex lock, if you should check hasLock() to determine if this object was successfully able to take the lock. 00220 * @param mutex a shared pointer to a Mutex. 00221 */ 00222 explicit ScopedSharedMutexTryLock(shared_ptr<Mutex> mutex) : 00223 mutex_(mutex), has_lock_(false) { 00224 has_lock_ = mutex_->tryLock(); 00225 } 00226 00227 /** 00228 * Unlock the mutex (if we hold the lock) 00229 */ 00230 ~ScopedSharedMutexTryLock() { 00231 if (has_lock_) { 00232 mutex_->unlock(); 00233 } 00234 } 00235 00236 /** 00237 * @return True if the lock was taken, False if it was not taken. 00238 */ 00239 bool hasLock() { 00240 return has_lock_; 00241 } 00242 private: 00243 shared_ptr<Mutex> mutex_; 00244 bool has_lock_; 00245 }; 00246 00247 } /* atscppapi */ 00248 00249 00250 #endif /* ATSCPPAPI_MUTEX_H_ */