/*
 *
 *  Copyright (C) 1997-2024, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module:  ofstd
 *
 *  Author:  Marco Eichelberg
 *
 *  Purpose: Provides operating system independent abstractions for basic
 *           multi-thread concepts: threads, thread specific data,
 *           semaphores, mutexes and read/write locks. The implementation
 *           of these classes supports the Solaris, POSIX and Win32
 *           multi-thread APIs.
 *
 */


#ifndef OFTHREAD_H
#define OFTHREAD_H

#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/oftypes.h"  /* for class OFBool */
#include "dcmtk/ofstd/ofstring.h" /* for class OFString */

/** Stub function with extern "C" linkage that is passed as a function pointer
 *  to the system function that creates the thread.
 *  @param arg pointer to the OFThread instance to be started, passed as void pointer.
 */
extern "C"
{
#ifdef HAVE_WINDOWS_H
  unsigned int __stdcall thread_stub(void *arg);
#else
  void *thread_stub(void *arg);
#endif
}


/** provides an operating system independent abstraction for threads.
 *  Threads are executed asynchronously in parallel to the main thread
 *  of the process. On multi-processor machines threads may run on
 *  different CPUs if the operating system permits.
 *  This class is abstract. Deriving classes must implement the run()
 *  method which contains the code executed by the thread.
 */
class DCMTK_OFSTD_EXPORT OFThread
{
public:

  /** default constructor. The new thread is not started immediately
   *  upon creation of the OFThread object. Calling the start()
   *  method causes the creation of the thread.
   */
  OFThread();

  /** destructor. Destruction of an OFThread object does not cause
   *  the referenced thread to be stopped and may result in undefined
   *  behaviour if the derived class maintains thread specific data
   *  in this object (which is not recommended).
   *  The join() method should be called prior to destruction
   *  of the thread object to make sure a thread has terminated.
   */
  virtual ~OFThread();

  /** adds a new thread of control to the  current process. The main()
   *  procedure itself is a single thread of control. Each thread executes
   *  simultaneously  with  all  the other  threads  within  the  calling
   *  process. A newly created thread shares all of  the  calling  process'
   *  global data with the other threads in this process except the
   *  execution stack.   The new thread executes the run() method and
   *  terminates upon return from this method or a call to thread_exit()
   *  from within the thread. This method should not be called if a thread
   *  is already running, otherwise a new thread will be started and the
   *  identifier of the old thread will be overwritten, making it
   *  impossible to call join() for the old thread.  It may also result in
   *  undefined behaviour if the derived class maintains thread specific
   *  data in this object (which is not recommended).
   *  @return 0 upon success, an error code otherwise.
   */
  int start();

  /** blocks the calling thread until the thread referenced by the OFThread
   *  object terminates. Several threads cannot wait for the same thread to
   *  complete; one  thread  will  complete the join() method successfully
   *  others may or may not return OFThread::busy. The method will not
   *  block the calling thread if the target thread has already terminated.
   *  @return 0 upon success, OFThread::busy if another thread is already
   *    waiting for the termination of the target thread,
   *    an error code otherwise.
   */
  int join();

  /** returns the thread identifier of the thread referenced by the
   *  OFThread object, if the thread has already been started.
   *  Otherwise returns 0. On certain platforms like OSF/1, a thread ID
   *  contains a pointer to a structure. Therefore, thread IDs should
   *  never be compared directly, but always using the equal() method
   *  provided in this class.
   *  @return thread ID of target thread if started, 0 otherwise.
   */
  unsigned long threadID();

  /** checks if the given thread ID matches the thread ID of the thread
   *  referenced by this object.
   *  @param tID thread ID to be compared
   *  @return OFTrue if equal, OFFalse otherwise.
   */
  OFBool equal(unsigned long tID);

  /** converts any of the error codes returned by the methods of this class
   *  into a textual description, which is written into the string object.
   *  @param description string object into which the error description is written.
   *  @param code error code
   */
  static void errorstr(OFString& description, int code);

  /** this constant is returned by the join() method if another thread
   *  is already waiting for termination of the thread referenced by the
   *  OFThread object. Since this value is operating system dependent,
   *  comparisons should always compare the return value of join()
   *  with this constant.
   */
  static const int busy;

protected:

  /** terminates the calling thread, in a similar way that exit()
   *  terminates the calling process. This method does not return.
   */
  static void thread_exit();

