1 /* Copyright (C) 2014 InfiniDB, Inc. 2 3 This program is free software; you can redistribute it and/or 4 modify it under the terms of the GNU General Public License 5 as published by the Free Software Foundation; version 2 of 6 the License. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License 14 along with this program; if not, write to the Free Software 15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 16 MA 02110-1301, USA. */ 17 18 /****************************************************************************** 19 * $Id$ 20 * 21 *****************************************************************************/ 22 23 /** @file 24 * class RWLock interface 25 */ 26 27 #ifndef RWLOCK_H_ 28 #define RWLOCK_H_ 29 30 #include <unistd.h> 31 #include <stdexcept> 32 33 #include <boost/interprocess/shared_memory_object.hpp> 34 #include <boost/interprocess/mapped_region.hpp> 35 #include <boost/interprocess/sync/interprocess_semaphore.hpp> 36 #include <boost/date_time/posix_time/posix_time.hpp> 37 38 #if defined(_MSC_VER) && defined(xxxRWLOCK_DLLEXPORT) 39 #define EXPORT __declspec(dllexport) 40 #else 41 #define EXPORT 42 #endif 43 44 namespace rwlock 45 { 46 47 /// the layout of the shmseg 48 struct State 49 { 50 #ifdef _MSC_VER 51 volatile LONG writerswaiting; 52 volatile LONG writing; 53 volatile LONG readerswaiting; 54 volatile LONG reading; 55 #else 56 volatile int writerswaiting; 57 volatile int writing; 58 volatile int readerswaiting; 59 volatile int reading; 60 #endif 61 boost::interprocess::interprocess_semaphore sems[3]; 62 }; 63 64 /* the lock state without the semaphores, passed out by timed_write_lock() for 65 class RWLockMonitor 66 */ 67 struct LockState 68 { 69 #ifdef _MSC_VER 70 LONG writerswaiting; 71 LONG writing; 72 LONG readerswaiting; 73 LONG reading; 74 bool mutexLocked; 75 #else 76 int writerswaiting; 77 int writing; 78 int readerswaiting; 79 int reading; 80 bool mutexLocked; 81 #endif 82 }; 83 84 class RWLockShmImpl 85 { 86 public: 87 static RWLockShmImpl* makeRWLockShmImpl(int key, bool* excl = 0); 88 89 boost::interprocess::shared_memory_object fStateShm; 90 boost::interprocess::mapped_region fRegion; 91 State* fState; 92 keyString()93 std::string keyString() 94 { 95 return fKeyString; 96 } 97 private: 98 explicit RWLockShmImpl(int key, bool excl = false); 99 ~RWLockShmImpl(); 100 RWLockShmImpl(const RWLockShmImpl& rhs); 101 RWLockShmImpl& operator=(const RWLockShmImpl& rhs); 102 std::string fKeyString; 103 }; 104 105 class not_excl : public std::exception 106 { 107 public: what()108 virtual const char* what() const throw() 109 { 110 return "not_excl"; 111 } 112 }; 113 114 class wouldblock : public std::exception 115 { 116 public: what()117 virtual const char* what() const throw() 118 { 119 return "wouldblock"; 120 } 121 }; 122 123 /** @brief Implements RW locks for use across threads & processes 124 * 125 * Implements RW locks for use across threads & processes. Every 126 * instance that shares a lock must be instantiated using the same 127 * key. There is 'no limit' on the number of RW locks that can 128 * exist on the system at any one time. 129 * 130 * Summary of operation: 131 * - readers can work concurrently 132 * - writers get exclusive access 133 * - writers have priority 134 * - all state persists across all invocations sharing a given key 135 * 136 * Note: because state has to persist, it will have to be cleaned 137 * up somewhere else. Crashes while holding a read or write lock will 138 * eventually deadlock the set of processes that share the same key obviously. 139 */ 140 class RWLock 141 { 142 public: 143 144 // semaphore numbers 145 static const int MUTEX = 0; 146 static const int READERS = 1; 147 static const int WRITERS = 2; 148 149 /** @brief Keyed constructor. 150 * 151 * Instantiate an RWLock with the given key. All instances that 152 * share a key share the same lock. 153 * 154 * @param key The key 155 * @param excl If true and this is the first instance with the 156 * supplied key, it will return holding the write lock. If true and 157 * this is not the first instance, it will throw not_excl. The intent 158 * is similar to the IPC_EXCL flag in the sem/shm implementations. 159 */ 160 EXPORT explicit RWLock(int key, bool* excl = 0); 161 162 EXPORT ~RWLock(); 163 164 /** @brief Grab a read lock 165 * 166 * Grab a read lock. This will block iff writers are waiting or 167 * a writer is active. The version with priority ignores any 168 * waiting threads and grabs the lock. 169 * 170 * @param block (For testing only) If false, will throw 171 * wouldblock instead of blocking 172 */ 173 EXPORT void read_lock(bool block = true); 174 175 EXPORT void read_lock_priority(bool block = true); 176 177 /** @brief Release a read lock. 178 * 179 * Release a read lock. 180 */ 181 EXPORT void read_unlock(); 182 183 /** @brief Grab a write lock 184 * 185 * Grab a write lock. This will block while another writer or reader is 186 * active and will have exclusive access on waking. 187 * 188 * @param block (For testing only) If false, will throw 189 * wouldblock instead of blocking 190 */ 191 EXPORT void write_lock(bool block = true); 192 193 /** @brief A timed write lock. 194 * 195 * Queues up for the write lock for a specified amount of time. Returns 196 * true if it got the lock, return false if it timed out first. 197 * If the timeout happens, it will also return the lock state if passed 198 * a non-NULL LockState struct. This is a specialization for supporting 199 * the RWLockMonitor class. 200 */ 201 EXPORT bool timed_write_lock(const struct timespec& ts, 202 struct LockState* state = 0); 203 204 /** @brief Release a write lock. 205 * 206 * Release a write lock. 207 */ 208 EXPORT void write_unlock(); 209 210 /* note: these haven't been proven yet */ 211 212 /** @brief Upgrade a read lock to a write lock 213 * 214 * Upgrade a read lock to a write lock. It may have to block 215 * if there are other readers currently reading. No guarantees of atomicity. 216 */ 217 EXPORT void upgrade_to_write(); 218 219 /** @brief Downgrade a write lock to a read lock 220 * 221 * Downgrade a write lock to a read lock. The conversion happens 222 * atomically. 223 */ 224 EXPORT void downgrade_to_read(); 225 226 /** @brief Reset the lock's state (Use with caution!) 227 * 228 * If the lock gets into a bad state in testing or something, 229 * this will reset the state. 230 * @warning This is safe only if there are no other threads using this 231 * lock. 232 */ 233 EXPORT void reset(); 234 235 /* These are for white box testing only */ lock()236 inline void lock() 237 { 238 down(MUTEX, true); 239 } unlock()240 inline void unlock() 241 { 242 up(MUTEX); 243 } getWriting()244 inline int getWriting() const 245 { 246 return fPImpl->fState->writing; 247 } getReading()248 inline int getReading() const 249 { 250 return fPImpl->fState->reading; 251 } getWritersWaiting()252 inline int getWritersWaiting() const 253 { 254 return fPImpl->fState->writerswaiting; 255 } getReadersWaiting()256 inline int getReadersWaiting() const 257 { 258 return fPImpl->fState->readerswaiting; 259 } 260 LockState getLockState(); 261 262 private: 263 RWLock(const RWLock& rwl); 264 RWLock& operator=(const RWLock& rwl); 265 getSemval(int)266 inline int getSemval(int) const 267 { 268 return 0; 269 } 270 void down(int num, bool block = true); 271 bool timed_down(int num, const boost::posix_time::ptime& ts); // to support timed_write_lock() 272 void up(int num); 273 274 RWLockShmImpl* fPImpl; 275 }; 276 277 } //namespace rwlock 278 279 #undef EXPORT 280 281 #endif 282 // vim:ts=4 sw=4: 283