/********************************************************************************
*                                                                               *
*                 M u l i t h r e a d i n g   S u p p o r t                     *
*                                                                               *
*********************************************************************************
* Copyright (C) 2004,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
*********************************************************************************
* This library is free software; you can redistribute it and/or                 *
* modify it under the terms of the GNU Lesser General Public                    *
* License as published by the Free Software Foundation; either                  *
* version 2.1 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             *
* Lesser General Public License for more details.                               *
*                                                                               *
* You should have received a copy of the GNU Lesser General Public              *
* License along with this library; if not, write to the Free Software           *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
*********************************************************************************
* $Id: FXThread.h,v 1.40.2.2 2006/07/26 15:25:53 fox Exp $                          *
********************************************************************************/
#ifndef FXTHREAD_H
#define FXTHREAD_H

namespace FX {


// Thread ID type
#ifndef WIN32
typedef unsigned long FXThreadID;
#else
typedef void*         FXThreadID;
#endif


class FXCondition;


/**
* FXMutex provides a mutex which can be used to enforce critical
* sections around updates of data shared by multiple threads.
*/
class FXAPI FXMutex {
  friend class FXCondition;
private:
  FXuval data[24];
private:
  FXMutex(const FXMutex&);
  FXMutex &operator=(const FXMutex&);
public:

  /// Initialize the mutex
  FXMutex(FXbool recursive=FALSE);

  /// Lock the mutex
  void lock();

  /// Return TRUE if succeeded locking the mutex
  FXbool trylock();

  /// Return TRUE if mutex is already locked
  FXbool locked();

  /// Unlock mutex
  void unlock();

  /// Delete the mutex
  ~FXMutex();
  };


/**
* An easy way to establish a correspondence between a C++ scope
* and a critical section is to simply declare an FXMutexLock
* at the beginning of the scope.
* The mutex will be automatically released when the scope is
* left (either by natural means or by means of an exception.
*/
class FXAPI FXMutexLock {
private:
  FXMutex& mtx;
private:
  FXMutexLock();
  FXMutexLock(const FXMutexLock&);
  FXMutexLock& operator=(const FXMutexLock&);
public:

  /// Construct & lock associated mutex
  FXMutexLock(FXMutex& m):mtx(m){ lock(); }

  /// Return reference to associated mutex
  FXMutex& mutex(){ return mtx; }

  /// Lock mutex
  void lock(){ mtx.lock(); }

  /// Return TRUE if succeeded locking the mutex
  FXbool trylock(){ return mtx.trylock(); }

  /// Return TRUE if mutex is already locked
  FXbool locked(){ return mtx.locked(); }

  /// Unlock mutex
  void unlock(){ mtx.unlock(); }

  /// Destroy and unlock associated mutex
  ~FXMutexLock(){ unlock(); }
  };


/**
* A condition allows one or more threads to synchronize
* to an event.  When a thread calls wait, the associated
* mutex is unlocked while the thread is blocked.  When the
* condition becomes signaled, the associated mutex is
* locked and the thread(s) are reawakened.
*/
class FXAPI FXCondition {
private:
  FXuval data[12];
private:
  FXCondition(const FXCondition&);
  FXCondition& operator=(const FXCondition&);
public:

  /// Initialize the condition
  FXCondition();

  /**
  * Wait until condition becomes signalled, using given mutex,
  * which must already have been locked prior to this call.
  */
  void wait(FXMutex& mtx);

  /**
  * Wait until condition becomes signalled, using given mutex,
  * which must already have been locked prior to this call.
  * Returns TRUE if successful, FALSE if timeout occurred.
  * Note that the wait-time is specified in nanoseconds
  * since the Epoch (Jan 1, 1970).
  */
  FXbool wait(FXMutex& mtx,FXlong nsec);

  /**
  * Wake or unblock a single blocked thread
  */
  void signal();

  /**
  * Wake or unblock all blocked threads
  */
  void broadcast();

  /// Delete the condition
  ~FXCondition();
  };


/**
* A semaphore allows for protection of a resource that can
* be accessed by a fixed number of simultaneous threads.
*/
class FXAPI FXSemaphore {
private:
  FXuval data[16];
private:
  FXSemaphore(const FXSemaphore&);
  FXSemaphore& operator=(const FXSemaphore&);
public:

  /// Initialize semaphore with given count
  FXSemaphore(FXint initial=1);

  /// Decrement semaphore
  void wait();

  /// Non-blocking semaphore decrement; return true if locked
  FXbool trywait();

  /// Increment semaphore
  void post();

  /// Delete semaphore
  ~FXSemaphore();
  };


/**
* FXThread provides system-independent support for threads.
* Subclasses must implement the run() function do implement
* the desired functionality of the thread.
* The storage of the FXThread object is to be managed by the
* calling thread, not by the thread itself.
*/
class FXAPI FXThread {
private:
  volatile FXThreadID tid;
private:
  FXThread(const FXThread&);
  FXThread &operator=(const FXThread&);
#ifdef WIN32
  static unsigned int CALLBACK execute(void*);
#else
  static void* execute(void*);
#endif
protected:

  /**
  * All threads execute by deriving the run method of FXThread.
  * If an uncaught exception was thrown, this function returns -1.
  */
  virtual FXint run() = 0;

public:

  /// Initialize thread object.
  FXThread();

  /**
  * Return handle of this thread object.
  * This handle is valid in the context of the thread which
  * called start().
  */
  FXThreadID id() const;

  /**
  * Return TRUE if this thread is running.
  */
  FXbool running() const;

  /**
  * Start thread; the thread is started as attached.
  * The thread is given stacksize for its stack; a value of
  * zero for stacksize will give it the default stack size.
  */
  FXbool start(unsigned long stacksize=0);

  /**
  * Suspend calling thread until thread is done.
  */
  FXbool join();

  /**
  * Suspend calling thread until thread is done, and set code to the
  * return value of run() or the argument passed into exit().
  * If an exception happened in the thread, return -1.
  */
  FXbool join(FXint& code);

  /**
  * Cancel the thread, stopping it immediately, running or not.
  * If the calling thread is this thread, nothing happens.
  * It is probably better to wait until it is finished, in case the
  * thread currently holds mutexes.
  */
  FXbool cancel();

  /**
  * Detach thread, so that a no join() is necessary to harvest the
  * resources of this thread.
  */
  FXbool detach();

  /**
  * Exit the calling thread.
  * No destructors are invoked for objects on thread's stack;
  * to invoke destructors, throw an exception instead.
  */
  static void exit(FXint code=0);

  /**
  * Make the thread yield its time quantum.
  */
  static void yield();

  /**
  * Return time in nanoseconds since Epoch (Jan 1, 1970).
  */
  static FXlong time();

  /**
  * Make the calling thread sleep for a number of nanoseconds.
  */
  static void sleep(FXlong nsec);

  /**
  * Wake at appointed time specified in nanoseconds since Epoch.
  */
  static void wakeat(FXlong nsec);

  /**
  * Return pointer to the FXThread instance associated
  * with the calling thread; it returns NULL for the main
  * thread and all threads not created by FOX.
  */
  static FXThread* self();

  /**
  * Return thread id of calling thread.
  */
  static FXThreadID current();

  /**
  * Set thread priority.
  */
  void priority(FXint prio);

  /**
  * Return thread priority.
  */
  FXint priority();

  /**
  * Destroy the thread immediately, running or not.
  * It is probably better to wait until it is finished, in case
  * the thread currently holds mutexes.
  */
  virtual ~FXThread();
  };

}

#endif

