00001 /*========================================================================== 00002 * Copyright (c) 2004 University of Massachusetts. All Rights Reserved. 00003 * 00004 * Use of the Lemur Toolkit for Language Modeling and Information Retrieval 00005 * is subject to the terms of the software license set forth in the LICENSE 00006 * file included with this software, and also available at 00007 * http://www.lemurproject.org/license.html 00008 * 00009 *========================================================================== 00010 */ 00011 00012 // 00013 // ReadersWritersLock 00014 // 00015 // 14 December 2004 -- tds 00016 // 00017 00018 #include "indri/ScopedMonitor.hpp" 00019 #include "indri/ConditionVariable.hpp" 00020 00021 #ifndef INDRI_READERSWRITERSLOCK_HPP 00022 #define INDRI_READERSWRITERSLOCK_HPP 00023 namespace indri 00024 { 00025 namespace thread 00026 { 00027 00028 class ReadersWritersLock { 00029 private: 00030 struct wait_queue_entry { 00031 bool writing; 00032 bool awakened; 00033 wait_queue_entry* next; 00034 ConditionVariable wakeup; 00035 }; 00036 00037 Mutex _mutex; 00038 wait_queue_entry* _head; 00039 wait_queue_entry* _tail; 00040 00041 int _readers; 00042 int _writers; 00043 00044 void _enqueue( wait_queue_entry& entry ) { 00045 entry.awakened = false; 00046 00047 // called within the mutex 00048 if( _tail == 0 ) { 00049 entry.next = 0; 00050 _tail = &entry; 00051 _head = &entry; 00052 } else { 00053 entry.next = 0; 00054 _tail->next = &entry; 00055 _tail = &entry; 00056 } 00057 } 00058 00059 void _wakeOthers() { 00060 bool reading = false; 00061 00062 if( _head ) { 00063 // wakeup the next thread, no matter what 00064 _head->awakened = true; 00065 _head->wakeup.notifyOne(); 00066 reading = !_head->writing; 00067 _head = _head->next; 00068 00069 // if the next waiter wants to read, then we should 00070 // continue to wake threads up until there are no readers left 00071 if( reading ) { 00072 while( _head && _head->writing == false ) { 00073 _head->awakened = true; 00074 _head->wakeup.notifyOne(); 00075 _head = _head->next; 00076 } 00077 } 00078 } 00079 00080 // if we took the last thing off the queue, fix up the tail 00081 if( _head == 0 ) { 00082 _tail = 0; 00083 } 00084 } 00085 00086 public: 00087 ReadersWritersLock() : 00088 _tail(0), 00089 _head(0), 00090 _readers(0), 00091 _writers(0) 00092 { 00093 } 00094 00095 void lockRead() { 00096 _mutex.lock(); 00097 00098 if( _head != 0 || _writers ) { 00099 do { 00100 wait_queue_entry entry; 00101 00102 entry.writing = false; 00103 entry.next = 0; 00104 00105 _enqueue( entry ); 00106 00107 // wait for our time to come 00108 entry.wakeup.wait( _mutex ); 00109 } 00110 while( _writers ); 00111 } 00112 _readers++; 00113 assert( !_writers ); 00114 00115 _mutex.unlock(); 00116 } 00117 00118 void lockWrite() { 00119 _mutex.lock(); 00120 00121 if( _head != 0 || _readers || _writers ) { 00122 do { 00123 wait_queue_entry entry; 00124 00125 entry.writing = true; 00126 entry.next = 0; 00127 00128 _enqueue( entry ); 00129 00130 // wait for our time to come 00131 entry.wakeup.wait( _mutex ); 00132 } 00133 while( _readers || _writers ); 00134 } 00135 00136 assert( _writers == 0 ); 00137 _writers++; 00138 _mutex.unlock(); 00139 00140 assert( !_readers && _writers == 1 ); 00141 } 00142 00143 void unlockWrite() { 00144 assert( _writers ); 00145 assert( !_readers ); 00146 00147 _mutex.lock(); 00148 _writers = 0; 00149 _wakeOthers(); 00150 _mutex.unlock(); 00151 } 00152 00153 void unlockRead() { 00154 assert( _readers ); 00155 assert( !_writers ); 00156 00157 _mutex.lock(); 00158 _readers--; 00159 00160 if( _readers == 0 ) 00161 _wakeOthers(); 00162 00163 _mutex.unlock(); 00164 } 00165 00166 void yieldWrite() { 00167 assert( !_readers && _writers == 1 ); 00168 00169 if( _head ) { 00170 unlockWrite(); 00171 lockWrite(); 00172 } 00173 00174 assert( !_readers && _writers == 1 ); 00175 } 00176 00177 void yieldRead() { 00178 assert( _readers && _writers == 0 ); 00179 00180 if( _head ) { 00181 unlockRead(); 00182 lockRead(); 00183 } 00184 00185 assert( _readers && _writers == 0 ); 00186 } 00187 00188 }; 00189 } 00190 } 00191 00192 #endif // INDRI_READERSWRITERSLOCK_HPP 00193