//==================================================================================================
// S t a n d a r d                                                                        Interface
// T h r e a d
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This 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 (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to manipulate threads. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/thread.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guStandardThread
#define guStandardThread

// Headers //---------------------------------------------------------------------------------------
#include <cstdlib> /*INCLUDE*/
#include <vector> /*INCLUDE*/
#include <bpp/standard/keyword.hpp> /*INCLUDE*/

#ifndef USE_FORK_FOR_THREAD
 #ifdef SUSPEND_HEADER
  #include SUSPEND_HEADER /*INCLUDE*/
 #endif

 #ifdef THREAD_HEADER
  #include THREAD_HEADER /*INCLUDE*/
 #endif

 #ifdef THREAD_ALLOCATOR_HEADER
  #include THREAD_ALLOCATOR_HEADER /*INCLUDE*/
 #endif
#else
 #undef CREATE_THREAD
 #undef EXIT_THREAD
 #undef SUSPEND_THREAD
 #undef THREAD_TYPE
 #undef MUTEX_TYPE
 #undef CREATE_MUTEX
 #undef DESTROY_MUTEX
 #undef LOCK_MUTEX
 #undef RELEASE_MUTEX
 #undef TRY_LOCK_MUTEX
#endif

// Portability //-----------------------------------------------------------------------------------
#ifndef CREATE_THREAD
 #include <unistd.h> /*INCLUDE*/

 #define CREATE_THREAD(prThread,prFunction,prData)                             \
  switch (fork()) {                                                            \
   case -1:                                                                    \
    (prThread)=0;                                                              \
    break;                                                                     \
   case 0:                                                                     \
    (prThread)=getpid(); ((void * (*)(void *))(prFunction))((void *)(prData)); \
    EXIT_THREAD;                                                               \
    break;                                                                     \
   default: (prThread)=getpid();                                               \
  }                                                                            \

 #define EXIT_THREAD (_exit(EXIT_SUCCESS))
 #define SUSPEND_THREAD(prDuration) (sleep(prDuration))
 #define THREAD_TYPE pid_t
 #define MUTEX_TYPE void *
#endif

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef STANDARD_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardThread
#define private_area standardThread_private

namespace public_area  { /*NAMESPACE*/ using namespace standardKeyword; }
namespace private_area { using namespace public_area; }

// Initialization //--------------------------------------------------------------------------------

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------------Classes
 class clMutex;
 class clInitializerMutex;
 //-----------------------------------------------------------------------------------Variable Types
 /*TYPE*/ /* Thread. */
 typedef THREAD_TYPE tyThread;
 //-----------------------------------------------------------------------------------Constant Types
 typedef const clMutex            ctMutex;
 typedef const clInitializerMutex ctInitializerMutex;
 typedef const tyThread           tcThread;
}

namespace private_area {
 typedef BOOLEAN_TYPE  tyBoolean;
 typedef CARDINAL_TYPE tyCardinal;
 typedef MUTEX_TYPE    tyMutex;
 typedef void *        tyPointer;
 typedef void          (*tyFunction)(void);

 typedef void (*tySendErrorFunction)(void);
}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 function void createThread(private_area::tyFunction,private_area::tyPointer);
 inline   void exitThread(void);
 inline   void suspendThread(private_area::tyCardinal);
}

namespace private_area { testing_mode ( function void test(void); ) }

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

commentary (
 /*ERROR*/ extern_error erCreateThread; /* Can not create a new thread. */
 /*ERROR*/ extern_error erMutexNotAvailable; /* The mutex facility is not available. */
 /*ERROR*/ extern_error erMutexUnlocked; /* The mutex is unlocked. */
)

// Constants & Variables //-------------------------------------------------------------------------
extern_dynamic_constant(private,clMutex,goDisplayMutex,?);
extern_dynamic_constant(private,clMutex,goMallocMutex,?);

namespace private_area {
 extern function tySendErrorFunction sendCreateThreadError;
 extern function tySendErrorFunction sendMutexNotAvailableError;
 extern function tySendErrorFunction sendMutexUnlockedError;
}

// M u t e x  Interface //--------------------------------------------------------------------------
namespace public_area {
 /*CLASS clMutex */
 /* Represents a mutex (mutual exclusion for threads). It is used to lock the use of a resource
    from other threads. */
 class clMutex {
  //-----------------------------------------------------------------------------------------Private
  private_property private_area::tyBoolean atLocked;
  private_property private_area::tyMutex   atMutex;

