MicoleBusWriteLock.cpp

Go to the documentation of this file.
00001 /*
00002  * This file is part of Micole Architecture
00003  *
00004  * Copyright (C) 2007 Micole partners
00005  *
00006  * Micole Architecture is free software: you can redistribute it 
00007  * and/or modify it under the terms of the GNU Lesser General 
00008  * Public License as published by the Free Software Foundation, 
00009  * either version 3 of the License, or (at your option) any 
00010  * later version.
00011  *
00012  * Micole Architecture is distributed in the hope that it will be 
00013  * useful, * but WITHOUT ANY WARRANTY; without even the implied 
00014  * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
00015  * PURPOSE.  See the GNU Lesser General Public License for more 
00016  * details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with Micole Architecture.  If not, see
00020  * <http://www.gnu.org/licenses/>.
00021  */
00022 
00023 //#include "stdafx.h"
00024 #include "MicoleBusWriteLock.h"
00025 
00026 #ifdef _DEBUG
00027 #define new DEBUG_NEW
00028 #undef THIS_FILE
00029 static char THIS_FILE[] = __FILE__;
00030 #endif
00031 
00032 inline bool Wait( int nObjects, HANDLE* pObjects, DWORD Timeout )
00033 {
00034     DWORD Result = WaitForMultipleObjects(nObjects, pObjects, true, Timeout);
00035     if ( DWORD(Result - WAIT_OBJECT_0) < nObjects )
00036         return true;
00037 
00038     // timeout or error
00039     return false;
00040 };
00041 
00042 inline bool Wait1( HANDLE hObj, DWORD Timeout )
00043 {
00044     DWORD Result = WaitForSingleObject(hObj, Timeout);
00045     return (Result == WAIT_OBJECT_0);
00046 };
00047 
00048 inline bool Wait2( HANDLE hObj1, HANDLE hObj2, DWORD Timeout )
00049 {
00050     HANDLE hArray[2] = {hObj1, hObj2};
00051     return Wait(2, hArray, Timeout);
00052 };
00053 
00054 inline bool Wait3( HANDLE hObj1, HANDLE hObj2, HANDLE hObj3, DWORD Timeout )
00055 {
00056     HANDLE hArray[3] = {hObj1, hObj2, hObj3};
00057     return Wait(3, hArray, Timeout);
00058 };
00059 
00060 MicoleBusReadWriteLock::MicoleBusReadWriteLock()
00061 :
00062 m_CanRead(true, true),   // initially signalled, manual reset
00063 m_CanWrite(true, true),  // ditto
00064 m_Readers(0)
00065 {
00066 };
00067 
00068 bool MicoleBusReadWriteLock::LockRead( DWORD Timeout )
00069 {
00070     if (!Wait2( m_Access, m_CanRead, Timeout))
00071         return false;
00072 
00073     ++m_Readers;
00074     ResetEvent(m_CanWrite); // cannot write
00075     ReleaseMutex(m_Access); // let others access the lock
00076     return true;
00077 };
00078 
00079 bool MicoleBusReadWriteLock::UnlockRead( DWORD Timeout )
00080 {
00081     if (!Wait1( m_Access, Timeout ))
00082         return false;
00083 
00084 
00085     ASSERT(m_Readers > 0);
00086     --m_Readers;
00087 
00088     if (m_Readers == 0)
00089     {
00090         SetEvent(m_CanWrite);
00091     };
00092 
00093     ReleaseMutex(m_Access); // let others access the lock
00094     return true;
00095 };
00096 
00097 bool MicoleBusReadWriteLock::LockWrite( DWORD Timeout )
00098 {
00099     if (!Wait3(m_Access, m_CanWrite, m_WriterMutex, Timeout))
00100         return false;
00101 
00102     ASSERT(m_Readers == 0);
00103     ResetEvent(m_CanRead); // cannot read
00104     ReleaseMutex(m_Access); // let others access the lock
00105 
00106     // note: write mutex remains locked
00107     return true;
00108 };
00109 
00110 bool MicoleBusReadWriteLock::UnlockWrite( DWORD Timeout )
00111 {
00112     if (!Wait1( m_Access, Timeout ))
00113         return false;
00114 
00115     ASSERT(m_Readers == 0);
00116 
00117     ReleaseMutex(m_WriterMutex);
00118     SetEvent(m_CanWrite);
00119     SetEvent(m_CanRead);
00120     ReleaseMutex(m_Access); // let others access the lock
00121     return true;
00122 };    
00123 
00124 //**********************************************************
00125 MicoleBusReadWriteLockEx::MicoleBusReadWriteLockEx()
00126 :
00127 m_CanRead(true, true),   // initially signalled, manual reset
00128 m_CanWrite(true, true),  // ditto
00129 m_TotalReaderThreads(0),
00130 m_TotalWriterThreads(0)
00131 {
00132 };
00133 
00134 bool MicoleBusReadWriteLockEx::LockRead( DWORD Timeout )
00135 {
00136     DWORD ThreadId = GetCurrentThreadId();
00137 
00138     // gain access to the map and check if we are already a reader or a writer
00139     if (!Wait1( m_Access, Timeout ))
00140         return false;
00141 
00142     KThreadMap::iterator pInfo = m_Threads.find(ThreadId);
00143 
00144     if (pInfo != m_Threads.end())
00145     {
00146         // we are in the map
00147         KThreadInfo& Info = pInfo->second;
00148         ASSERT( Info.IsReader() || Info.IsWriter() );
00149         ++Info.ReadCount;
00150     }
00151     else
00152     {
00153         // we are a new reader
00154         ReleaseMutex(m_Access);
00155 
00156         if (!Wait2(m_Access, m_CanRead, Timeout))
00157             return false;
00158 
00159         KThreadInfo Info;
00160         Info.ReadCount = 1;
00161         Info.WriteCount = 0;
00162         m_Threads[ThreadId] = Info;
00163         IncReaders();
00164     }
00165 
00166     ReleaseMutex(m_Access);
00167 
00168     return true;
00169 };
00170 
00171 bool MicoleBusReadWriteLockEx::UnlockRead( DWORD Timeout )
00172 {
00173     DWORD ThreadId = GetCurrentThreadId();
00174 
00175     // gain access to the map
00176     if (!Wait1( m_Access, Timeout ))
00177         return false;
00178 
00179     KThreadMap::iterator pInfo = m_Threads.find(ThreadId);
00180 
00181     // we **MUST** be in the map    
00182     ASSERT(pInfo != m_Threads.end());
00183 
00184     KThreadInfo& Info = pInfo->second;
00185     ASSERT(Info.IsReader());
00186 
00187     --Info.ReadCount;
00188     if (!Info.IsWriter() && !Info.IsReader())
00189     {
00190         DecReaders();
00191         m_Threads.erase(pInfo);
00192     }
00193 
00194     ReleaseMutex(m_Access);
00195     return true;
00196 };
00197 
00198 bool MicoleBusReadWriteLockEx::LockWrite( DWORD Timeout )
00199 {
00200     DWORD ThreadId = GetCurrentThreadId();
00201 
00202     // gain access to the map and check if we are already a reader or a writer
00203     if (!Wait1( m_Access, Timeout ))
00204         return false;
00205 
00206     KThreadMap::iterator pInfo = m_Threads.find(ThreadId);
00207 
00208     if (pInfo != m_Threads.end())
00209     {
00210         // we are in the map - must be either writer or reader
00211         KThreadInfo& Info = pInfo->second;
00212         ASSERT( Info.IsReader() || Info.IsWriter() );
00213 
00214         if (!Info.IsWriter())
00215         {
00216             ASSERT(Info.IsReader());
00217 
00218             // Since we are an active reader, there can be no other writers
00219             // Grab writer mutex - it MUST be available
00220             VERIFY( Wait1(m_WriterMutex, 0 ) );
00221 
00222             // denounce our reader status
00223             DecReaders();
00224 
00225             // We are now a potential writer
00226             // Increment total writers count to include ourselves
00227             // This also prevents new readers from coming in
00228             IncWriters();
00229 
00230             if (m_TotalReaderThreads != 0)
00231             {
00232                 // we are not the only reader
00233                 // wait for others to finish
00234 
00235                 // release access and wait for last reader to signal CanWrite event
00236                 ReleaseMutex(m_Access);
00237                 if (!Wait2( m_Access, m_CanWrite, Timeout ))
00238                 {
00239                     // we did not make it; must wait for access and restore our reader status
00240                     VERIFY( Wait1(m_Access, INFINITE) );
00241 
00242                     // restore our reader status
00243                     IncReaders();
00244 
00245                     // withdraw our writer request
00246                     DecWriters();
00247 
00248                     ReleaseMutex(m_WriterMutex);
00249                     ReleaseMutex(m_Access);
00250                     return false;
00251                 }
00252             }
00253         }
00254     }
00255     else
00256     {
00257         // we are not in the map - we are neither writer nor reader
00258 
00259         // Increment total writers count to include ourselves
00260         // This also prevents new readers from coming in
00261         IncWriters();
00262 
00263         // wait for readers to finish and for any writers to release writer mutex
00264         ReleaseMutex(m_Access);
00265 
00266         if (!Wait3(m_Access, m_CanWrite, m_WriterMutex, Timeout))
00267         {
00268             // we did not make it - restore everything
00269             VERIFY( Wait1(m_Access, INFINITE) );
00270             DecWriters();
00271             ReleaseMutex(m_Access);
00272             return false;
00273         }
00274 
00275         KThreadInfo Info;
00276         Info.ReadCount = 0;
00277         Info.WriteCount = 0;
00278 
00279         std::pair<KThreadMap::iterator, bool> InsertResult =
00280             m_Threads.insert(std::make_pair(ThreadId, Info));
00281 
00282         ASSERT(InsertResult.second);
00283         pInfo = InsertResult.first;
00284 
00285         // all mutexes are acquired - fall through to IncrementWriteCount
00286     }
00287 
00288     // we are already a writer - just update the counter
00289     ++pInfo->second.WriteCount;
00290     ReleaseMutex(m_Access);
00291     return true;
00292 };
00293 
00294 bool MicoleBusReadWriteLockEx::UnlockWrite( DWORD Timeout )
00295 {
00296     DWORD ThreadId = GetCurrentThreadId();
00297     
00298     // gain access to the map
00299     if (!Wait1( m_Access, Timeout ))
00300         return false;
00301 
00302     // we **MUST** be in the map and we must be a writer
00303     KThreadMap::iterator pInfo = m_Threads.find(ThreadId);
00304     ASSERT(pInfo != m_Threads.end());
00305 
00306     KThreadInfo& Info = pInfo->second;
00307     ASSERT(Info.IsWriter());
00308 
00309     --Info.WriteCount;
00310     if (!Info.IsWriter())
00311     {
00312         // we are no longer a writer
00313         ReleaseMutex(m_WriterMutex);
00314         DecWriters();
00315 
00316         if (Info.IsReader())
00317         {
00318             // restore our reader status
00319             IncReaders();
00320         }
00321         else
00322         {
00323             // we don't hold any more locks - remove from the map
00324             m_Threads.erase(pInfo);
00325         }
00326     }
00327 
00328     ReleaseMutex(m_Access);
00329     return true;
00330 };
00331 

Generated on Tue Oct 16 17:10:43 2007 for Micole by  doxygen 1.4.7