  /** returns the thread ID of the calling thread. For a running thread,
   *  this->threadID() and this->self() should return the same value,
   *  but self() is more robust and should be preferred.
   *  On certain platforms like OSF/1, a thread ID
   *  contains a pointer to a structure. Therefore, thread IDs should
   *  never be compared directly, but always using the equal() method
   *  provided in this class.
   *  @return thread ID of the calling thread.
   */
  static unsigned long self();

private:

  /** this method implements the thread that is run by calling the start
   *  method of the OFThread object. Defined as abstract method in OFThread.
   *  It is recommended that derived classes implementing this method
   *  do not rely on attributes of the OFThread object because this might
   *  result in undefined behaviour if another thread deletes or restarts the
   *  OFThread object before the thread has terminated.
   */
  virtual void run() = 0;

#ifdef HAVE_WINDOWS_H
  /** thread handle (Win32 only) */
  OFuintptr_t theThreadHandle;
#endif

  /** thread identifier */
#ifdef HAVE_POINTER_TYPE_PTHREAD_T
  void *theThread;
#else
  unsigned long theThread;
#endif

  /** unimplemented private copy constructor */
  OFThread(const OFThread& arg);

  /** unimplemented private assignment operator */
  OFThread& operator=(const OFThread& arg);

  /** thread stub must be friend to call run() */
#ifdef HAVE_WINDOWS_H
  friend unsigned int __stdcall thread_stub(void *arg);
#else
  friend DCMTK_OFSTD_EXPORT void *thread_stub(void *arg);
#endif
};


/** provides an operating system independent abstraction for thread
  * specific data. An instance of this class manages a key which is global
  * to all threads in the process but locates data specific to each thread.
  * Each thread can set a different value once the object has been created.
  * Upon creation, the value NULL is assigned for all threads. This class
  * does not perform any memory management on the objects pointed to.
  * Threads must ensure on their own that the data pointed to by the thread
  * specific data key is freed upon termination of the thread.
  */
class DCMTK_OFSTD_EXPORT OFThreadSpecificData
{
public:

  /** default constructor */
  OFThreadSpecificData();

  /** destructor. Deletes all thread specific key values (pointers), but
   *  not the objects pointed to.
   */
  ~OFThreadSpecificData();

  /** checks whether creation of the object was successful.
   *  @return OFTrue if the object was successfully created, OFFalse otherwise.
   */
  OFBool initialized() const;

  /** sets the thread specific value for this object. No attempt is made to
   *  automatically delete the object pointed to at the termination of the
   *  thread.
   *  @param value new pointer to thread-specific data for this object.
   *  @return 0 if successful, an error code otherwise.
   */
  int set(void *value);

  /** retrieves the thread specific value for this object. If no call to set()
   *  has been made for the calling thread before, NULL is returned.
   *  @param value new pointer to thread-specific data for this object
   *    returned in this parameter.
   *  @return 0 if successful, an error code otherwise.
   */
  int get(void *&value);

  /** converts any of the error codes returned by the methods of this class
   *  into a textual description, which is written into the string object.
   *  @param description string object into which the error description is written.
   *  @param code error code
   */
  static void errorstr(OFString& description, int code);

private:

  /** thread specific data key resource */
  volatile void *theKey;

  /** unimplemented private copy constructor */
  OFThreadSpecificData(const OFThreadSpecificData& arg);

  /** unimplemented private assignment operator */
  OFThreadSpecificData& operator=(const OFThreadSpecificData& arg);
};

/** provides an operating system independent abstraction for semaphores.
 *  A semaphore is a non-negative integer counter that is used
 *  to coordinate access to resources. The initial and maximum value of the counter
 *  is defined by the constructor. Each call to wait() decreases
 *  the counter by one and each call to post() increases the count by one.
 *  If a thread calls wait() while the counter is zero, the thread
 *  is blocked until another thread has increased the counter using post().
 */
class DCMTK_OFSTD_EXPORT OFSemaphore
{
public:

  /** constructor.
   *  @param numResources is the initial and maximum value for the semaphore.
   */
  OFSemaphore(unsigned int numResources);

  /** destructor */
  ~OFSemaphore();

  /** checks whether creation of the object was successful.
   *  @return OFTrue if the object was successfully created, OFFalse otherwise.
   */
  OFBool initialized() const;

