/*---------------------------------------------------------------------------*
 *  ptrd.h  *
 *                                                                           *
 *  Copyright 2007, 2008 Nuance Communciations, Inc.                               *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the 'License');          *
 *  you may not use this file except in compliance with the License.         *
 *                                                                           *
 *  You may obtain a copy of the License at                                  *
 *      http://www.apache.org/licenses/LICENSE-2.0                           *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an 'AS IS' BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * 
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 *---------------------------------------------------------------------------*/

#ifndef PTRD_H
#define PTRD_H




#ifdef USE_THREAD

#include "PortPrefix.h"
#include "ptypes.h"
#include "ESR_ReturnCode.h"

#define STACKSIZE_S2G_SINKER      12*1024
#define STACKSIZE_S2G_RECOGNIZER  25*1024
#define STACKSIZE_DEFAULT         18*1024

#ifdef _WIN32
typedef unsigned int PTHREAD_ID;
#define PtrdGetCurrentThreadId GetCurrentThreadId
#elif defined(POSIX)

#if defined(__vxworks) && !defined(REAL_PTHREADS)
#include "pthread_vx.h"
#else
#include <pthread.h>

#ifndef _POSIX_THREADS
#error "Thread is not defined!"
#endif
#endif /* #if defined(__vxworks) && !defined(REAL_PTHREADS) */

typedef pthread_t PTHREAD_ID;
#define PtrdGetCurrentThreadId pthread_self
#else
#error Portable Synchronization not defined for this OS!
#endif /* os dependant basic types */

/**
 * @addtogroup PtrdModule PThread API functions
 * Library for basic thread and monitor functionality to ensure portability.
 * Call PtrdInit() to initialize and PtrdShutdown() to shutdown module.
 *
 * Every thread has a priority. Threads with higher priority are executed in preference
 * to threads with lower priority. When code running in some thread creates a new Thread
 * object, the new thread has its priority initially set equal to the priority of the creating
 * thread.
 *
 *
 * @{
 */

/** Typedef */
typedef struct PtrdMonitor_t PtrdMonitor;
/** Typedef */
typedef struct PtrdMutex_t PtrdMutex;
/** Typedef */
typedef struct PtrdSemaphore_t PtrdSemaphore;
/** Typedef */
typedef struct PtrdThread_t PtrdThread;


/**
 * Blocks the current thread for the specified amount of time.
 *
 * @param sleepTimeMs number of milliseconds to sleep.  A value of 0 is
 * equivalent to a thread yield.
 *
 * @return ESR_SUCCESS if success, or something else to indicate a failure.
 */
PORTABLE_API ESR_ReturnCode PtrdSleep(asr_uint32_t sleepTimeMs);

