• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

src/Mutex.cpp

00001 /*
00002  *   This file is part of the Standard Portable Library (SPL).
00003  *
00004  *   SPL is free software: you can redistribute it and/or modify
00005  *   it under the terms of the GNU General Public License as published by
00006  *   the Free Software Foundation, either version 3 of the License, or
00007  *   (at your option) any later version.
00008  *
00009  *   SPL is distributed in the hope that it will be useful,
00010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *   GNU General Public License for more details.
00013  *
00014  *   You should have received a copy of the GNU General Public License
00015  *   along with SPL.  If not, see <http://www.gnu.org/licenses/>.
00016  */
00017 #ifdef _WIN32
00018 #include <spl/configwin32.h>
00019 #else
00020 #include <spl/autoconf/config.h>
00021 #endif
00022 
00023 #include <errno.h>
00024 #ifdef HAVE_UNISTD_H
00025 #include <unistd.h>
00026 #endif
00027 #ifdef HAVE_SYS_TIME_H
00028 #include <sys/time.h>
00029 #endif
00030 #ifdef HAVE_TIME_H
00031 #include <time.h>
00032 #endif
00033 
00034 #include <spl/Environment.h>
00035 #include <spl/threading/Mutex.h>
00036 #include <spl/threading/Event.h>
00037 #include <spl/threading/RWLock.h>
00038 #include <spl/Exception.h>
00039 #include <spl/String.h>
00040 
00041 Mutex::Mutex()
00042 {
00043         m_timeoutMs = INFINITE;
00044         initialized = false;
00045         Init();
00046 }
00047 
00048 Mutex::Mutex(int timeoutMs)
00049 {
00050         m_timeoutMs = timeoutMs;
00051         initialized = false;
00052         Init();
00053 }
00054 
00055 Mutex::~Mutex()
00056 {
00057         if (initialized)
00058         {
00059 #ifdef WIN32
00060                 CloseHandle(mutex);
00061 #else
00062                 pthread_mutex_destroy( &mutex );
00063 #endif
00064         }
00065 }
00066 
00067 void Mutex::Init()
00068 {
00069         if (initialized)
00070         {
00071                 throw new Exception("Mutex already initialized");
00072         }
00073 #ifdef WIN32
00074         mutex = CreateMutex(NULL,FALSE,NULL);
00075         if (mutex == NULL)
00076         {
00077                 throw new IOException(Environment::LastErrorMessage());
00078         }
00079 #else
00080         int ret;
00081         if ( (ret = pthread_mutex_init( &mutex, NULL )) != 0 )
00082         {
00083                 throw new IOException(Environment::LastErrorMessage());
00084         }
00085 #endif
00086         initialized = true;
00087         return;
00088 }
00089 
00090 bool Mutex::Lock()
00091 {
00092         if (!initialized)
00093         {
00094                 throw new Exception("Mutex not initialized");
00095         }
00096 #ifdef WIN32
00097         int timeout = m_timeoutMs == 0 ? INFINITE : m_timeoutMs;
00098         int ret;
00099         if ( ret = (WAIT_TIMEOUT == WaitForSingleObject(mutex, timeout)) )
00100         {
00101                 return false;
00102         }
00103         if ( ret == WAIT_FAILED )
00104         {
00105                 throw new IOException(Environment::LastErrorMessage());
00106         }
00107         return true;
00108 #else
00109         if ( 0 != m_timeoutMs )
00110         {
00111                 // emulate timeout
00112                 int ret = pthread_mutex_trylock( &mutex );
00113                 if ( 0 != ret )
00114                 {
00115                         if ( EBUSY == ret )
00116                         {
00117                                 sleep(m_timeoutMs/1000);
00118                                 ret = pthread_mutex_trylock( &mutex );
00119                         }
00120                         if ( EBUSY == ret )
00121                         {
00122                                 return false;
00123                         }
00124                         if ( 0 != ret )
00125                         {
00126                                 throw new IOException(Environment::LastErrorMessage());
00127                         }
00128                         return true;
00129                 }
00130         }
00131         else
00132         {
00133                 int ret = pthread_mutex_lock( &mutex );
00134                 if ( 0 != ret )
00135                 {
00136                         if ( EINVAL == ret )
00137                         {
00138                                 throw new IOException(Environment::LastErrorMessage());
00139                         }
00140                         else if ( 0 != ret )
00141                         {
00142                                 throw new IOException(Environment::LastErrorMessage());
00143                         }
00144                         return true;
00145                 }
00146         }
00147         return false;
00148 #endif
00149 }
00150 
00151 void Mutex::Unlock()
00152 {
00153         if (!initialized)
00154         {
00155                 throw new Exception("Mutex not initialized");
00156         }
00157 #ifdef _WIN32
00158         if (!ReleaseMutex(mutex))
00159         {
00160                 throw new IOException(Environment::LastErrorMessage());
00161         }
00162 #else
00163         int ret;
00164         if ( 0 != (ret = pthread_mutex_unlock(&mutex)) )
00165         {
00166                 if ( EPERM != ret )
00167                 {
00168                         throw new IOException(Environment::LastErrorMessage());
00169                 }
00170         }
00171 #endif
00172 }
00173 
00174 Event::Event()
00175 {
00176 #ifdef WIN32
00177         if ( NULL == (hevent = CreateEvent(NULL, 0, 0, NULL)) )
00178         {
00179                 throw OutOfMemoryException();
00180         }
00181 #else
00182         int ret;
00183         if ( 0 != (ret = pthread_mutex_init( &mtx, NULL )) )
00184         {
00185                 throw new IOException(Environment::LastErrorMessage());
00186         }
00187         if ( 0 != (ret = pthread_cond_init( &cond, NULL )) )
00188         {
00189                 throw new IOException(Environment::LastErrorMessage());
00190         }
00191 #endif
00192         m_timeoutMs = INFINITE;
00193 }
00194 
00195 Event::Event( int timeoutMs )
00196 {
00197 #ifdef WIN32
00198         if ( NULL == (hevent = CreateEvent(NULL, 0, 0, NULL)) )
00199         {
00200                 throw OutOfMemoryException();
00201         }
00202 #else
00203         int ret = pthread_mutex_init( &mtx, NULL );
00204         if ( 0 != ret )
00205         {
00206                 throw new IOException(Environment::LastErrorMessage());
00207         }
00208         if ( 0 != (ret = pthread_cond_init( &cond, NULL )) )
00209         {
00210                 throw new IOException(Environment::LastErrorMessage());
00211         }
00212 #endif
00213         m_timeoutMs = timeoutMs;
00214 }
00215 
00216 void Event::SetTimeOut( int ms )
00217 {
00218         m_timeoutMs = ms;
00219 }
00220 
00221 Event::~Event()
00222 {
00223 #ifdef WIN32
00224         CloseHandle(hevent);
00225 #else
00226         pthread_mutex_destroy( &mtx );
00227         pthread_cond_destroy( &cond );
00228 #endif
00229 }
00230 
00231 void Event::Wait()
00232 {
00233 #ifdef WIN32
00234         DWORD ret = WaitForSingleObject(hevent, m_timeoutMs);
00235         if (ret == WAIT_FAILED)
00236         {
00237                 throw new IOException(Environment::LastErrorMessage());
00238         }
00239 #else
00240         int ret;
00241 
00242         if ( 0 != (ret = pthread_mutex_lock(&mtx)) )
00243         {
00244                 throw new IOException(Environment::LastErrorMessage());
00245         }
00246 
00247         if ( 0 != m_timeoutMs )
00248         {
00249                 struct timespec ts;
00250                 struct timeval tp;
00251                 
00252                 gettimeofday(&tp, NULL);
00253                 ts.tv_sec  = tp.tv_sec;
00254                 ts.tv_nsec = tp.tv_usec * 1000;
00255                 ts.tv_sec += m_timeoutMs / 1000;
00256                 
00257                 ret = pthread_cond_timedwait(&cond, &mtx, &ts);
00258         }
00259         else
00260         {
00261                 ret = pthread_cond_wait(&cond, &mtx);
00262         }
00263         if ( 0 != ret && ETIMEDOUT != ret )
00264         {
00265                 throw new IOException(Environment::LastErrorMessage());
00266         }
00267         if ( 0 != (ret = pthread_mutex_unlock( &mtx )) )
00268         {
00269                 throw new IOException(Environment::LastErrorMessage());
00270         }
00271 
00272 #endif
00273 }
00274 
00275 void Event::Notify()
00276 {
00277 #ifdef WIN32
00278         if (!SetEvent(hevent))
00279         {
00280                 throw new IOException(Environment::LastErrorMessage());
00281         }
00282 #else
00283         if (0 != pthread_cond_signal(&cond))
00284         {
00285                 throw new IOException(Environment::LastErrorMessage());
00286         }
00287 #endif
00288 }
00289 
00290 RWLock::RWLock()
00291 #if defined(_WIN32) || defined(__TANDEM)
00292 : m_stateMtx(), m_readLockCount(0), m_writeLockCount(0), m_waitingWriters(5000), m_waitingWritersCount(0), m_waitingReaders(5000), m_waitingReadersCount(0)
00293 #endif
00294 {
00295 #if defined(_WIN32) || defined(__TANDEM)
00296 #else
00297         if ( 0 != pthread_rwlock_init(&m_rwlock, NULL) )
00298         {
00299                 throw new IOException(Environment::LastErrorMessage());
00300         }
00301 #endif
00302 }
00303 
00304 RWLock::~RWLock()
00305 {
00306 #if defined(_WIN32) || defined(__TANDEM)
00307 #else
00308         pthread_rwlock_destroy(&m_rwlock);
00309 #endif
00310 }
00311 
00312 void RWLock::LockRead()
00313 {
00314 #if defined(_WIN32) || defined(__TANDEM)
00315         while ( true )
00316         {
00317                 m_stateMtx.Lock();
00318                 if ( m_waitingWritersCount > 0 || 0 != m_writeLockCount )
00319                 {
00320                         m_waitingReadersCount++;
00321                         m_stateMtx.Unlock();
00322                         m_waitingReaders.Wait();
00323                         m_stateMtx.Lock();
00324                         m_waitingReadersCount--;
00325                         m_stateMtx.Unlock();
00326                 }
00327                 else
00328                 {
00329                         ASSERT(0 == m_writeLockCount);
00330                         m_readLockCount++;
00331                         m_stateMtx.Unlock();
00332                         return;
00333                 }
00334         }
00335 #else
00336         if ( 0 != pthread_rwlock_rdlock(&m_rwlock) )
00337         {
00338                 throw new IOException(Environment::LastErrorMessage());
00339         }
00340 #endif
00341 }
00342 
00343 void RWLock::LockWrite()
00344 {
00345 #if defined(_WIN32) || defined(__TANDEM)
00346         while ( true )
00347         {
00348                 m_stateMtx.Lock();
00349                 if ( 0 != m_writeLockCount || m_waitingWritersCount > 0 || m_readLockCount != 0 )
00350                 {
00351                         m_waitingWritersCount++;
00352                         m_stateMtx.Unlock();
00353                         m_waitingWriters.Wait();
00354                         m_stateMtx.Lock();
00355                         m_waitingWritersCount--;
00356                         m_stateMtx.Unlock();
00357                 }
00358                 else
00359                 {
00360                         ASSERT(0 == m_writeLockCount);
00361                         m_writeLockCount++;
00362                         m_stateMtx.Unlock();
00363                         return;
00364                 }
00365         }
00366 #else
00367         if ( 0 != pthread_rwlock_wrlock(&m_rwlock) )
00368         {
00369                 throw new IOException(Environment::LastErrorMessage());
00370         }
00371 #endif
00372 }
00373 
00374 void RWLock::UnlockRead()
00375 {
00376 #if defined(_WIN32) || defined(__TANDEM)
00377         m_stateMtx.Lock();
00378         m_readLockCount--;
00379         ASSERT(m_readLockCount >= 0);
00380         ASSERT( 0 == m_writeLockCount );
00381         m_stateMtx.Unlock();
00382 
00383         if (0 == m_readLockCount && 0 != m_waitingWritersCount )
00384         {
00385                 m_waitingWriters.Notify();
00386         }
00387 #else
00388         if (0 != pthread_rwlock_unlock(&m_rwlock))
00389         {
00390                 throw new IOException(Environment::LastErrorMessage());
00391         }
00392 #endif
00393 }
00394 
00395 void RWLock::UnlockWrite()
00396 {
00397 #if defined(_WIN32) || defined(__TANDEM)
00398         ASSERT( 0 == m_readLockCount );
00399 
00400         m_stateMtx.Lock();
00401         ASSERT(m_writeLockCount == 1);
00402         m_writeLockCount--;
00403         if ( 0 != m_waitingWritersCount )
00404         {
00405                 m_waitingWriters.Notify();
00406         }
00407         else if( 0 != m_waitingReadersCount )
00408         {
00409                 m_waitingReaders.Notify();
00410         }
00411         m_stateMtx.Unlock();
00412 #else
00413         if (0 != pthread_rwlock_unlock(&m_rwlock))
00414         {
00415                 throw new IOException(Environment::LastErrorMessage());
00416         }
00417 #endif
00418 }