//                              Package : omnithread
// omnithread.h                 Created : 7/94 tjr
//
//    Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
//    This file is part of the omnithread library
//
//    The omnithread library is free software; you can redistribute it and/or
//    modify it under the terms of the GNU Library General Public
//    License as published by the Free Software Foundation; either
//    version 2 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
//    Library General Public License for more details.
//
//    You should have received a copy of the GNU Library General Public
//    License along with this library; if not, write to the Free
//    Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
//    02110-1301, USA
//

//
// Interface to OMNI thread abstraction.
//
// This file declares classes for threads and synchronisation objects
// (mutexes, condition variables and counting semaphores).
//
// Wherever a seemingly arbitrary choice has had to be made as to the interface
// provided, the intention here has been to be as POSIX-like as possible.  This
// is why there is no semaphore timed wait, for example.
//

#ifndef __omnithread_h_
#define __omnithread_h_

#ifndef NULL
#define NULL (void*)0
#endif

class omni_mutex;
class omni_condition;
class omni_semaphore;
class omni_thread;

//
// OMNI_THREAD_EXPOSE can be defined as public or protected to expose the
// implementation class - this may be useful for debugging.  Hopefully this
// won't change the underlying structure which the compiler generates so that
// this can work without recompiling the library.
//

#ifndef OMNI_THREAD_EXPOSE
#define OMNI_THREAD_EXPOSE private
#endif


//
// Include implementation-specific header file.
//
// This must define 4 CPP macros of the form OMNI_x_IMPLEMENTATION for mutex,
// condition variable, semaphore and thread.  Each should define any
// implementation-specific members of the corresponding classes.
//


#if defined(__arm__) && defined(__atmos__)
#include <omnithread/posix.h>

#elif defined(__alpha__) && defined(__osf1__)
#include <omnithread/posix.h>

#elif defined(__powerpc__) && defined(__aix__)
#include <omnithread/posix.h>

#elif defined(__hpux__)
#include <omnithread/posix.h>

#elif defined(__WIN32__)
#include "nt.h"

#ifdef _MSC_VER

// Using MSVC++ to compile. If compiling library as a DLL,
// define _OMNITHREAD_DLL. If compiling as a statuc library, define
// _WINSTATIC
// If compiling an application that is to be statically linked to omnithread,
// define _WINSTATIC (if the application is  to be dynamically linked, 
// there is no need to define any of these macros).

#if defined (_OMNITHREAD_DLL) && defined(_WINSTATIC)
#error "Both _OMNITHREAD_DLL and _WINSTATIC are defined."
#elif defined(_OMNITHREAD_DLL)
#define _OMNITHREAD_NTDLL_ __declspec(dllexport)
#elif !defined(_WINSTATIC)
#define _OMNITHREAD_NTDLL_ __declspec(dllimport)
#elif defined(_WINSTATIC)
#define _OMNITHREAD_NTDLL_
#endif
 // _OMNITHREAD_DLL && _WINSTATIC

#else

// Not using MSVC++ to compile
#define _OMNITHREAD_NTDLL_

#endif
 // _MSC_VER
 
#elif defined(__sunos__) && (__OSVERSION__ == 5)
#ifdef UsePthread
#include <omnithread/posix.h>
#else
#include <omnithread/solaris.h>
#endif

#elif defined(__linux__)
#include <omnithread/posix.h>

#elif defined(__nextstep__)
#include <omnithread/mach.h>

#elif defined(__VMS)
#include <omnithread/posix.h>

#elif defined(__SINIX__)
#include <omnithread/posix.h>

#elif defined(__osr5__)
#include <omnithread/posix.h>

#elif defined(__irix__)
#include <omnithread/posix.h>

#else
#error "No implementation header file"
#endif

#if !defined(__WIN32__)
#define _OMNITHREAD_NTDLL_
#endif

#if (!defined(OMNI_MUTEX_IMPLEMENTATION) || \
     !defined(OMNI_CONDITION_IMPLEMENTATION) || \
     !defined(OMNI_SEMAPHORE_IMPLEMENTATION) || \
     !defined(OMNI_THREAD_IMPLEMENTATION))
#error "Implementation header file incomplete"
#endif


//
// This exception is thrown in the event of a fatal error.
//

class _OMNITHREAD_NTDLL_ omni_thread_fatal {
public:
    int error;
    omni_thread_fatal(int e = 0) : error(e) {}
};


//
// This exception is thrown when an operation is invoked with invalid
// arguments.
//

class _OMNITHREAD_NTDLL_ omni_thread_invalid {};


///////////////////////////////////////////////////////////////////////////
//
// Mutex
//
///////////////////////////////////////////////////////////////////////////

class _OMNITHREAD_NTDLL_ omni_mutex {

public:
    omni_mutex(void);
    ~omni_mutex(void);