/**
 * Creates a thread monitor.  Thread monitors can be locked, unlocked, can be
 * waited on and can be notified.  Monitors implement so-called recursive
 * locking, meaning that a thread owning the monitor can call lock without
 * blocking and will have to call unlock() as many times as lock() was called.
 *
 * @param  monitor  Handle to the created monitor
 *
 * @return ESR_SUCCESS if succes, or something else to indicate a failure.  In
 * particular, it will return ESR_INVALID_STATE if the threading API is not
 * properly initialized.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorCreate(PtrdMonitor **monitor);

/**
 * Destroys a monitor.
 *
 * @param  monitor  Handle to the monitor to destroy
 *
 * @return ESR_SUCCESS if success; ESR_INVALID_STATE if this function is called after the thread
 * library is shutdown, or cannot lock on mutex; ESR_INVALID_ARGUMENT if monitor is null
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorDestroy(PtrdMonitor *monitor);

/**
 * Locks a monitor.
 *
 * @param  monitor  Handle to the monitor to lock
 * @param  fname Filename of code requesting a lock
 * @param  line Line of code requesting a lock
 *
 * @return ESR_SUCCESS if success; ESR_INVALID_ARGUMENT if monitor is null; ESR_FATAL_ERROR if waiting on the mutex failed
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorLockWithLine(PtrdMonitor *monitor, const LCHAR *fname, int line);
/**
 * Locks a monitor.
 *
 * @param  monitor  Handle to the monitor to lock
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
#define PtrdMonitorLock(monitor) PtrdMonitorLockWithLine(monitor, L(__FILE__), __LINE__)

/**
 * Unlock a Monitor
 *
 * @param  monitor  Handle to the monitor to unlock
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.  In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the monitor.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorUnlock(PtrdMonitor *monitor);

/**
 * Causes current thread to wait until another thread invokes the
 * <code>PtrdMonitorNotify()</code> method or the
 * <code>PtrdMonitorNotifyAll()</code> method for this monitor.
 *
 * <p>
 *
 * The current thread must own this monitor. The thread releases ownership of
 * this monitor and waits until another thread notifies threads waiting on
 * this object's monitor to wake up either through a call to the
 * <code>PtrdMonitorNotify</code> method or the
 * <code>PtrdMonitorNotifyAll</code> method. The thread then waits until it
 * can re-obtain ownership of the monitor and resumes execution.
 *
 * @param monitor The monitor on which to wait.
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.  In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the monitor.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorWait(PtrdMonitor *monitor);


/**
 * Causes current thread to wait until either another thread invokes the
 * <code>PtrdMonitorNotify()</code> method or the
 * <code>PtrdMonitorNotifyAll()</code> method for this monitor, or a specified
 * amount of time has elapsed.
 *
 * @param monitor The monitor on which to wait.
 *
 * @param timeoutMs The amount of time (in millisecs) to wait for
 * notification.
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.  In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the monitor, or ESR_TIMED_OUT if the timeout expired
 * without a notification.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorWaitTimeout(PtrdMonitor *monitor,
    asr_uint32_t timeoutMs);
    
/**
 * Wakes up a single thread that is waiting on this monitor. If more than one
 * thread are waiting on this object, one of them is arbitrarily chosen to be
 * awakened. A thread waits on the monitor by calling
 * <code>PtrdMonitorWait</code> or <code>PtrdMonitorWaitTimeout</code>.
 *
 * <p>
 *
 * The awakened thread will not be able to proceed until the current thread
 * relinquishes the lock on this object. The awakened thread will compete in
 * the usual manner with any other threads that might be actively competing to
 * synchronize on this object; for example, the awakened thread enjoys no
 * reliable privilege or disadvantage in being the next thread to lock this
 * monitor.
 *
 * <p>
 *
 * This method should only be called by a thread that is the owner of this
 * monitor.
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.  In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the monitor, or ESR_TIMED_OUT if the timeout expired
 * without a notification.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorNotify(PtrdMonitor *monitor);

/**
 * Wakes up all threads that are waiting on this monitor. A thread waits on
 * a monitor by calling <code>PtrdMonitorWait</code> or
 * <code>PtrdMonitorWaitTimeout</code>
 *
 * <p>
 *
 * The awakened threads will not be able to proceed until the current thread
 * relinquishes the monitor. The awakened threads will compete in the usual
 * manner with any other threads that might be actively competing to
 * synchronize on this monitor; for example, the awakened threads enjoy no
 * reliable privilege or disadvantage in being the next thread to lock this
 * object.
 *
 * <p>
 *
 * This method should only be called by a thread that is the owner of this
 * object's monitor.
 *
 * @param monitor The monitor on which to wait.
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.  In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the monitor.
 */
PORTABLE_API ESR_ReturnCode PtrdMonitorNotifyAll(PtrdMonitor *monitor);

