/* Copyright (C) 2014 InfiniDB, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /***************************************************************************** * $Id$ * ****************************************************************************/ /* * Brief description of the file contents * * More detailed description */ #include using namespace std; #include #include using namespace boost; #define RWLOCK_LOCAL_DLLEXPORT #include "rwlock_local.h" #undef RWLOCK_LOCAL_DLLEXPORT // semaphore numbers #define MUTEX 0 #define READERS 1 #define WRITERS 2 #ifdef DEBUG using namespace std; #define PRINTSTATE() \ cerr << " reading = " << state.reading << endl \ << " writing = " << state.writing << endl \ << " readerswaiting = " << state.readerswaiting << endl \ << " writerswaiting = " << state.writerswaiting << endl; #define CHECKSAFETY() \ if (!((state.reading == 0 && (state.writing == 0 || state.writing == 1)) || \ (state.reading > 0 && state.writing == 0))) \ { \ cerr << "RWLock_local::" << __func__ << ": safety invariant violation" << endl; \ PRINTSTATE(); \ throw std::logic_error("RWLock_local: safety invariant violation"); \ } #define CHECKLIVENESS() \ if (!((!(state.readerswaiting > 0 || state.writerswaiting > 0) || \ (state.reading > 0 || state.writing > 0)) || \ (!(state.reading == 0 && state.writing == 0) || \ (state.readerswaiting == 0 && state.writerswaiting == 0)))) \ { \ cerr << "RWLock_local::" << __func__ << ": liveness invariant violation" << endl; \ PRINTSTATE(); \ throw std::logic_error("RWLock_local: liveness invariant violation"); \ } #undef CHECKLIVENESS #define CHECKLIVENESS() #endif namespace rwlock { RWLock_local::RWLock_local() { state.reading = 0; state.readerswaiting = 0; state.writing = 0; state.writerswaiting = 0; } RWLock_local::~RWLock_local() { } void RWLock_local::read_lock() { mutex.lock(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif if (state.writerswaiting > 0 || state.writing > 0) { state.readerswaiting++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif while (state.writerswaiting > 0 || state.writing > 0) okToRead.wait(mutex); state.readerswaiting--; } state.reading++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif mutex.unlock(); } void RWLock_local::read_unlock() { mutex.lock(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif state.reading--; if (state.writerswaiting > 0 && state.reading == 0) okToWrite.notify_one(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif mutex.unlock(); } void RWLock_local::write_lock() { mutex.lock(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif if (state.writing > 0 || state.reading > 0) { state.writerswaiting++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif while (state.writing > 0 || state.reading > 0) okToWrite.wait(mutex); state.writerswaiting--; } state.writing++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif } void RWLock_local::write_unlock() { #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif state.writing--; if (state.writerswaiting > 0) okToWrite.notify_one(); else if (state.readerswaiting > 0) okToRead.notify_all(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif mutex.unlock(); } void RWLock_local::upgrade_to_write() { mutex.lock(); #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif state.reading--; // try to cut in line if (state.reading == 0) { state.writing++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif return; } // cut & paste from write_lock() if (state.writing > 0 || state.reading > 0) { state.writerswaiting++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif while (state.writing > 0 || state.reading > 0) okToWrite.wait(mutex); state.writerswaiting--; } state.writing++; } /* It's safe (and necessary) to simply convert this writer to a reader without blocking */ void RWLock_local::downgrade_to_read() { #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif state.writing--; if (state.readerswaiting > 0) okToRead.notify_all(); state.reading++; #ifdef DEBUG CHECKSAFETY(); CHECKLIVENESS(); #endif mutex.unlock(); } void RWLock_local::lock() { mutex.lock(); } void RWLock_local::unlock() { mutex.unlock(); } int RWLock_local::getWriting() { return state.writing; } int RWLock_local::getReading() { return state.reading; } int RWLock_local::getWritersWaiting() { return state.writerswaiting; } int RWLock_local::getReadersWaiting() { return state.readerswaiting; } ScopedRWLock_local::ScopedRWLock_local(RWLock_local* l, rwlock_mode m) { thelock = l; mode = m; assert(m == R || m == W); locked = false; lock(); } ScopedRWLock_local::~ScopedRWLock_local() { if (locked) unlock(); } void ScopedRWLock_local::lock() { if (mode == R) thelock->read_lock(); else thelock->write_lock(); locked = true; } void ScopedRWLock_local::unlock() { if (mode == R) thelock->read_unlock(); else thelock->write_unlock(); locked = false; } } // namespace rwlock