  private_property constructor clMutex(ctMutex &);
  private_property clMutex & operator = (ctMutex &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clMutex(void);
  public_property virtual destructor clMutex(void);

  public_property void                    lock(void);
  public_property void                    release(void);
  public_property private_area::tyBoolean tryLock(void);
 };
}

// I n i t i a l i z e r M u t e x  Interface //----------------------------------------------------
namespace public_area {
 /*CLASS clInitializerMutex */
 /* Represents a mutex that protects the initialization of a variable. */
 class clInitializerMutex : public clMutex {
  //-----------------------------------------------------------------------------------------Private
  private_property private_area::tyBoolean atInitialized;

  private_property constructor clInitializerMutex(ctInitializerMutex &);
  private_property clInitializerMutex & operator = (ctInitializerMutex &);

  private_property void lock(void);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clInitializerMutex(void);
  public_property virtual destructor clInitializerMutex(void);

  public_property void                    release(void);
  public_property private_area::tyBoolean tryLock(void);
 };
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area {
 //---------------------------------------------------------------------------------------ExitThread
 /*FUNCTION*/ /* Exits the current thread. */
 inline void exitThread(void) { EXIT_THREAD; }
 //------------------------------------------------------------------------------------SuspendThread
 /*FUNCTION*/ /* Suspends the current thread for a given number of seconds. */
 inline void suspendThread(private_area::tyCardinal agDuration) { SUSPEND_THREAD(agDuration); }
}

namespace private_area {}

// M u t e x  Inline //-----------------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clMutex */ /* Builds a mutex. */
 inline clMutex::clMutex(void) : atLocked(false),atMutex() {
  #if defined(THREAD_SAFETY) and defined(CREATE_MUTEX)
   CREATE_MUTEX(atMutex);
  #endif
 }
 //------------------------------------------------------------------------------------------Release
 /*METHOD clMutex */ /* Releases the mutex. */
 inline void clMutex::release(void) {
  #if defined(THREAD_SAFETY) and defined(RELEASE_MUTEX)
   if (not atLocked)
   { if (private_area::sendMutexUnlockedError!=nil) private_area::sendMutexUnlockedError(); }
   else {
    atLocked=false;
    RELEASE_MUTEX(atMutex);
   }
  #else
   if (private_area::sendMutexNotAvailableError!=nil) private_area::sendMutexNotAvailableError();
  #endif
 }
 //---------------------------------------------------------------------------------------------Lock
 /*METHOD clMutex */
 /* Locks the mutex. If it is already locked, the thread waits for its release to lock it again. */
 inline void clMutex::lock(void) {
  #if defined(THREAD_SAFETY) and defined(LOCK_MUTEX)
   LOCK_MUTEX(atMutex);
   atLocked=true;
  #else
   if (private_area::sendMutexNotAvailableError!=nil) private_area::sendMutexNotAvailableError();
  #endif
 }
 //------------------------------------------------------------------------------------------TryLock
 /*METHOD clMutex */
 /* Attempts to lock the mutex. If it is already locked, the thread does not wait for its release
    and goes on. */
 inline private_area::tyBoolean clMutex::tryLock(void) {
  #if defined(THREAD_SAFETY) and defined(TRY_LOCK_MUTEX)
   if (TRY_LOCK_MUTEX(atMutex)) {
    atLocked=true;
    return (true);
   }
   else return (false);
  #else
   if (private_area::sendMutexNotAvailableError!=nil) private_area::sendMutexNotAvailableError();
   return (false);
  #endif
 }
}

// I n i t i a l i z e r M u t e x  Inline //-------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------------------Constructor
 /*METHOD clInitializerMutex */ /* Builds a mutex. */
 inline clInitializerMutex::clInitializerMutex(void) : clMutex(),atInitialized(false) {}
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clInitializerMutex */ /* Destructs the mutex. */
 inline clInitializerMutex::~clInitializerMutex(void) {}
 //------------------------------------------------------------------------------------------Release
 /*METHOD clInitializerMutex */ /* Releases the mutex. */
 inline void clInitializerMutex::release(void) {
  atInitialized=true;
  safe_yes ( clMutex::release(); )
 }
 //------------------------------------------------------------------------------------------TryLock
 /*METHOD clInitializerMutex */
 /* Attempts to lock the mutex. If it is successful, that means the variable protected
    by the mutex needs to be initialized. */
 inline private_area::tyBoolean clInitializerMutex::tryLock(void) {
  safe_yes (
   if (not atInitialized) {
    clMutex::lock();

    if (atInitialized) {
     clMutex::release();
     return (false);
    }

    return (true);
   }

   return (false);
  )

  safe_no ( return (not atInitialized); )
 }
}