    void lock(void);
    void unlock(void);
    void acquire(void) { lock(); }
    void release(void) { unlock(); }
        // the names lock and unlock are preferred over acquire and release
        // since we are attempting to be as POSIX-like as possible.

    friend class omni_condition;

private:
    // dummy copy constructor and operator= to prevent copying
    omni_mutex(const omni_mutex&);
    omni_mutex& operator=(const omni_mutex&);

OMNI_THREAD_EXPOSE:
    OMNI_MUTEX_IMPLEMENTATION
};

//
// As an alternative to:
// {
//   mutex.lock();
//   .....
//   mutex.unlock();
// }
//
// you can use a single instance of the omni_mutex_lock class:
//
// {
//   omni_mutex_lock l(mutex);
//   ....
// }
//
// This has the advantage that mutex.unlock() will be called automatically
// when an exception is thrown.
//

class _OMNITHREAD_NTDLL_ omni_mutex_lock {
    omni_mutex& mutex;
public:
    omni_mutex_lock(omni_mutex& m) : mutex(m) { mutex.lock(); }
    ~omni_mutex_lock(void) { mutex.unlock(); }
private:
    // dummy copy constructor and operator= to prevent copying
    omni_mutex_lock(const omni_mutex_lock&);
    omni_mutex_lock& operator=(const omni_mutex_lock&);
};


///////////////////////////////////////////////////////////////////////////
//
// Condition variable
//
///////////////////////////////////////////////////////////////////////////

class _OMNITHREAD_NTDLL_ omni_condition {

    omni_mutex* mutex;

public:
    omni_condition(omni_mutex* m);
        // constructor must be given a pointer to an existing mutex. The
        // condition variable is then linked to the mutex, so that there is an
        // implicit unlock and lock around wait() and timed_wait().

    ~omni_condition(void);

    void wait(void);
        // wait for the condition variable to be signalled.  The mutex is
        // implicitly released before waiting and locked again after waking up.
        // If wait() is called by multiple threads, a signal may wake up more
        // than one thread.  See POSIX threads documentation for details.

    int timedwait(unsigned long secs, unsigned long nanosecs = 0);
        // timedwait() is given an absolute time to wait until.  To wait for a
        // relative time from now, use omni_thread::get_time. See POSIX threads
        // documentation for why absolute times are better than relative.
        // Returns 1 (true) if successfully signalled, 0 (false) if time
        // expired.

    void signal(void);
        // if one or more threads have called wait(), signal wakes up at least
        // one of them, possibly more.  See POSIX threads documentation for
        // details.

    void broadcast(void);
        // broadcast is like signal but wakes all threads which have called
        // wait().

private:
    // dummy copy constructor and operator= to prevent copying
    omni_condition(const omni_condition&);
    omni_condition& operator=(const omni_condition&);

OMNI_THREAD_EXPOSE:
    OMNI_CONDITION_IMPLEMENTATION
};


///////////////////////////////////////////////////////////////////////////
//
// Counting semaphore
//
///////////////////////////////////////////////////////////////////////////

class _OMNITHREAD_NTDLL_ omni_semaphore {

public:
    omni_semaphore(unsigned int initial = 1);
    ~omni_semaphore(void);

    void wait(void);
        // if semaphore value is > 0 then decrement it and carry on. If it's
        // already 0 then block.

    int trywait(void);
        // if semaphore value is > 0 then decrement it and return 1 (true).
        // If it's already 0 then return 0 (false).

    void post(void);
        // if any threads are blocked in wait(), wake one of them up. Otherwise
        // increment the value of the semaphore.

private:
    // dummy copy constructor and operator= to prevent copying
    omni_semaphore(const omni_semaphore&);
    omni_semaphore& operator=(const omni_semaphore&);

OMNI_THREAD_EXPOSE:
    OMNI_SEMAPHORE_IMPLEMENTATION
};

//
// A helper class for semaphores, similar to omni_mutex_lock above.
//

class _OMNITHREAD_NTDLL_ omni_semaphore_lock {
    omni_semaphore& sem;
public:
    omni_semaphore_lock(omni_semaphore& s) : sem(s) { sem.wait(); }
    ~omni_semaphore_lock(void) { sem.post(); }
private:
    // dummy copy constructor and operator= to prevent copying
    omni_semaphore_lock(const omni_semaphore_lock&);
    omni_semaphore_lock& operator=(const omni_semaphore_lock&);
};


///////////////////////////////////////////////////////////////////////////
//
// Thread
//
///////////////////////////////////////////////////////////////////////////

class _OMNITHREAD_NTDLL_ omni_thread {

public:

    enum priority_t {
        PRIORITY_LOW,
        PRIORITY_NORMAL,
        PRIORITY_HIGH
    };

