/* -*-c++-*- header - Open Producer - Copyright (C) 2002 Don Burns
 * Distributed under the terms of the GNU LIBRARY GENERAL PUBLIC LICENSE (LGPL)
 * as published by the Free Software Foundation.
 */
#ifndef PRODUCER_BARRIER
#define PRODUCER_BARRIER

#include <Producer/Export>

#ifdef _USE_OPEN_THREAD // [

#include <OpenThread/Barrier>

namespace Producer {

class Barrier : public OpenThreads::Barrier, public Referenced
{
    public:
        Barrier(int max) : OpenThreads::Barrier(max) {}
};

}

#else //][

#include <pthread.h>

#ifdef __sgi

#include <unistd.h>
#include <ulocks.h>

namespace Producer {


class Barrier  : public Referenced
{
    public :
        Barrier( int max ) : _max(max) 
        { 
            char file[128];
            sprintf( file, "/tmp/.producer_barrier_%8d", getpid() );
            us = usinit( file );
            b = new_barrier(us);
            init_barrier(b);
        }

        ~Barrier() 
        {
            char file[128];
            sprintf( file, "/tmp/.producer_barrier_%8d", getpid() );
            unlink(file);
            free_barrier( b );
        }

        void block()
        {
            barrier(b, _max);
        }

        int getMax() const { return _max; }

    private :
        int _max;
    usptr_t *us;
    barrier_t *b;
};

}

#else

namespace Producer {

class PR_EXPORT Barrier : public Referenced
{
    public :
        Barrier( int max )
        {
            pthread_mutexattr_t mutex_attr;
            pthread_mutexattr_init( &mutex_attr );

#if 0 // These lines of code are tried and tested on SGI only.  Since Barrier 
         is temporarily not using pthread_cond_wait on SGI, we will comment 
         this section out for now.  [
#ifndef __linux__
            pthread_mutexattr_settype( &mutex_attr, PTHREAD_MUTEX_ERRORCHECK );
#endif

    //-------------------------------------------------------------------------
    // Initialization is a bit tricky, since we have to be able to be aware
    // that on many-to-many execution vehicle systems, we may run into
    // priority inversion deadlocks if a mutex is shared between threads
    // of differing priorities.  Systems that do this should provide the
    // following protocol attributes to prevent deadlocks.  Check at runtime.
    //
    //  PRIO_INHERIT causes any thread locking the mutex to temporarily become
    //  the same priority as the highest thread also blocked on the mutex.
    //  Although more expensive, this is the prefered method.
    //
    //  PRIO_PROTECT causes any thread locking the mutex to assume the priority
    //  specified by setprioceiling.  pthread_mutex_lock will fail if
    //  the priority ceiling is lower than the thread's priority.  Therefore,
    //  the priority ceiling must be set to the max priority in order to
    //  garantee no deadlocks will occur.
    //
#if defined (_POSIX_THREAD_PRIO_INHERIT) \
 || defined (_POSIX_THREAD_PRIO_PROTECT) // [

            if(sysconf(_POSIX_THREAD_PRIO_INHERIT) > 0) 
            {
            pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);
            } 
            else if (sysconf(_POSIX_THREAD_PRIO_PROTECT) > 0) 
            {
            pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT);

                int th_policy;
                struct sched_param th_param;
                pthread_getschedparam(pthread_self(), &th_policy, &th_param);

                pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_PROTECT);
                pthread_mutexattr_setprioceiling(&mutex_attr,
                                 sched_get_priority_max(th_policy));
            }

#endif // ]
#endif // if 0 ]
            pthread_mutex_init( &_mut, &mutex_attr );
            pthread_cond_init( &_cond, 0L );
            _count = 0;
            _max = max;
            _frame = 0;
        }

        ~Barrier() {}

        void block()
        {
            pthread_mutex_lock( &_mut );
            _count++;
            if( _count == _max )
            {
                _count = 0;
                _frame++;
                pthread_cond_broadcast(&_cond);
            }
            else
            {
                unsigned long lframe = _frame;
                while( lframe == _frame )
                    pthread_cond_wait(&_cond, &_mut);
            }
            pthread_mutex_unlock( &_mut );
        }


        int getMax() const { return _max; }

    private :
        pthread_mutex_t _mut;
        pthread_cond_t _cond;
        volatile int _count;
        volatile int _max;
        volatile unsigned long _frame;
};
}

#endif // __sgi

#endif // ]

#endif
