/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 * @standard c++98
 *
 * @file thread.hh
 * @ccflags
 * @ldflags -lpthread
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * Threading and thread synchronisation classes:
 *
 *  - class sw::thread:
 *
 *      Simple thread handling by extending this class and overloading the methods
 *      on_loop(), on_start() and on_stop():
 *
 *        class mythread : public sw::thread
 *        {
 *        public:
 *          mythread() : sw::thread(), counter(0)   { ; }
 *          virtual ~mythread()  { ; }
 *        protected:
 *          void on_start()
 *          { cout << "Thread started: " << thread::current_thread() << endl; }
 *
 *          void on_stop()
 *          { cout << "Thread stopped: " << thread::current_thread() << endl; }
 *
 *          bool on_loop()
 *          { thread::sleep(100); ++counter; cout << "Thread running: " <<
 *            (long)thread::current_thread() << " " << counter << endl; return true;
 *          }
 *
 *          int counter;
 *        };
 *
 *        [...]
 *        mythread thread;
 *        if(!thread.start()) { error ...  }
 *        thread.stop();
 *        if(thread.running()) { ... }
 *        if(thread.stopped()) { ... }
 *        if(thread.id() == ...) { ... }
 *        sw::thread::sleep(milliseconds);
 *        unsigned long this_thread_id = sw::thread::current_thread();
 *
 *      ------------------------------------------------------------------------
 *
 * - class sw::sync::lock
 *
 *      Simple mutex/crirical section based lock. methods: lock(), unlock(), trylock().
 *      For two mutex: lock2(), unlock2()
 *
 *      ------------------------------------------------------------------------
 *
 * - class sw::sync::autolock
 *
 *      Used in conjunction with sw::sync::lock. Automatically locks a sw::sync::lock
 *      passed in the constructor and unlocks in the destructor. Additional methods
 *      allow to unlock/relock earlier.
 *
 *      ------------------------------------------------------------------------
 *
 * - class sw::sync::flag
 *
 *      Simple synchronised boolean like variable, which allows to be waited for
 *      with timeout. Under Windows known as SetEvent/WaitEvent, under *nix realised
 *      with thread conditions.
 *
 *      ------------------------------------------------------------------------
 *
 * - class template sw::sync::fifo<data type, container type= default deque>
 *
 *     Synchronised (auto locked) fifo.
 *
 *      ------------------------------------------------------------------------
 *
 * - class template sw::sync::synchronised<data type>
 *
 *     Synchronised (auto locked) primitive type.
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header +++
 * Copyright (c) 2008-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW_THREAD_HH
#define	SW_THREAD_HH

// <editor-fold desc="includes/debug output" defaultstate="collapsed">
#if defined(OS_WIN) || defined(__MSC_VER) || defined(_WIN32) || defined (_WINDOWS_)
#include <windows.h>
#ifndef OS_WIN
#define OS_WIN
#endif
#define TID_INV INVALID_HANDLE_VALUE
#else
#include <pthread.h>
#include <unistd.h>
#include <sys/time.h>
#define TID_INV 0
#endif
#include <deque>
#include <cstdlib>
#include <cerrno>
#include <cstring>
#include <exception>

#if defined THREAD_DEBUG || defined THREAD_DEBUG2
#include <cstdio>
#include <stdarg.h>
template <typename char_t> static void thread_trace(const char_t* ptrn, ...)
{ va_list args; va_start(args, ptrn); vfprintf(stderr, ptrn, args);
  fprintf(stderr, "\n"); fflush(stderr); va_end(args); }
#else
template <typename char_t> static inline void thread_trace(const char_t*, ...) {;}
#endif
#undef ctrace
#undef ctracem
#undef ctracem2
#define ctrace thread_trace
#define ctracem thread_trace
#ifdef THREAD_DEBUG2
#define ctracem2 thread_trace
#else
#define ctracem2(X)
#endif
// </editor-fold>

// <editor-fold desc="basic_lock" defaultstate="collapsed">
/**
 * Simple mutex based lock. Contains mutable variables, so that the methods can be
 * const (to be used in const methods of classes including the basic_lock to read only).
 */
namespace sw { namespace detail {
template <typename T=int>
class basic_lock
{
#ifdef OS_WIN
public:

  inline basic_lock()
  { try{::InitializeCriticalSection(&cs_);} catch(...) {}}

  virtual ~basic_lock()
  { try{ ::DeleteCriticalSection(&cs_); } catch(...) {;} }

  inline bool lock() const throw()
  { try{::EnterCriticalSection(&cs_); return true;} catch(...) {return false;} }

  inline bool unlock() const throw()
  { try{::LeaveCriticalSection(&cs_); return true;} catch(...) {return false;} }