/**
 * Creates a thread mutex.  Thread mutexes are similar to thread monitors
 * except that they do not support wait and notify mechanism and require less
 * resources from the OS.  In situations where this mechanism is not required,
 * using mutexes instead of monitors is preferable. Mutexes implement
 * so-called recursive locking, meaning that a thread owning the mutex can
 * call lock without blocking and will have to call unlock() as many times as
 * lock() was called.
 *
 * @param  mutex  Handle to the created mutex
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
PORTABLE_API ESR_ReturnCode PtrdMutexCreate(PtrdMutex **mutex);

/**
 * Destroys a mutex.
 *
 * @param  mutex  Handle to the mutex to destroy
 *
 * @return        ESR_ReturnCode 0 on success
 */
PORTABLE_API ESR_ReturnCode PtrdMutexDestroy(PtrdMutex *mutex);

/**
 * Lock a mutex
 *
 * @param  mutex  Handle to the mutex to lock
 * @param  fname Filename of code requesting a lock
 * @param  line Line of code requesting a lock
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
PORTABLE_API ESR_ReturnCode PtrdMutexLockWithLine(PtrdMutex *mutex, const LCHAR *fname, int line);
/**
 * Lock a mutex
 *
 * @param  mutex  Handle to the mutex to lock
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
#define PtrdMutexLock(mutex) PtrdMutexLockWithLine(mutex, L(__FILE__), __LINE__)

/**
 * Unlock a Mutex
 *
 * @param  mutex  Handle to the mutex to unlock
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure. In particular, it will return ESR_INVALID_STATE if the current
 * thread does not hold the mutex.
 */
PORTABLE_API ESR_ReturnCode PtrdMutexUnlock(PtrdMutex *mutex);


/**
 * Creates a thread semaphore.
 *
 * @param  semaphore  Handle to the created semaphore.
 * @param  initValue  Initial semaphore value
 * @param  maxValue   Maximum semaphore value
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
PORTABLE_API ESR_ReturnCode PtrdSemaphoreCreate(unsigned int initValue,
    unsigned int maxValue,
    PtrdSemaphore **semaphore);
    
/**
 * Destroy a semaphore
 *
 * @param  semaphore  Handle to the semaphore to destroy
 *
 * @return ESR_SUCCESS if success, or an an error indicating the cause of the
 * failure.
 */
PORTABLE_API ESR_ReturnCode PtrdSemaphoreDestroy(PtrdSemaphore *semaphore);

/**
 * Decrements the semaphore.  If the semaphore's current value is 0, the
 * current thread waits until the semaphore's value is greater than 0.
 *
 * @param  semaphore  Handle to the semaphore to acquire.
 *
 * @return ESR_SUCCESS if successful, or a status code indicating the nature of
 * the error.
 */
PORTABLE_API ESR_ReturnCode PtrdSemaphoreAcquire(PtrdSemaphore *semaphore);


/**
 * Decrements the semaphore.  If the semaphore's current value is 0, the
 * current thread waits until the semaphore's value is greater than 0 or until
 * the timeout expires.
 *
 * @param  semaphore  Handle to the semaphore to acquire.
 * @param  timeoutMs  Timeout in milliseconds.
 *
 * @return ESR_SUCCESS if wait is successful, ESR_TIMED_OUT if timed out, or an
 * error status indicating the nature of the error in other situations.
 */
PORTABLE_API ESR_ReturnCode PtrdSemaphoreAcquireTimeout(PtrdSemaphore *semaphore,
    asr_uint32_t timeoutMs);
    
/**
 * Increments a semaphore.
 *
 * @param semaphore Handle to the semaphore to release.
 *
 * @return ESR_SUCCESS success or an error status indicating the nature of the
 * error.  In particular, it will return ESR_INVALID_STATE if the semaphore is
 * currently at its maximum value.
 */
PORTABLE_API ESR_ReturnCode PtrdSemaphoreRelease(PtrdSemaphore *semaphore);


/**
 * Function signature invoked on the new thread by PtrdThreadCreate(), and
 * the argument to that function.
 */
typedef void* PtrdThreadArg;
/**
 * Function prototype that launched threads must conform to.
 *
 * @param userData Data passed in by caller of PtrdThreadCreate
 */
