//                              Package : omnithread
// omnithread.h                 Created : 7/94 tjr
//
//    Copyright (C) 2016 D. R. Commander. All Rights Reserved.
//    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_

#if defined(_MSC_VER) && _MSC_VER < 1900
#define noexcept(a)
#endif

#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) noexcept(false);

    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) noexcept(false);
    // 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