  /** blocks the calling thread until the semaphore counter is greater than zero
   *  and then atomically decreases the counter.
   *  @return 0 upon success, an error code otherwise.
   */
  int wait();

  /** atomically decreases the counter if it is larger than zero,
   *  otherwise returns OFSemaphore::busy.
   *  @return 0 upon success, OFSemaphore::busy if the semaphore is already locked,
   *    an error code otherwise.
   */
  int trywait();

  /** atomically increases the counter. If threads are blocked on the semaphore,
   *  at least one of them is unblocked.
   *  @return 0 upon success, an error code otherwise.
   */
  int post();

  /** converts any of the error codes returned by the methods of this class
   *  into a textual description, which is written into the string object.
   *  @param description string object into which the error description is written.
   *  @param code error code
   */
  static void errorstr(OFString& description, int code);

  /** this constant is returned by the trywait() method if the semaphore
   *  is already locked. Since this value is operating system dependent,
   *  comparisons should always compare the return value of trywait()
   *  with this constant.
   */
  static const int busy;

private:
  /** semaphore resource */
  volatile void * theSemaphore;

  /** unimplemented private copy constructor */
  OFSemaphore(const OFSemaphore& arg);

  /** unimplemented private assignment operator */
  OFSemaphore& operator=(const OFSemaphore& arg);
};

/** provides an operating system independent abstraction for mutexes
 *  (mutual exclusion locks).
 *  Mutexes prevent multiple threads from simultaneously executing critical
 *  sections of code which access shared data. A successful call for a
 *  mutex lock by way of lock() will cause another thread that is also
 *  trying to lock the same mutex to block until the owner thread unlocks
 *  it by way of unlock().
 */
class DCMTK_OFSTD_EXPORT OFMutex
{
public:

  /** default constructor */
  OFMutex();

  /** destructor */
  ~OFMutex();

  /** checks whether creation of the object was successful.
   *  @return OFTrue if the object was successfully created, OFFalse otherwise.
   */
  OFBool initialized() const;

  /** locks the mutex object. If the mutex is already locked, the calling
   *  thread blocks until the mutex is freed; If the current owner of a
   *  mutex tries to relock the mutex, it may or may not result in
   *  deadlock.
   *  @return 0 upon success, an error code otherwise.
   */
  int lock();

  /** tries to lock the mutex object. If the mutex is already locked,
   *  returns OFMutex::busy.
   *  @return 0 upon success, OFMutex::busy if the mutex is already locked,
   *    an error code otherwise.
   */
  int trylock();

  /** releases the lock on the mutex object. The mutex must be locked and
   *  the calling thread must be the owner of the lock, otherwise the
   *  behaviour is undefined. If there are threads blocked on the mutex
   *  when unlock() is called, one of them is unblocked and receives
   *  ownership of the mutex lock.
   *  @return 0 upon success, an error code otherwise.
   */
  int unlock();

  /** converts any of the error codes returned by the methods of this class
   *  into a textual description, which is written into the string object.
   *  @param description string object into which the error description is written.
   *  @param code error code
   */
  static void errorstr(OFString& description, int code);

  /** this constant is returned by the trylock() method if the mutex
   *  is already locked. Since this value is operating system dependent,
   *  comparisons should always compare the return value of trylock()
   *  with this constant.
   */
  static const int busy;

private:
  /** mutex resource */
  volatile void * theMutex;

  /** unimplemented private copy constructor */
  OFMutex(const OFMutex& arg);

  /** unimplemented private assignment operator */
  OFMutex& operator=(const OFMutex& arg);
};


/** provides an operating system independent abstraction for read/write
 *  locks. Many threads can have simultaneous read-only access to data,
 *  while only one thread can have write access at any given time.
 *  Multiple read access with single write access is controlled by
 *  read/write locks, which are generally used to protect data that is
 *  frequently searched.
 */
class DCMTK_OFSTD_EXPORT OFReadWriteLock
{
public:

  /** default constructor */
  OFReadWriteLock();

  /** destructor */
  ~OFReadWriteLock();

  /** checks whether creation of the object was successful.
   *  @return OFTrue if the object was successfully created, OFFalse otherwise.
   */
  OFBool initialized() const;

  /** gets a read lock. If the read/write lock is currently locked for
   *  writing, the calling thread blocks until the write lock is freed.
   *  Multiple threads may simultaneously hold a read lock on a read/write
   *  lock.
   *  @return 0 upon success, an error code otherwise.
   */
  int rdlock();