typedef void(*PtrdThreadStartFunc)(PtrdThreadArg userData);

/**
 * Minimum thread priority.
 */
#define PtrdThreadMinPriority 0

/**
 * Maximum thread priority.
 */
#define PtrdThreadMaxPriority UINT16_TMAX

/**
 * Normal thread priority.
 */
#define PtrdThreadNormalPriority (PtrdThreadMaxPriority / 2)

/**
 * Creates a thread.
 *
 * Execution starts on the thread immediately. To pause execution use a
 * monitor or a mutex between the thread and the thread creator.
 *
 * @param  thread       Handle to the thread that is created
 * @param  startFunc    Function for the thread to start execution on
 * @param  arg          Argument to the thread function
 *
 * @return ESR_INVALID_ARGUMENT if thread or startFunc are null; ESR_OUT_OF_MEMORY if system is out of memory;
 * ESR_THREAD_CREATION_ERROR if thread cannot be created
 */
PORTABLE_API ESR_ReturnCode PtrdThreadCreate(PtrdThreadStartFunc startFunc, PtrdThreadArg arg,
    PtrdThread** thread);
    
/**
 * Destroys a thread handle.
 *
 * Note: this does NOT stop or destroy the thread, it just releases
 * the handle for accessing it. If this is not done, a memory leak
 * occurs, so if the creator of the thread never needs to communicate
 * with the thread again it should call this immediately after the
 * create if the create was successful.
 *
 * @return ESR_SUCCESS on failure or an error indicating the nature of the
 * error.
 */
PORTABLE_API ESR_ReturnCode PtrdThreadDestroy(PtrdThread *thread);

/**
 * Wait for the termination of a specified thread
 *
 * @param thread Handle to the thread to wait for
 *
 * @return ESR_INVALID_ARGUMENT if thread is null
 */
PORTABLE_API ESR_ReturnCode PtrdThreadJoin(PtrdThread *thread);

/**
 * Returns the thread priority.
 *
 * @param thread PtrdThread handle
 * @param value [out] Thread priority
 *
 * @return ESR_INVALID_ARGUMENT if thread or value are null; ESR_INVALID_STATE if thread priority cannot be
 * retrieved
 */
PORTABLE_API ESR_ReturnCode PtrdThreadGetPriority(PtrdThread *thread, asr_uint16_t* value);

/**
 * Sets the thread priority.
 *
 * @param thread PtrdThread handle
 * @param value Thread priority
 *
 * @return ESR_INVALID_ARGUMENT if thread or value are null; ESR_INVALID_STATE if thread priority cannot be
 * set
 */
PORTABLE_API ESR_ReturnCode PtrdThreadSetPriority(PtrdThread *thread, asr_uint16_t value);

/**
 * Yields execution of the current thread to other threads.
 *
 * @return ESR_SUCCESS
 */
PORTABLE_API ESR_ReturnCode PtrdThreadYield(void);

/**
 * Initializes the thread library.  This should be called before creating the
 * first thread or the first monitor.
 *
 * @return ESR_INVALID_STATE if the Ptrd module has already been initialized;
 * ESR_MUTEX_CREATION_ERROR if mutex cannot be created
 */
PORTABLE_API ESR_ReturnCode PtrdInit(void);

/**
 * Indicates if thread library has been initialized.
 *
 * @param enabled [out] True if library is initialized
 * @return ESR_INVALID_ARGUMENT if enabled is null
 */
PORTABLE_API ESR_ReturnCode PtrdIsEnabled(ESR_BOOL* enabled);

/**
 * Shutdowns the thread library.  All thread and monitor should be terminated
 * and destroyed before calling this function.
 *
 * @return ESR_INVALID_STATE if Ptrd module is not running
 * error.
 */
PORTABLE_API ESR_ReturnCode PtrdShutdown(void);

/**
 * @}
 */

#else


//#error "Including ptrd.h on a non-threaded platform."


#endif /* USE_THREAD */
#endif