// Copyright (C) 2003  Davis E. King (davisking@users.sourceforge.net)
// License: Boost Software License   See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_2_
#define DLIB_THREADS_KERNEl_2_

#ifdef DLIB_ISO_CPP_ONLY
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code.  Turn DLIB_ISO_CPP_ONLY off if you want to use it."
#endif

#include "threads_kernel_abstract.h"
#include <pthread.h>
#include <errno.h>
#include <sys/time.h>
#include "../algs.h"

namespace dlib
{

// ----------------------------------------------------------------------------------------
    
    typedef pthread_t thread_id_type;

    inline thread_id_type get_thread_id (
    )
    {
        return pthread_self();
    }

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // mutex object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    // forward declaration of signaler 
    class signaler;

    class mutex
    {
        // give signaler access to hMutex
        friend class signaler;
    public:

        mutex (
        )
        { 
            if (pthread_mutex_init(&myMutex,0)) 
            {
                throw dlib::thread_error(ECREATE_MUTEX,
        "in function mutex::mutex() an error occurred making the mutex"
                );      
            }
        }

        ~mutex (
        ) { pthread_mutex_destroy(&myMutex); }

        void lock (
        ) const { pthread_mutex_lock(&myMutex); }

        void unlock (
        ) const { pthread_mutex_unlock(&myMutex); }

    private:

        mutable pthread_mutex_t myMutex;

        // restricted functions
        mutex(mutex&);        // copy constructor
        mutex& operator=(mutex&);    // assignement opertor
    };

// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
    // signaler object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

    class signaler
    {

    public:


        signaler (
            const mutex& assoc_mutex
        ) :
            associated_mutex(&assoc_mutex.myMutex),
            m(assoc_mutex)
        { 
            if (pthread_cond_init(&cond,0))
            {
                throw dlib::thread_error(ECREATE_SIGNALER,
        "in function signaler::signaler() an error occurred making the signaler"
                );      
            }
        }

        ~signaler (
        ) { pthread_cond_destroy(&cond); }

        void wait (
        ) const
        { 
            pthread_cond_wait(&cond,associated_mutex);
        }

        bool wait_or_timeout (
            unsigned long milliseconds
        ) const
        { 
            timespec time_to_wait;

            timeval curtime;
            gettimeofday(&curtime,0);

            // get the time and adjust the timespec object by the appropriate amount
            time_to_wait.tv_sec = milliseconds/1000 + curtime.tv_sec;
            time_to_wait.tv_nsec = curtime.tv_usec;
            time_to_wait.tv_nsec *= 1000; 
            time_to_wait.tv_nsec += (milliseconds%1000)*1000000;

            time_to_wait.tv_sec += time_to_wait.tv_nsec/1000000000;
            time_to_wait.tv_nsec = time_to_wait.tv_nsec%1000000000;

            if ( pthread_cond_timedwait(&cond,associated_mutex,&time_to_wait) == ETIMEDOUT)
            {
                return false;
            }
            else 
            {
                return true;
            }
        }

        void signal (
        ) const { pthread_cond_signal(&cond); }

        void broadcast (
        ) const { pthread_cond_broadcast(&cond); }

        const mutex& get_mutex (
        ) const { return m; }

    private:

        pthread_mutex_t* const associated_mutex;
        mutable pthread_cond_t  cond;
        const mutex& m;

        // restricted functions
        signaler(signaler&);        // copy constructor
        signaler& operator=(signaler&);    // assignement opertor
    };

// ----------------------------------------------------------------------------------------

    namespace threads_kernel_shared_helpers
    {
        bool spawn_thread (
            void (*funct)(void*),
            void* param
        );
        /*!
            is identical to create_new_thread() but just doesn't use any thread pooling.
        !*/
    }

// ----------------------------------------------------------------------------------------

}

#include "threads_kernel_shared.h"

#ifdef NO_MAKEFILE
#include "threads_kernel_2.cpp"
#endif

#endif // DLIB_THREADS_KERNEl_2_