  inline bool trylock() const throw()
  { try{ return ::TryEnterCriticalSection(&cs_) != 0;} catch(...) {return false;} }

  inline bool corrupt() const throw()
  { return false; }

private:
  mutable CRITICAL_SECTION cs_;

#else
public:

  inline basic_lock()
  {
    ctracem2("basic_lock::basic_lock()");
    if(::pthread_mutexattr_init(&attr_)
    || ::pthread_mutexattr_settype(&attr_, PTHREAD_MUTEX_RECURSIVE)
    || ::pthread_mutex_init(&cs_, &attr_)) {
      memset(&cs_, 0, sizeof(pthread_mutex_t));
    }
  }

  virtual ~basic_lock()
  { ctracem2("basic_lock::~basic_lock()"); ::pthread_mutex_destroy(&cs_); }

  inline bool lock() const throw()
  { ctracem2("basic_lock::lock()"); return !::pthread_mutex_lock(&cs_); }

  inline bool unlock() const throw()
  { ctracem2("basic_lock::unlock()"); return !::pthread_mutex_unlock(&cs_); }

  inline bool trylock() const throw()
  { ctracem2("basic_lock::trylock()"); return !::pthread_mutex_trylock(&cs_); }

  inline bool corrupt() const throw()
  {
    ctracem2("basic_lock::corrupt()");
    for(unsigned i=0; i<sizeof(pthread_mutex_t); ++i) {
      if(((const char*)(&cs_))[i]) return false;
    }
    return true;
  }

private:

  pthread_mutexattr_t attr_;
  mutable pthread_mutex_t cs_;

#endif

  #if(0) /* unused */
  bool lock2(const basic_lock & other_cs) const throw()
  {
    ctracem2("basic_lock::lock2()");
    while(true) {
      if(!lock()) return false;
      if(other_cs.trylock()) return true;
      if(!unlock()) return false;
    }
  }

  bool unlock2(const basic_lock & other_cs) const throw()
  { ctracem2("basic_lock::unlock2()"); return (other_cs.unlock()?1:0) & (unlock()?1:0); }
  #endif

};
}}
// </editor-fold>

// <editor-fold desc="basic_autolock" defaultstate="collapsed">
/**
 * Auto lock class template, locks in constructor, unlocks in destructor (if not
 * already unlocked)
 */
namespace sw { namespace detail {
template <typename lock_type=basic_lock<> >
class basic_autolock
{
public:

  explicit inline basic_autolock(lock_type &lck) throw() : locked_(false), lock_(lck)
  { ctracem2("basic_autolock::basic_autolock()"); lock(); }

  virtual ~basic_autolock() throw()
  { ctracem2("basic_autolock::~basic_autolock()"); unlock(); }

  inline bool locked() const throw()
  { ctracem2("basic_autolock::locked()"); return locked_; }

  inline bool lock() const throw()
  { ctracem2("basic_autolock::lock()"); return (locked_ = lock_.lock()); }

  inline bool unlock() const throw()
  { ctracem2("basic_autolock::unlock()"); return !(locked_ &= !lock_.unlock()); }

protected:

  inline basic_autolock() : locked_(false), lock_()
  { }

  mutable bool locked_;
  lock_type & lock_;
};
}}
// </editor-fold>

// <editor-fold desc="specialisations" defaultstate="collapsed">
namespace sw { namespace sync {
  typedef sw::detail::basic_lock<> lock;
  typedef sw::detail::basic_autolock<> autolock;
}}
// </editor-fold>

// <editor-fold desc="fifo" defaultstate="collapsed">
/**
 * Fifo class template
 */
namespace sw { namespace sync {
template<typename Value_Type, typename Container_Type=std::deque<Value_Type> >
class fifo
{
public:

  typedef Value_Type value_type;
  typedef Container_Type container_type;

  #ifdef SW_FIFO_STD_EXCEPTION
  class exception_type : public std::exception {
    private: const char* s_;
    public : explicit exception_type(const char *p) : s_(p) { ; }
    public : const char* what() const throw() { return s_ ? s_ : ""; }
  };
  #define lock_failed exception_type("Fifo: lock failed");
  #define fifo_empty exception_type("Fifo: empty");
  #define data_error exception_type("Fifo: data error");
  #else
  typedef enum { ok = 0, fifo_empty, lock_failed, data_error } exception_type;
  #endif

  #define lock_fifo() autolock_type alck(cs_); if(!alck.locked()){throw lock_failed;}
  #define fwd_dex(CODE) try { CODE; } catch(...) { throw data_error; }

  inline fifo() : cs_(), ct_()
  {}

  virtual ~fifo()
  { ct_.clear(); cs_.unlock(); }

  inline bool empty() const
  { lock_fifo(); fwd_dex(return ct_.empty();) }

