/*----------------------------------------------------------------------------- * readwritelock.cpp - class to deal with mutex locking. * Copyright (C) 2004, 2005 Akito Nozaki * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include NAMESPACE_TANOSHI { ReadWriteLock::ReadWriteLock(const int maxReaders) : d(new ReadWriteLockPrivate()) { d->maxReaders = maxReaders; d->localAccessCounter = 0; pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_rwlock_init(&d->rwlocker, &attr); pthread_rwlockattr_destroy(&attr); // this is used to make this class thread safe. pthread_mutex_init(&d->mutex, NULL); // used to implement max readers. pthread_cond_init(&d->readerWait, NULL); } ReadWriteLock::~ReadWriteLock() { pthread_rwlock_destroy(&d->rwlocker); pthread_mutex_destroy(&d->mutex); pthread_cond_destroy(&d->readerWait); delete d; } void ReadWriteLock::readLock() { for(;;) { if(tryReadLock()) break; pthread_mutex_lock(&d->mutex); if(_checkDeadLock()) { pthread_mutex_unlock(&d->mutex); return; } ++d->waitingReaders; // to prevent dead lock we will wait for 2 sec and try again. struct timeval curtime; struct timespec ts; if(gettimeofday(&curtime, NULL) != 0) { pthread_mutex_unlock(&d->mutex); return; } ts.tv_sec = curtime.tv_sec + 1; ts.tv_nsec = curtime.tv_usec * 1000; // sleep until unlock is called. pthread_cond_wait(&d->readerWait, &d->mutex); --d->waitingReaders; pthread_mutex_unlock(&d->mutex); } } void ReadWriteLock::writeLock() { pthread_mutex_lock(&d->mutex); if(_checkDeadLock()) { pthread_mutex_unlock(&d->mutex); return; } pthread_mutex_unlock(&d->mutex); pthread_rwlock_wrlock(&d->rwlocker); d->currentWriter = pthread_self(); ++d->localAccessCounter; } bool ReadWriteLock::_checkDeadLock() { if((int)d->currentWriter) { if(!pthread_equal(d->currentWriter, pthread_self())) { return false; } else { return true; } } return false; } bool ReadWriteLock::tryReadLock() { bool result = false; pthread_mutex_lock(&d->mutex); if(d->localAccessCounter < d->maxReaders) { if(!pthread_rwlock_tryrdlock(&d->rwlocker)) { ++d->localAccessCounter; result = true; } } pthread_mutex_unlock(&d->mutex); return result; } bool ReadWriteLock::tryWriteLock() { bool result = false; pthread_mutex_lock(&d->mutex); if(!pthread_rwlock_trywrlock(&d->rwlocker)) { d->currentWriter = pthread_self(); ++d->localAccessCounter; result = true; } pthread_mutex_unlock(&d->mutex); return result; } void ReadWriteLock::readWriteUnlock() { if(!d->localAccessCounter) { return; // we should return right? } pthread_mutex_lock(&d->mutex); pthread_rwlock_unlock(&d->rwlocker); d->currentWriter = (pthread_t)0; d->localAccessCounter--; if(d->waitingReaders && d->localAccessCounter < d->maxReaders) { // wakeup all the blocked threads pthread_cond_broadcast(&d->readerWait); } pthread_mutex_unlock(&d->mutex); } }