// -*- C++ -*- // Copyright (C) 2009-2010, Vaclav Haisman. All rights reserved. // // Redistribution and use in source and binary forms, with or without modifica- // tion, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- // DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //! @file //! This file contains implementations of synchronization //! primitives using the POSIX threads. It does not contain any //! include guards because it is only a fragment to be included by //! syncprims.h. #include "log4cplus/thread/threads.h" #include #include namespace log4cplus { namespace thread { namespace impl { struct PthreadMutexAttr { PthreadMutexAttr () { int ret = pthread_mutexattr_init (&attr); if (ret != 0) LOG4CPLUS_THROW_RTE ("PthreadMutexAttr::PthreadMutexAttr"); } ~PthreadMutexAttr () { try { int ret = pthread_mutexattr_destroy (&attr); if (ret != 0) LOG4CPLUS_THROW_RTE ("PthreadMutexAttr::~PthreadMutexAttr"); } catch (...) { } } void set_type (log4cplus::thread::Mutex::Type t) { int mutex_type; switch (t) { case log4cplus::thread::Mutex::RECURSIVE: mutex_type = PTHREAD_MUTEX_RECURSIVE; break; default: mutex_type = PTHREAD_MUTEX_DEFAULT; } int ret = pthread_mutexattr_settype (&attr, mutex_type); if (ret != 0) LOG4CPLUS_THROW_RTE ("PthreadMutexAttr::set_type"); } pthread_mutexattr_t attr; }; // // // inline Mutex::Mutex (log4cplus::thread::Mutex::Type t) { PthreadMutexAttr attr; attr.set_type (t); int ret = pthread_mutex_init (&mtx, &attr.attr); if (ret != 0) LOG4CPLUS_THROW_RTE ("Mutex::Mutex"); } inline Mutex::~Mutex () { try { int ret = pthread_mutex_destroy (&mtx); if (ret != 0) LOG4CPLUS_THROW_RTE ("Mutex::~Mutex"); } catch (...) { } } inline void Mutex::lock () const { int ret = pthread_mutex_lock (&mtx); if (ret != 0) LOG4CPLUS_THROW_RTE ("Mutex::lock"); } inline void Mutex::unlock () const { int ret = pthread_mutex_unlock (&mtx); if (ret != 0) LOG4CPLUS_THROW_RTE ("Mutex::unlock"); } // // // inline Semaphore::Semaphore (unsigned max, unsigned initial) { unsigned const sem_value_max = #if defined (SEM_VALUE_MAX) SEM_VALUE_MAX #else (std::numeric_limits::max) () #endif ; unsigned const limited_max = (std::min) (max, sem_value_max); unsigned const limited_initial = (std::min) (initial, limited_max); int ret = 0; #if defined (LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE) std::ostringstream oss; char size_check[2 * (static_cast(sizeof (std::ptrdiff_t)) - static_cast(sizeof (this))) + 1]; (void)size_check; oss << getpid () << "-" << reinterpret_cast(this); std::string name (oss.str ()); sem = sem_open (name.c_str (), O_CREAT, S_IRWXU | S_IRWXG, limited_max); ret = sem == SEM_FAILED; if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::Semaphore"); try { // Unlink the semaphore early to simulate anonymous semaphore. ret = sem_unlink (name.c_str ()); if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::Semaphore"); } catch (std::runtime_error const &) { ret = sem_close (sem); if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::~Semaphore"); throw; } #else ret = sem_init (&sem, 0, limited_max); if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::Semaphore"); #endif try { for (unsigned i = limited_initial; i < limited_max; ++i) lock (); } catch (std::runtime_error const &) { #if defined (LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE) ret = sem_close (sem); #else ret = sem_destroy (&sem); #endif if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::~Semaphore"); throw; } } inline Semaphore::~Semaphore () { try { int ret = 0; #if defined (LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE) ret = sem_close (sem); #else ret = sem_destroy (&sem); #endif if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::~Semaphore"); } catch (...) { } } inline void Semaphore::unlock () const { #if defined (LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE) int ret = sem_post (sem); #else int ret = sem_post (&sem); #endif if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::unlock"); } inline void Semaphore::lock () const { #if defined (LOG4CPLUS_USE_NAMED_POSIX_SEMAPHORE) int ret = sem_wait (sem); #else int ret = sem_wait (&sem); #endif if (ret != 0) LOG4CPLUS_THROW_RTE ("Semaphore::lock"); } // // // inline FairMutex::FairMutex () : sem (1, 1) { } inline FairMutex::~FairMutex () { } inline void FairMutex::lock () const { sem.lock (); } inline void FairMutex::unlock () const { sem.unlock (); } // // // inline ManualResetEvent::ManualResetEvent (bool sig) : mtx (log4cplus::thread::Mutex::DEFAULT) , sigcount (0) , signaled (sig) { int ret = pthread_cond_init (&cv, 0); if (ret != 0) LOG4CPLUS_THROW_RTE ("ManualResetEvent::ManualResetEvent"); } inline ManualResetEvent::~ManualResetEvent () { try { int ret = pthread_cond_destroy (&cv); if (ret != 0) LOG4CPLUS_THROW_RTE ("ManualResetEvent::~ManualResetEvent"); } catch (...) { } } inline void ManualResetEvent::signal () const { MutexGuard mguard (mtx); signaled = true; sigcount += 1; int ret = pthread_cond_broadcast (&cv); if (ret != 0) LOG4CPLUS_THROW_RTE ("ManualResetEVent::signal"); } inline void ManualResetEvent::wait () const { MutexGuard mguard (mtx); if (! signaled) { unsigned prev_count = sigcount; do { int ret = pthread_cond_wait (&cv, &mtx.mtx); if (ret != 0) { mguard.unlock (); mguard.detach (); LOG4CPLUS_THROW_RTE ("ManualResetEvent::wait"); } } while (prev_count == sigcount); } } inline bool ManualResetEvent::timed_wait (unsigned long msec) const { MutexGuard mguard (mtx); if (! signaled) { helpers::Time const wakeup_time (helpers::Time::gettimeofday () + helpers::Time (msec / 1000, (msec % 1000) * 1000)); struct timespec const ts = {wakeup_time.sec (), wakeup_time.usec () * 1000}; unsigned prev_count = sigcount; do { int ret = pthread_cond_timedwait (&cv, &mtx.mtx, &ts); switch (ret) { case 0: break; case ETIMEDOUT: return false; default: mguard.unlock (); mguard.detach (); LOG4CPLUS_THROW_RTE ("ManualResetEvent::timed_wait"); } } while (prev_count == sigcount); } return true; } inline void ManualResetEvent::reset () const { MutexGuard mguard (mtx); signaled = false; } // // // #if defined (LOG4CPLUS_POOR_MANS_SHAREDMUTEX) #include "log4cplus/thread/impl/syncprims-pmsm.h" #else inline SharedMutex::SharedMutex () { int ret = pthread_rwlock_init (&rwl, 0); if (ret != 0) LOG4CPLUS_THROW_RTE ("SharedMutex::SharedMutex"); } inline SharedMutex::~SharedMutex () { try { int ret = pthread_rwlock_destroy (&rwl); if (ret != 0) LOG4CPLUS_THROW_RTE ("SharedMutex::~SharedMutex"); } catch (...) { } } inline void SharedMutex::rdlock () const { int ret; do { ret = pthread_rwlock_rdlock (&rwl); switch (ret) { case EAGAIN: // The read lock could not be acquired because the maximum // number of read locks for rwlock has been exceeded. log4cplus::thread::yield (); // Fall through. case 0: break; default: LOG4CPLUS_THROW_RTE ("SharedMutex::rdlock"); } } while (ret != 0); } inline void SharedMutex::rdunlock () const { unlock (); } inline void SharedMutex::wrlock () const { int ret = pthread_rwlock_wrlock (&rwl); if (ret != 0) LOG4CPLUS_THROW_RTE ("SharedMutex::wrlock"); } inline void SharedMutex::wrunlock () const { unlock (); } inline void SharedMutex::unlock () const { int ret = pthread_rwlock_unlock (&rwl); if (ret != 0) LOG4CPLUS_THROW_RTE ("SharedMutex::unlock"); } #endif } } } // namespace log4cplus { namespace thread { namespace impl {