  inline void clear()
  { lock_fifo(); fwd_dex(ct_.clear();) }

  inline unsigned long size() const
  { lock_fifo(); fwd_dex(return (unsigned long) ct_.size();) }

  inline value_type first() const
  {
    lock_fifo();
    if(ct_.empty()) throw fifo_empty;
    fwd_dex(return ct_.front();)
  }

  inline value_type last() const
  {
    lock_fifo();
    if(ct_.empty()) throw fifo_empty;
    fwd_dex(return ct_.back();)
  }

  inline container_type copy() const
  {
    lock_fifo(); fwd_dex(return ct_;)
  }

  inline void put(const value_type & data)
  {
    lock_fifo();
    fwd_dex(ct_.push_back(data);)
  }

  inline value_type get()
  {
    lock_fifo();
    if(ct_.empty()) throw fifo_empty;
    fwd_dex(
      value_type r = ct_.front();
      ct_.pop_front();
      return r;
    )
  }

  unsigned long get(container_type & c, unsigned long n=0)
  {
    lock_fifo();
    fwd_dex(
      if(n==0 || n <= ct_.size()) {
        n = (unsigned long) ct_.size();
        typename container_type::const_iterator it;
        for(it=ct_.begin(); it<ct_.end(); ++it) c.push_back(*it);
        ct_.clear();
        return n;
      }
      unsigned long i=0;
      while(i<n) { c.push_back(ct_.front()); ct_.pop_front(); if(++i >= n) break;}
      return i;
    );
  }

  #undef lock_fifo
  #undef fwd_dex
  #ifdef SW_FIFO_STD_EXCEPTION
  #undef lock_failed
  #undef fifo_empty
  #undef data_error
  #endif

protected:
  typedef lock lock_type;
  typedef autolock autolock_type;
  mutable lock_type cs_;
  container_type ct_;
};
}}
// </editor-fold>

// <editor-fold desc="synchronized" defaultstate="collapsed">
namespace sw { namespace sync {
template<typename T>
class synchronized
{
public:

  inline synchronized()
  {}

  explicit inline synchronized(const T& initval) : value_(initval)
  {}

  virtual ~synchronized()
  {}

  inline void operator = (T value)
  { set(value); }

  inline synchronized& operator = (const synchronized& rf)
  { set(rf.get()); return *this; }

  inline operator T () const
  { return get(); }

  inline void set(const T& value)
  { detail::basic_autolock<> al(cs_); value_ = value; }

  inline T get() const
  { detail::basic_autolock<> al(cs_); return value_; }

private:

  volatile T value_;
  mutable detail::basic_lock<> cs_;
};
}}
// </editor-fold>

// <editor-fold desc="basic_thread" defaultstate="collapsed">
namespace sw { namespace detail {
template <typename Id_Type=unsigned long>
class basic_thread
{
protected:

  typedef Id_Type id_type;

  /**
   * This mehod is to be overloaded. The framing threadFunction()
   * organizes the starting, stopping and suspending. The task()
   * is a method of the extended thread class, so the this pointer
   * is in scope.
   * return false if the thread is to be exited
   */
  virtual void on_start() {}
  virtual void on_stop()  {}
  virtual bool on_loop() = 0;

  /**
   * Constructor / destructor
   */
  #ifdef OS_WIN
  basic_thread() : tid_(TID_INV), state_(0) { ; }
  virtual ~basic_thread() {if(tid_ != TID_INV) {::TerminateThread(tid_, 0); ::CloseHandle(tid_);}}
  #else

  basic_thread() : tid_(TID_INV), state_(0)
  { ctracem("basic_thread::basic_thread([constructed from thread=%d])", current_thread()); }

  basic_thread(const basic_thread&)
  {;}

  void operator = (const basic_thread&)
  {;}

  virtual ~basic_thread()
  {
    ctracem("basic_thread::~basic_thread([%d, from=%d])", tid_, current_thread());
    if(state_ < 2) state_ = 2;
    if(tid_) ::pthread_join(tid_, NULL);
  }
  #endif

public:

  /**
   * Starts the thread. Returns success.
   */
  bool start()
  {
    ctracem("basic_thread::start()");
    if(tid_!=TID_INV || state_) return false; // already running
    #ifdef OS_WIN
    DWORD ec;
    tid_ = ::CreateThread(NULL,0,thread_function, this, 0, &ec);
    if(tid_==NULL || tid_==TID_INV) { tid_ = TID_INV; return false; }
    state_ = 1; // running
    return true;
    #else
    int r = ::pthread_create(&tid_, NULL, thread_function, this);
    switch(r) {
      case 0: state_ = 1; return true;
      case EAGAIN: ctrace("basic_thread::start(): EAGAIN"); break;
      case EINVAL: ctrace("basic_thread::start(): EINVAL"); break;
      case EPERM:  ctrace("basic_thread::start(): EPERM"); break;
      default:     ctrace("basic_thread::start(): ERROR %d", r); break;
    }
    state_ = 3;
    return false;
    #endif
  }

