Computer Chess Club Archives


Search

Terms

Messages

Subject: Funny you ask. I just put together a class for this type of stuff ...

Author: Hristo

Date: 00:04:38 12/01/03

Go up one level in this thread


On December 01, 2003 at 02:41:28, Tony Werten wrote:

>When my masterthread is spinning, waiting for results from it's workers threads,
>how do I keep it from burning CPU time ?
>
>The apifunction sleep() doesn't some threadsafe, suspending the thread and
>having it resumed by the worker seems overly complicated. ( Accept maybe if I
>can use a callback function )
>
>Any thoughts ?
>
>Tony


Use mutexes. No other way around it.

You still need to structure your code correctly but the classes bellow will
allow you to do the following:

#include "NILock.h"

NILock haveResults(0,0);

void mainThreadProc(){
   while(true){
      if (haveResults.lock(1000)){
         You got results, since someone unlocked the haveResults
      }else{
         After a 1000 ms you have no results. Perhaps you can do something else.
      }
   }
}

void workerThreadProc(){
   while(true){
   Do something.
   If you have results.

   haveResults.unlock();
   After this the mainThreadProc will awake ... eventually
  }
}

Please feel free to use the source code as you wish.
source for NILock.h:

#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>

#include <iostream>

#include "NIDefinitions.h"
#include "NITime.h"

/**
 *  Need to add simple mutex wrepper object for the simplest possible
lock/unlock
 *  Need to add Pulse like object for use to wakeup waiting objects, without
having
 * to use it for locking/unlocking semantics. "If there is Pulse; they wake-up."
 *
 */
class NISemaphore{
public:
    /** */
    NISemaphore()
    {
        pthread_mutex_init(&m_mut, 0);
    }
    /** */
    ~NISemaphore()
    {
        pthread_mutex_destroy(&m_mut);
    }
    /** */
    inline void lock()
    { pthread_mutex_lock(&m_mut); }
    /** */
    inline void unlock()
    { pthread_mutex_unlock(&m_mut); }
    /** */
    inline NIBool tryLock()
    { return (pthread_mutex_trylock(&m_mut) != 0); }
private:
    pthread_mutex_t m_mut;
};

/**
 * General purpose Lock (semaphore,mutex) interface
 * If there is a timeout for the lock then it is possible that
 * the lock will never be successful. Part of how things seem to work on unix.
 *
 * @author Hristo Doichev
 * @version 0.1
 */
class NILock{
public:
    /**
    * create a single semaphore.
    *
    * @param initial determines the initial state of the semaphore.
    *                 0 the semaphore is created and locked immediately.
    *                 1 the semaphore is not locked within the constructor.
    * @return
    */
    explicit NILock(NIUInt_32 initial):
        m_conVar(initial),
        m_maxLocks(1)
    {
        if (m_conVar > m_maxLocks){
            m_conVar = m_maxLocks;
        }
        pthread_mutex_init(&m_mut, 0);
        pthread_cond_init(&m_con, 0);
    }
    /**
    * create a counting Semaphore.
    *
    * @param initial   determines the amount of locks that can be applied to
this semaphore
    *                   after the cnstruction.
    *                   0 the semaphore is completely locked.
    *                   n <= _max is the number of loks that can be applied
    * @param maxLocks  the maximum number of locks this semaphore can take
    *
    * @return
    */
    explicit NILock(NIUInt_32 initial, NIUInt_32 maxLocks):
        m_conVar(initial),
        m_maxLocks(maxLocks)
    {
        pthread_mutex_init(&m_mut, 0);
        pthread_cond_init(&m_con, 0);
        if (m_conVar > m_maxLocks) {
            m_conVar = m_maxLocks;
        }
    }
    virtual ~NILock(){
        pthread_mutex_destroy(&m_mut);
        pthread_cond_destroy(&m_con);
    }
    /**
    * return true if it is locked.
    */
    virtual NIBool check(){
        NIBool itIsLocked;
        pthread_mutex_lock(&m_mut);
        if (m_conVar > 0) {
            itIsLocked = NIFalse;
        }else{
            itIsLocked = NITrue;
        }
        pthread_mutex_unlock(&m_mut);
        return itIsLocked;
    }
    /** wait forever until lock is acquired */
    virtual void lock(){
        int status=0;
        pthread_mutex_lock(&m_mut);

        while (evaluateConditional() == NIFalse){
            while ((status = pthread_cond_wait(&m_con, &m_mut)) != 0)
                ;
        }
        if (status == 0){
            pthread_mutex_unlock(&m_mut);
        }
    }
    virtual void unlock(){
        pthread_mutex_lock(&m_mut);
        ++m_conVar;
        if (m_conVar > m_maxLocks) {
            m_conVar = m_maxLocks;
        }
        pthread_cond_signal(&m_con);
        pthread_mutex_unlock(&m_mut);
        // allow others to get to the lock if waiting.
        sched_yield();   // This is not absolutely necessary
    }

    /**
    * This is the most complicatied of all NILock functions.
    * The general unix pthread library does not support timeouts on
    * mutexes and therefore we need to use conditional variables.
    * However, the conditional variables do not guarantee smooth
    * execution. There are many conditions which can cause problems.
    *
    * @parameters timeOut in ms
    */
    virtual NIBool lock(NIUInt_32 desiredTimeOut){
        NIBool gotLock = NIFalse;
        NIUInt_32 status = 0;
        struct timespec timeout;
        struct timeval startTime;
        // lock the mutex so we can check the predicate
        pthread_mutex_lock(&m_mut);
        // if someone has the predicate then wait on the condition variable
    TRY_TO_WAIT:
        if (m_conVar == 0) {
            gettimeofday(&startTime,0);
            //std::cout << "  sec: " << startTime.tv_sec << ", usec: " <<
startTime.tv_usec << std::endl;
            NIUInt_32 secDelta = desiredTimeOut/1000;
            timeout.tv_sec = startTime.tv_sec + (secDelta);
            timeout.tv_nsec = startTime.tv_usec +
(desiredTimeOut-(secDelta*1000))*1000;
            //cout << "> sec: " << timeout.tv_sec << ", usec: " <<
timeout.tv_nsec << endl;
            status = pthread_cond_timedwait(&m_con, &m_mut, &timeout);
        }
        switch (status){
            case 0:{
                if (evaluateConditional()){
                    gotLock = true;
                }else{
                    NIUInt_32 tempTime = (time(0) - timeout.tv_sec);
                    if ((desiredTimeOut/1000) > tempTime ) {
                        desiredTimeOut -= (tempTime*1000);
                        //cout << " retry lock( " << _time_out << " )" << endl;
                        goto TRY_TO_WAIT;
                    }
                }
            }break;
            //
            default:
            case EINVAL:
            case ETIMEDOUT:{
                gotLock = false;
            }break;
        }
        // unlock the mutex so others can do their job (and wait)
        if (status != EINVAL){
            pthread_mutex_unlock(&m_mut);
        }
        return gotLock;
    }
protected:
    /** evaluate the conditional variable and change it's state
     * This function should be called only after a lock is acquired on the
conditional variable.
     * @return true if the Condition is true.
     */
    virtual NIBool evaluateConditional(){
        if (m_conVar > 0) {
            --m_conVar;
            return NITrue;
        }
        return NIFalse;
    }
protected:
    pthread_mutex_t m_mut;
    pthread_cond_t m_con;
    NIInt_32 m_conVar;
    NIInt_32 m_maxLocks;
};



This page took 0 seconds to execute

Last modified: Thu, 15 Apr 21 08:11:13 -0700

Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.