// Namespace End //---------------------------------------------------------------------------------
}
#if defined(THREAD_SAFETY) and defined(NEED_SAFE_STL_ALLOCATOR)

// T h r e a d S a f e A l l o c a t o r  Interface //----------------------------------------------
template <class prType> class thread_safe_allocator {
 //--------------------------------------------------------------------------------------------Types
 private_property typedef std_allocator<prType> tyAllocator;

 public_property typedef typename tyAllocator::difference_type difference_type;
 public_property typedef typename tyAllocator::pointer         pointer;
 public_property typedef typename tyAllocator::reference       reference;
 public_property typedef typename tyAllocator::size_type       size_type;
 public_property typedef typename tyAllocator::value_type      value_type;

 public_property typedef typename tyAllocator::const_pointer   const_pointer;
 public_property typedef typename tyAllocator::const_reference const_reference;

 public: template <class prType2> struct rebind
 { public_property typedef thread_safe_allocator<prType2> other; };
 //------------------------------------------------------------------------------------------Private
 private_property tyAllocator * atAllocator;
 //-------------------------------------------------------------------------------------------Public
 public_property thread_safe_allocator(void);
 public_property thread_safe_allocator(const thread_safe_allocator<prType> &);
 public_property ~thread_safe_allocator(void);

 public:
 template <class prType2> thread_safe_allocator(const thread_safe_allocator<prType2> &);

 public_property pointer       address(reference) const;
 public_property const_pointer address(const_reference) const;

 public_property pointer   allocate(size_type,const void * = 0);
 public_property void      construct(pointer,const_reference);
 public_property void      deallocate(pointer,size_type);
 public_property void      destroy(pointer);
 public_property size_type max_size(void) const;
};

// T h r e a d S a f e A l l o c a t o r  Inline //-------------------------------------------------
//---------------------------------------------------------------------------------------Constructor
template <class prType> inline thread_safe_allocator<prType>::thread_safe_allocator(void) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator=reinterpret_cast<tyAllocator *>(malloc(sizeof(tyAllocator)));
 new (atAllocator) tyAllocator;
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//---------------------------------------------------------------------------------------Constructor
template <class prType> inline thread_safe_allocator<prType>::
thread_safe_allocator(const thread_safe_allocator<prType> & agAllocator) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator=reinterpret_cast<tyAllocator *>(malloc(sizeof(tyAllocator)));
 new (atAllocator) tyAllocator;
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//---------------------------------------------------------------------------------------Constructor
template <class prType> template <class prType2> inline thread_safe_allocator<prType>::
thread_safe_allocator(const thread_safe_allocator<prType2> & agAllocator) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator=reinterpret_cast<tyAllocator *>(malloc(sizeof(tyAllocator)));
 new (atAllocator) tyAllocator;
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//----------------------------------------------------------------------------------------Destructor
template <class prType> inline thread_safe_allocator<prType>::~thread_safe_allocator(void) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator->~allocator<prType>();
 free(atAllocator);
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//-------------------------------------------------------------------------------------------Address
template <class prType> inline thread_safe_allocator<prType>::pointer
thread_safe_allocator<prType>::address(reference agReference) const
{ return (atAllocator->address(agReference)); }
//-------------------------------------------------------------------------------------------Address
template <class prType> inline thread_safe_allocator<prType>::const_pointer
thread_safe_allocator<prType>::address(const_reference agReference) const
{ return (atAllocator->address(agReference)); }
//------------------------------------------------------------------------------------------Allocate
template <class prType> inline thread_safe_allocator<prType>::pointer
thread_safe_allocator<prType>::allocate(size_type agSize,const void * agPointer) {
 pointer lcPointer;

 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 lcPointer=atAllocator->allocate(agSize,agPointer);
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
 return (lcPointer);
}
//-----------------------------------------------------------------------------------------Construct
template <class prType> inline void thread_safe_allocator<prType>::
construct(pointer agPointer,const_reference agReference) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator->construct(agPointer,agReference);
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//----------------------------------------------------------------------------------------Deallocate
template <class prType> inline void thread_safe_allocator<prType>::
deallocate(pointer agPointer,size_type agSize) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator->deallocate(agPointer,agSize);
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//-------------------------------------------------------------------------------------------Destroy
template <class prType> inline void thread_safe_allocator<prType>::destroy(pointer agPointer) {
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
 atAllocator->destroy(agPointer);
 if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
}
//------------------------------------------------------------------------------------------Max_size
template <class prType> inline thread_safe_allocator<prType>::size_type
thread_safe_allocator<prType>::max_size(void) const { return (atAllocator->max_size()); }