  /**
   * Stops the thread.
   */
  void stop()
  {
    ctracem("basic_thread::stop()");
    #ifdef OS_WIN
    state_ = 2;
    if(basic_thread::thread_id() == basic_thread::current_thread()) return;
    for(int t=0; t<50; t++) { usleep(2000); if(!running()) return; }
    if(tid_ != TID_INV) { ::TerminateThread(tid_, 0); ::CloseHandle(tid_); }
    tid_ = TID_INV;
    state_ = 3;
    #else
    if(!tid_ || state_ >= 3) return;
    state_ = 2; // should close
    if(basic_thread::thread_id() == basic_thread::current_thread()) return;
    ::pthread_join(tid_, NULL); // wait aborted and detatch resources
    state_ = 3;
    #endif
  }

  /**
   * Returns true if the thread is running
   * @return bool
   */
  inline bool running() const
  { return tid_!=TID_INV && state_ < 3;}

  /**
   * Returns true if the thread is stopped
   * @return bool
   */
  inline bool stopped() const
  { return tid_==TID_INV || state_ == 3;}

  /**
   * Returns the id of the objects thread.
   * @return id_type
   */
  inline id_type thread_id() const throw()
  {
    #ifdef OS_WIN
    return (id_type) GetThreadId(tid_);
    #else
    return (id_type) tid_;
    #endif
  }

  /**
   * Returns the id of the current thread
   * @return id_type
   */
  static inline id_type current_thread()
  {
    #ifdef OS_WIN
    return (id_type) ::GetCurrentThreadId();
    #else
    return (id_type) ::pthread_self();
    #endif
  }

  /**
   * Sleep current thread
   * @param unsigned ms
   */
  static void usleep(unsigned long us)
  {
    #ifdef OS_WIN
    ::Sleep((DWORD)(us/1000));
    #else
    ::usleep((useconds_t)us);
    #endif
  }

  #ifdef OS_WIN
  static DWORD WINAPI thread_function(LPVOID pParam)
  {
    basic_thread* me = reinterpret_cast<basic_thread*>(pParam);
    if(!me) return 0;
    HANDLE tid = me->tid_;
    try {
      me->on_start();
      while(me->state_ < 2 && me->on_loop());
    } catch (const std::exception & e) {
      ctrace("basic_thread::thread_function([%d]): Exception in on_start/loop(): %s", tid, e.what());
    } catch(...) {
      ctrace("basic_thread::thread_function([%d]): Exception in on_start/loop()", tid);
    }
    try {
      me->on_stop();
    } catch (const std::exception & e) {
      ctrace("basic_thread::thread_function([%d]): Exception in on_loop(): %s", tid, e.what());
    } catch(...) {
      ctrace("basic_thread::thread_function([%d]): Exception in on_loop()", tid);
    }
    me->state_ = 3;
    return 0;
  }
  #else
  static void * thread_function(void* pParam)
  {
    ::pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    ::pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    basic_thread* me = reinterpret_cast<basic_thread*>(pParam);
    if(!me) return 0;
    ctrace("basic_thread::thread_function([%d]): starting", basic_thread::current_thread());
    id_type tid = (id_type)me->tid_;
    try {
      me->on_start();
      while(me->state_ < 2 && me->on_loop());
    } catch (const std::exception & e) {
      ctrace("basic_thread::thread_function([%x]): Exception in on_start/loop(): %s", tid, e.what());
    } catch(...) {
      ctrace("basic_thread::thread_function([%x]): Exception in on_start/loop()", tid);
    }
    try {
      me->on_stop();
    } catch (const std::exception & e) {
      ctrace("basic_thread::thread_function([%x]): Exception in on_loop(): %s", tid, e.what());
    } catch(...) {
      ctrace("basic_thread::thread_function([%x]): Exception in on_loop()", tid);
    }
    me->state_ = 3;
    ctrace("basic_thread::thread_function([%x]): stopped", tid);
    ::pthread_exit(0);
    return 0;
  }
  #endif

private:

  #ifdef OS_WIN
  HANDLE tid_;
  #else
  pthread_t tid_; // Used read-only, except when creating the thread, so not interlocked
  #endif
  ::sw::sync::synchronized<unsigned> state_; // Read/written potentially everywhere
};
}}
namespace sw {
  typedef sw::detail::basic_thread<> thread;
}
// </editor-fold>

// <editor-fold desc="undefs" defaultstate="collapsed">
#undef TID_INV
#undef ctrace
#undef ctracem
#undef ctracem2
// </editor-fold>
#endif