  /** gets a write lock. If the read/write lock is currently locked for
   *  reading or writing, the calling thread blocks until all the read and
   *  write locks are freed. At any given time, only one thread may have a
   *  write lock on a read/write lock.
   *  @return 0 upon success, an error code otherwise.
   */
  int wrlock();

  /** trys to get a read lock. If the read/write lock is locked for
   *  writing, returns OFReadWriteLock::busy.
   *  @return 0 upon success, OFReadWriteLock::busy if the read/write lock
   *    is already locked, an error code otherwise.
   */
  int tryrdlock();

  /** trys to get a write lock. If the read/write lock is currently locked
   *  for reading or writing, returns OFReadWriteLock::busy.
   *  @return 0 upon success, OFReadWriteLock::busy if the read/write lock
   *    is already locked, an error code otherwise.
   */
  int trywrlock();

  /** unlocks the read lock. The read/write lock must be locked and
   *  the calling thread must be the owner of the lock, otherwise the
   *  behaviour is undefined. One of the other threads that is waiting for
   *  the read/write lock to be freed will be unblocked, provided there are
   *  other waiting threads.
   *  @return 0 upon success, an error code otherwise.
   */
  int rdunlock();

  /** unlocks the write lock. The read/write lock must be locked and
   *  the calling thread must be the owner of the lock, otherwise the
   *  behaviour is undefined. One of the other threads that is waiting for
   *  the read/write lock to be freed will be unblocked, provided there are
   *  other waiting threads.
   *  @return 0 upon success, an error code otherwise.
   */
  int wrunlock();

  /** converts any of the error codes returned by the methods of this class
   *  into a textual description, which is written into the string object.
   *  @param description string object into which the error description is written.
   *  @param code error code
   */
  static void errorstr(OFString& description, int code);

  /** this constant is returned by the tryrdlock() and trywrlock() methods
   *  if the read/write lock is already locked. Since this value is operating
   *  system dependent, comparisons should always compare the return value
   *  of tryrdlock() and trywrlock() with this constant.
   */
  static const int busy;

private:
  /** read/write lock resource */
  volatile void * theLock;

  /** unimplemented private copy constructor */
  OFReadWriteLock(const OFReadWriteLock& arg);

  /** unimplemented private assignment operator */
  OFReadWriteLock& operator=(const OFReadWriteLock& arg);
};

/** This class aims to provide an easy way for making sure OFReadWriteLocks
 *  are unlocked in an exception safe way. You can just create a local
 *  instance of this class and lock the OFReadWriteLock through it. When it
 *  is destructed it will make sure that the lock is unlocked if necessary.
 */
class DCMTK_OFSTD_EXPORT OFReadWriteLocker {
public:
  /** constructor
   *  @param lock the lock to associate this instance with
   */
  OFReadWriteLocker(OFReadWriteLock& lock);

  /** destructor, unlocks the mutex if necessary */
  ~OFReadWriteLocker();

  /** lock the lock for reading
   *  @return 0 upon success, an error code otherwise
   *  @see OFReadWriteLock::rdlock
   */
  int rdlock();

  /** lock the lock for writing
   *  @return 0 upon success, an error code otherwise
   *  @see OFReadWriteLock::wrlock
   */
  int wrlock();

  /** try to lock the lock for reading
   *  @return 0 upon success, OFReadWriteLock::busy if the read/write lock
   *    is already locked, an error code otherwise
   *  @see OFReadWriteLock::tryrdlock
   */
  int tryrdlock();

  /** try to lock the lock for writing
   *  @return 0 upon success, OFReadWriteLock::busy if the read/write lock
   *    is already locked, an error code otherwise
   *  @see OFReadWriteLock::trywrlock
   */
  int trywrlock();

  /** unlock the read/write lock
   *  @return 0 upon success, an error code otherwise
   *  @see OFReadWriteLock::unlock
   */
  int unlock();

private:
  /** the lock on which we are operating */
  OFReadWriteLock& theLock;

  /** did we successfully lock the lock? */
  OFBool locked;

  /** did we acquire a write lock? */
  OFBool isWriteLock;

  /** unimplemented private copy constructor */
  OFReadWriteLocker(const OFReadWriteLocker& arg);

  /** unimplemented private assignment operator */
  OFReadWriteLocker& operator=(const OFReadWriteLocker& arg);
};

#endif
