/* * Portable Agile C++ Classes (PACC) * Copyright (C) 2001-2003 by Marc Parizeau * http://manitou.gel.ulaval.ca/~parizeau/PACC * * 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 * * Contact: * Laboratoire de Vision et Systemes Numeriques * Departement de genie electrique et de genie informatique * Universite Laval, Quebec, Canada, G1K 7P4 * http://vision.gel.ulaval.ca * */ /*! * \file PACC/Threading/Condition.cpp * \brief Class methods for the portable condition. * \author Marc Parizeau, Laboratoire de vision et systèmes numériques, Université Laval * $Revision: 1.5.2.1 $ * $Date: 2007/09/10 18:24:09 $ */ #include "Threading/Condition.hpp" #ifdef WIN32 #define _WIN32_WINNT 0x0400 // for SignalObjectAndWait #include typedef struct { // This windows implementation uses the SignalObjectAndWait solution from D.C. // Schmidt and Irfan Pyarali. See http://www.cs.wustl.edu/~schmidt/win32-cv-1.html int mWaiters; bool mBroadcast; // Critical section to protect variables CRITICAL_SECTION mLock; // Semaphore is used to queue up waiting threads HANDLE mSemaphore; // Auto-reset event to signal last waiting thread HANDLE mDone; } pthread_cond_t; #else #include #include #include #include #endif using namespace PACC; /*! \brief Allocate native structure and create condition. Any error raises a Threading::Exception. */ Threading::Condition::Condition(void) { pthread_cond_t* lCondition = new pthread_cond_t; #ifdef WIN32 lCondition->mWaiters = 0; lCondition->mBroadcast = false; ::InitializeCriticalSection(&lCondition->mLock); lCondition->mSemaphore = ::CreateSemaphore(0, 0, 0x7fffffff, 0); lCondition->mDone = ::CreateEvent(0, false, false, 0); if(lCondition->mSemaphore == 0 || lCondition->mDone == 0) #else if(::pthread_cond_init(lCondition, 0) != 0) #endif throw Exception(eOtherError, "Threading::Condition() can't create!"); mCondition = lCondition; } //! Destroy condition and free allocated native structure. Threading::Condition::~Condition(void) { pthread_cond_t* lCondition = (pthread_cond_t*) mCondition; #ifdef WIN32 ::DeleteCriticalSection(&lCondition->mLock); while(::CloseHandle(lCondition->mSemaphore) == 0) broadcast(); while(::CloseHandle(lCondition->mDone) == 0) broadcast(); #else while(::pthread_cond_destroy(lCondition) == EBUSY) broadcast(); #endif delete lCondition; mCondition = 0; } /*! \brief Broadcast a wake up signal to all waiting threads. This method assumes that the embedded mutex has already been LOCKED by the calling thread (using method Condition::lock). It allows the caller to execute some thread critical operations like setting an external condition prior to broadcasting. If no thread is waiting, the signal is simply ignored. Here is an example of typical usage: \code lCondition.lock(); // set some external condition ... lCondition.broadcast(); lCondition.unlock(); \endcode WARNING: Calling this method without first locking the mutex may produce unexpected OS dependant results. And don't forget to unlock the mutex afterwards! Any error raises a Threading::Exception. */ void Threading::Condition::broadcast(void) const { pthread_cond_t* lCondition = (pthread_cond_t*) mCondition; #ifdef WIN32 EnterCriticalSection(&lCondition->mLock); if(lCondition->mWaiters > 0) { lCondition->mBroadcast = true; // wake up the waiting threads atomically if(!::ReleaseSemaphore(lCondition->mSemaphore, lCondition->mWaiters, 0)) { unlock(); throw Exception(eOtherError, "Condition::broadcast() unable to release semaphore!"); } // wait for threads to acquire the counting semaphore LeaveCriticalSection(&lCondition->mLock); if(::WaitForSingleObject(lCondition->mDone, INFINITE) != WAIT_OBJECT_0) { unlock(); throw Exception(eOtherError, "Condition::broadcast() invalid event!"); } lCondition->mBroadcast = false; } else LeaveCriticalSection(&lCondition->mLock); #else if(::pthread_cond_broadcast(lCondition) != 0) { unlock(); throw Exception(eOtherError, "Condition::broadcast() invalid condition!"); } #endif } /*! \brief Signal a single waiting thread to wake up (which waiting thread wakes up is undetermined). This method assumes that the embedded mutex has already been locked by the calling thread (using method Condition::lock). It allows the caller to execute some thread critical operations, like setting or resetting external conditions. If no thread is waiting, the signal is simply ignored. Here is an example of typical usage: \code lCondition.lock(); // set some external condition ... lCondition.signal(); lCondition.unlock(); \endcode WARNING: Calling this method without first locking the mutex may produce unexpected OS dependant results. And don't forget to unlock the mutex afterwards! Any error raises a Threading::Exception. */ void Threading::Condition::signal(void) const { pthread_cond_t* lCondition = (pthread_cond_t*) mCondition; #ifdef WIN32 EnterCriticalSection(&lCondition->mLock); int lWaiters = lCondition->mWaiters; LeaveCriticalSection(&lCondition->mLock); // wake up a single waiting thread if any waiters if(lWaiters > 0 && ::ReleaseSemaphore(lCondition->mSemaphore, 1, 0) == 0) #else if(::pthread_cond_signal(lCondition) != 0) #endif { unlock(); throw Exception(eOtherError, "Condition::signal() invalid condition!"); } } /*! \brief Wait up to \c inMaxTime seconds for the condition to be signaled (or broadcasted). \return True if condition was signaled (or broadcasted), false if timed out. This method assumes that the embedded mutex has already been locked by the calling thread (using method Condition::lock), and will also return with the mutex locked. A negative or null time out (default) means that the method should wait indefinitely. Here is an example of typical usage: \code ... lCondition.lock(); if(external_condition == false) { // wait indefinitely... lCondition.wait(); // reset external condition ... } lCondition.unlock(); ... \endcode WARNING: Calling this method without first locking the mutex may produce unexpected OS dependant results. And don't forget to unlock the mutex afterwards! Any error raises a Threading::Exception. */ bool Threading::Condition::wait(double inMaxTime) const { bool lReturn; pthread_cond_t* lCondition = (pthread_cond_t*) mCondition; #ifdef WIN32 EnterCriticalSection(&lCondition->mLock); // increment number of waiters lCondition->mWaiters += 1; LeaveCriticalSection(&lCondition->mLock); // wait for the semaphore after atomically unlocking the mutex HANDLE* lMutex = (HANDLE*) mMutex; DWORD lRes = ::SignalObjectAndWait(*lMutex, lCondition->mSemaphore, (inMaxTime <= 0 ? INFINITE : (DWORD)(inMaxTime*1000)), false); if((lReturn = (lRes != WAIT_TIMEOUT)) && lRes != WAIT_OBJECT_0) { unlock(); throw Exception(eOtherError, "Condition::wait() invalid semaphore!"); } EnterCriticalSection(&lCondition->mLock); // we're no longer waiting, decrement number of waiters lCondition->mWaiters -= 1; bool lLastWaiter = lCondition->mBroadcast && lCondition->mWaiters == 0; LeaveCriticalSection(&lCondition->mLock); if(lLastWaiter) { // wait for the mutex after atomically signaling the broadcaster that all threads have awakened if(::SignalObjectAndWait(lCondition->mDone, *lMutex, INFINITE, false) != WAIT_OBJECT_0) { unlock(); throw Exception(eOtherError, "Condition::wait() invalid mutex!"); } } else if(::WaitForSingleObject(*lMutex, INFINITE) != WAIT_OBJECT_0) { unlock(); throw Exception(eOtherError, "Condition::wait() invalid mutex!"); } #else int lRes; // pthread_cond_wait atomically unlocks the mutex, waits on the condition, and locks the mutex again if(inMaxTime <= 0) lRes = ::pthread_cond_wait(lCondition, (pthread_mutex_t*)mMutex); else { // get time of day and add specified time out struct timeval lVal; ::gettimeofday(&lVal, 0); struct timespec lSpec; lSpec.tv_sec = lVal.tv_sec + (long) inMaxTime; lSpec.tv_nsec = lVal.tv_usec*1000 + (long) ((inMaxTime - floor(inMaxTime)) * 1000000000); // check that the number of nanoseconds is less than 1 sec if(lSpec.tv_nsec >= 1000000000) { // otherwise adjust... lSpec.tv_nsec -= 1000000000; lSpec.tv_sec += 1; } // pthread_cond_timedwait atomically unlocks the mutex, waits on the condition, and locks the mutex again lRes = ::pthread_cond_timedwait(lCondition, (pthread_mutex_t*)mMutex, &lSpec); } if((lReturn = (lRes != ETIMEDOUT)) && lRes != 0) { unlock(); throw Exception(eOtherError, "Condition::wait() invalid condition!"); } #endif return lReturn; }