    enum state_t {
        STATE_NEW,              // thread object exists but thread hasn't
                                // started yet.
        STATE_RUNNING,          // thread is running.
        STATE_TERMINATED        // thread has terminated but storage has not
                                // been reclaimed (i.e. waiting to be joined).
    };

    //
    // Constructors set up the thread object but the thread won't start until
    // start() is called. The create method can be used to construct and start
    // a thread in a single call.
    //

    omni_thread(void (*fn)(void*), void* arg = NULL,
                priority_t pri = PRIORITY_NORMAL);
    omni_thread(void* (*fn)(void*), void* arg = NULL,
                priority_t pri = PRIORITY_NORMAL);
        // these constructors create a thread which will run the given function
        // when start() is called.  The thread will be detached if given a
        // function with void return type, undetached if given a function
        // returning void*. If a thread is detached, storage for the thread is
        // reclaimed automatically on termination. Only an undetached thread
        // can be joined.

    void start(void);
        // start() causes a thread created with one of the constructors to
        // start executing the appropriate function.

protected:

    omni_thread(void* arg = NULL, priority_t pri = PRIORITY_NORMAL);
        // this constructor is used in a derived class.  The thread will
        // execute the run() or run_undetached() member functions depending on
        // whether start() or start_undetached() is called respectively.

    void start_undetached(void);
        // can be used with the above constructor in a derived class to cause
        // the thread to be undetached.  In this case the thread executes the
        // run_undetached member function.

    virtual ~omni_thread(void);
        // destructor cannot be called by user (except via a derived class).
        // Use exit() or cancel() instead. This also means a thread object must
        // be allocated with new - it cannot be statically or automatically
        // allocated. The destructor of a class that inherits from omni_thread
        // shouldn't be public either (otherwise the thread object can be
        // destroyed while the underlying thread is still running).

public:

    void join(void**);
        // join causes the calling thread to wait for another's completion,
        // putting the return value in the variable of type void* whose address
        // is given (unless passed a null pointer). Only undetached threads
        // may be joined. Storage for the thread will be reclaimed.

    void set_priority(priority_t);
        // set the priority of the thread.

    static omni_thread* create(void (*fn)(void*), void* arg = NULL,
                               priority_t pri = PRIORITY_NORMAL);
    static omni_thread* create(void* (*fn)(void*), void* arg = NULL,
                               priority_t pri = PRIORITY_NORMAL);
        // create spawns a new thread executing the given function with the
        // given argument at the given priority. Returns a pointer to the
        // thread object. It simply constructs a new thread object then calls
        // start.

    static void exit(void* return_value = NULL);
        // causes the calling thread to terminate.

    static omni_thread* self(void);
        // returns the calling thread's omni_thread object.
        // If the calling thread is not the main thread and
        // is not created using this library, returns 0.

    static void yield(void);
        // allows another thread to run.

    static void sleep(unsigned long secs, unsigned long nanosecs = 0);
        // sleeps for the given time.

    static void get_time(unsigned long* abs_sec, unsigned long* abs_nsec,
                         unsigned long rel_sec = 0, unsigned long rel_nsec=0);
        // calculates an absolute time in seconds and nanoseconds, suitable for
        // use in timed_waits on condition variables, which is the current time
        // plus the given relative offset.

private:

    virtual void run(void* arg) {}
    virtual void* run_undetached(void* arg) { return NULL; }
        // can be overridden in a derived class.  When constructed using the
        // the constructor omni_thread(void*, priority_t), these functions are
        // called by start() and start_undetached() respectively.

    void common_constructor(void* arg, priority_t pri, int det);
        // implements the common parts of the constructors.

    omni_mutex mutex;
        // used to protect any members which can change after construction,
        // i.e. the following 2 members:

    state_t _state;
    priority_t _priority;

    static omni_mutex* next_id_mutex;
    static int next_id;
    int _id;

    void (*fn_void)(void*);
    void* (*fn_ret)(void*);
    void* thread_arg;
    int detached;

public:

    priority_t priority(void) {

        // return this thread's priority.

        omni_mutex_lock l(mutex);
        return _priority;
    }

    state_t state(void) {

        // return thread state (invalid, new, running or terminated).

        omni_mutex_lock l(mutex);
        return _state;
    }

    int id(void) { return _id; }
        // return unique thread id within the current process.


    // This class plus the instance of it declared below allows us to execute
    // some initialisation code before main() is called.

    class _OMNITHREAD_NTDLL_ init_t {
        static int count;
    public:
        init_t(void);
    };

    friend class init_t;

OMNI_THREAD_EXPOSE:
    OMNI_THREAD_IMPLEMENTATION
};

static omni_thread::init_t omni_thread_init;

#endif
