RWLocker.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2005 Novell, Inc. All rights reserved.
00004 *
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 *
00008 *  - Redistributions of source code must retain the above copyright notice,
00009 *    this list of conditions and the following disclaimer.
00010 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE 
00023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00032 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/RWLocker.hpp"
00040 #include "blocxx/Assertion.hpp"
00041 #include "blocxx/ThreadImpl.hpp"
00042 #include "blocxx/TimeoutException.hpp"
00043 #include "blocxx/ExceptionIds.hpp"
00044 
00045 namespace BLOCXX_NAMESPACE
00046 {
00047 
00048 BLOCXX_DEFINE_EXCEPTION_WITH_ID(RWLocker);
00050 RWLocker::RWLocker()
00051    : m_waiting_writers()
00052    , m_waiting_readers()
00053    , m_num_waiting_writers(0)
00054    , m_num_waiting_readers(0)
00055    , m_readers_next(0)
00056    , m_guard()
00057    , m_state(0)
00058 {
00059 }
00061 RWLocker::~RWLocker()
00062 {
00063    // ???
00064    //try
00065    //{
00066    // m_cond.notifyAll();
00067    //}
00068    //catch (...)
00069    //{
00070       // don't let exceptions escape
00071    //}
00072 }
00074 void
00075 RWLocker::getReadLock(UInt32 sTimeout, UInt32 usTimeout)
00076 {
00077    NonRecursiveMutexLock l(m_guard);
00078    
00079    // Wait until no exclusive lock is held.
00080    //
00081    // Note:  Scheduling priorities are enforced in the unlock()
00082    //   call.  unlock will wake the proper thread.
00083    // if we will lock, then make sure we won't deadlock
00084    Thread_t tid = ThreadImpl::currentThread();
00085    if (m_state < 0)
00086    {
00087       // see if we already have the write lock
00088       if (ThreadImpl::sameThreads(m_writer, tid))
00089       {
00090          BLOCXX_THROW(DeadlockException, "A thread that has a write lock is trying to acquire a read lock.");
00091       }
00092    }
00093    while (m_state < 0)
00094    {
00095       ++m_num_waiting_readers;
00096       //m_waiting_readers.wait(l);
00097       if (!m_waiting_readers.timedWait(l, sTimeout, usTimeout))
00098       {
00099          --m_num_waiting_readers;
00100          BLOCXX_THROW(TimeoutException, "Timeout while waiting for read lock.");
00101       }
00102       --m_num_waiting_readers;
00103    }
00104    
00105    // Increase the reader count
00106    m_state++;
00107    m_readers.push_back(tid);
00108 }
00110 void
00111 RWLocker::getWriteLock(UInt32 sTimeout, UInt32 usTimeout)
00112 {
00113    NonRecursiveMutexLock l(m_guard);
00114    // Wait until no exclusive lock is held.
00115    //
00116    // Note:  Scheduling priorities are enforced in the unlock()
00117    //   call.  unlock will wake the proper thread.
00118    // if we will lock, then make sure we won't deadlock
00119    Thread_t tid = ThreadImpl::currentThread();
00120    if (m_state != 0)
00121    {
00122       // see if we already have a read lock
00123       for (size_t i = 0; i < m_readers.size(); ++i)
00124       {
00125          if (ThreadImpl::sameThreads(m_readers[i], tid))
00126          {
00127             BLOCXX_THROW(DeadlockException, "A thread that has a read lock is trying to acquire a write lock.");
00128          }
00129       }
00130    }
00131    while (m_state != 0)
00132    {
00133       ++m_num_waiting_writers;
00134       if (!m_waiting_writers.timedWait(l, sTimeout, usTimeout))
00135       {
00136          --m_num_waiting_writers;
00137          BLOCXX_THROW(TimeoutException, "Timeout while waiting for write lock.");
00138       }
00139       --m_num_waiting_writers;
00140    }
00141    m_state = -1;
00142    m_writer = tid;
00143 }
00145 void
00146 RWLocker::releaseReadLock()
00147 {
00148    NonRecursiveMutexLock l(m_guard);
00149    if (m_state > 0)        // Release a reader.
00150       --m_state;
00151    else
00152       BLOCXX_THROW(RWLockerException, "A writer is releasing a read lock");
00153    if (m_state == 0)
00154    {
00155       doWakeups();
00156    }
00157    Thread_t tid = ThreadImpl::currentThread();
00158    size_t i = 0;
00159    while (i < m_readers.size())
00160    {
00161       if (ThreadImpl::sameThreads(m_readers[i], tid))
00162       {
00163          m_readers.remove(i);
00164       }
00165       else
00166       {
00167          ++i;
00168       }
00169    }
00170 }
00172 void
00173 RWLocker::releaseWriteLock()
00174 {
00175    NonRecursiveMutexLock l(m_guard);
00176    if (m_state == -1)
00177    {
00178       m_state = 0;
00179    }
00180    else
00181    {
00182       BLOCXX_THROW(RWLockerException, "A reader is releasing a write lock");
00183    }
00184    // After a writer is unlocked, we are always back in the unlocked state.
00185    //
00186    doWakeups();
00187 }
00189 void
00190 RWLocker::doWakeups()
00191 {
00192    if ( m_num_waiting_writers > 0 && 
00193       m_num_waiting_readers > 0)
00194    {
00195       if (m_readers_next == 1)
00196       {
00197          m_readers_next = 0;
00198          m_waiting_readers.notifyAll();
00199       }
00200       else
00201       {
00202          m_waiting_writers.notifyOne();
00203          m_readers_next = 1;
00204       }
00205    }
00206    else if (m_num_waiting_writers > 0)
00207    {
00208       // Only writers - scheduling doesn't matter
00209       m_waiting_writers.notifyOne();
00210    }
00211    else if (m_num_waiting_readers > 0)
00212    {
00213       // Only readers - scheduling doesn't matter
00214       m_waiting_readers.notifyAll();
00215    }
00216 }
00217 
00218 } // end namespace BLOCXX_NAMESPACE
00219 

Generated on Fri Jun 16 15:39:08 2006 for blocxx by  doxygen 1.4.6