// Functions Inline //------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------Operator ==
template <class prType1,class prType2>
inline bool operator == (const thread_safe_allocator<prType1> & agAllocator1,
                         const thread_safe_allocator<prType2> & agAllocator2)
{ return (*(agAllocator1.atAllocator)==*(agAllocator2.atAllocator)); }
//---------------------------------------------------------------------------------------Operator !=
template <class prType1,class prType2>
inline bool operator != (const thread_safe_allocator<prType1> & agAllocator1,
                         const thread_safe_allocator<prType2> & agAllocator2)
{ return (*(agAllocator1.atAllocator)!=*(agAllocator2.atAllocator)); }

// End //------------------------------------------------------------------------------------------
#endif
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// S t a n d a r d                                                                   Implementation
// T h r e a d
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This 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 (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "standard/thread.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define STANDARD_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/standard/thread.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  standardThread
#define private_area standardThread_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

// Initialization //--------------------------------------------------------------------------------

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
dynamic_constant(clMutex,goDisplayMutex);
dynamic_constant(clMutex,goMallocMutex);

namespace private_area {
 function tySendErrorFunction sendCreateThreadError      = nil;
 function tySendErrorFunction sendMutexNotAvailableError = nil;
 function tySendErrorFunction sendMutexUnlockedError     = nil;
}

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //-------------------------------------------------------------------------------------CreateThread
 /*FUNCTION*/
 /* Creates a new thread and executes a given function in it. The second parameter is a pointer
    to the data given to the function. */
 function void createThread(private_area::tyFunction agFunction,private_area::tyPointer agData) {
  tyThread lcThread;

  CREATE_THREAD(lcThread,agFunction,agData);

  if (lcThread==0 and private_area::sendCreateThreadError!=nil)
   private_area::sendCreateThreadError();
 }
}

namespace private_area {}

// M u t e x  Implementation //---------------------------------------------------------------------
namespace public_area {
 //---------------------------------------------------------------------------------------Destructor
 /*METHOD clMutex */ /* Destructs the mutex. */
 property clMutex::~clMutex(void) {
  #if defined(THREAD_SAFETY) and defined(DESTROY_MUTEX)
   if (atLocked) release();
   DESTROY_MUTEX(atMutex);
  #endif
 }
}

// Namespace End //---------------------------------------------------------------------------------
}

// N e w  &  D e l e t e  Implementation //---------------------------------------------------------
#if defined(THREAD_SAFETY) and defined(NEED_SAFE_PLAIN_NEW)
 //---------------------------------------------------------------------------------Single Plain New
 void * operator new(std::size_t agSize) throw(std::bad_alloc) {
  void * lcPointer;

  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  lcPointer=malloc(agSize);
  if (lcPointer==nil) throw std::bad_alloc();
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
  return (lcPointer);
 }
 //-------------------------------------------------------------------------------Multiple Plain New
 void * operator new[](std::size_t agSize) throw(std::bad_alloc) {
  void * lcPointer;

  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  lcPointer=malloc(agSize);
  if (lcPointer==nil) throw std::bad_alloc();
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
  return (lcPointer);
 }
 //------------------------------------------------------------------------------Single Plain Delete
 void operator delete(void * agPointer) throw() {
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  free(agPointer);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
 }
 //----------------------------------------------------------------------------Multiple Plain Delete
 void operator delete[](void * agPointer) throw() {
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  free(agPointer);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
 }
#endif

#if defined(THREAD_SAFETY) and defined(NEED_SAFE_NOTHROW_NEW)
 //-------------------------------------------------------------------------------Single Nothrow New
 void * operator new(std::size_t agSize,const std::nothrow_t &) throw() {
  void * lcPointer;

  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  lcPointer=malloc(agSize);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
  return (lcPointer);
 }
 //-----------------------------------------------------------------------------Multiple Nothrow New
 void * operator new[](std::size_t agSize,const std::nothrow_t &) throw() {
  void * lcPointer;

  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  lcPointer=malloc(agSize);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
  return (lcPointer);
 }
 //----------------------------------------------------------------------------Single Nothrow Delete
 void operator delete(void * agPointer,const std::nothrow_t &) throw() {
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  free(agPointer);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
 }
 //--------------------------------------------------------------------------Multiple Nothrow Delete
 void operator delete[](void * agPointer,const std::nothrow_t &) throw() {
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->lock();
  free(agPointer);
  if (bpp::private_area::goMallocMutex!=nil) bpp::private_area::goMallocMutex->release();
 }
#endif

// End //-------------------------------------------------------------------------------------------