/*
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#ifndef __PJ_OS_H__
#define __PJ_OS_H__

/**
 * @file os.h
 * @brief OS dependent functions
 */
#include <pj/types.h>

PJ_BEGIN_DECL

/**
 * @defgroup PJ_OS Operating System Dependent Functionality.
 */

/* **************************************************************************/
/**
 * @defgroup PJ_SYS_INFO System Information
 * @ingroup PJ_OS
 * @{
 */

/**
 * These enumeration contains constants to indicate support of miscellaneous
 * system features. These will go in "flags" field of #pj_sys_info structure.
 */
typedef enum pj_sys_info_flag {
    /**
     * Support for Apple iOS background feature.
     */
    PJ_SYS_HAS_IOS_BG = 1

} pj_sys_info_flag;

/**
 * This structure contains information about the system. Use #pj_get_sys_info()
 * to obtain the system information.
 */
typedef struct pj_sys_info {
    /**
     * Null terminated string containing processor information (e.g. "i386",
     * "x86_64"). It may contain empty string if the value cannot be obtained.
     */
    pj_str_t machine;

    /**
     * Null terminated string identifying the system operation (e.g. "Linux",
     * "win32", "wince"). It may contain empty string if the value cannot be
     * obtained.
     */
    pj_str_t os_name;

    /**
     * A number containing the operating system version number. By convention,
     * this field is divided into four bytes, where the highest order byte
     * contains the most major version of the OS, the next less significant
     * byte contains the less major version, and so on. How the OS version
     * number is mapped into these four bytes would be specific for each OS.
     * For example, Linux-2.6.32-28 would yield "os_ver" value of 0x0206201c,
     * while for Windows 7 it will be 0x06010000 (because dwMajorVersion is
     * 6 and dwMinorVersion is 1 for Windows 7).
     *
     * This field may contain zero if the OS version cannot be obtained.
     */
    pj_uint32_t os_ver;

    /**
     * Null terminated string identifying the SDK name that is used to build
     * the library (e.g. "glibc", "uclibc", "msvc", "wince"). It may contain
     * empty string if the value cannot eb obtained.
     */
    pj_str_t sdk_name;

    /**
     * A number containing the SDK version, using the numbering convention as
     * the "os_ver" field. The value will be zero if the version cannot be
     * obtained.
     */
    pj_uint32_t sdk_ver;

    /**
     * A longer null terminated string identifying the underlying system with
     * as much information as possible.
     */
    pj_str_t info;

    /**
     * Other flags containing system specific information. The value is
     * bitmask of #pj_sys_info_flag constants.
     */
    pj_uint32_t flags;

} pj_sys_info;

/**
 * Obtain the system information.
 *
 * @return      System information structure.
 */
PJ_DECL(const pj_sys_info *) pj_get_sys_info(void);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_THREAD Threads
 * @ingroup PJ_OS
 * @{
 * This module provides multithreading API.
 *
 * \section pj_thread_examples_sec Examples
 *
 * For examples, please see:
 *  - \ref page_pjlib_thread_test
 *  - \ref page_pjlib_sleep_test
 *
 */

/**
 * Thread creation flags:
 * - PJ_THREAD_SUSPENDED: specify that the thread should be created suspended.
 */
typedef enum pj_thread_create_flags { PJ_THREAD_SUSPENDED = 1 } pj_thread_create_flags;

/**
 * Type of thread entry function.
 */
typedef int(PJ_THREAD_FUNC pj_thread_proc)(void *);

/**
 * Size of thread struct.
 */
#if !defined(PJ_THREAD_DESC_SIZE)
#define PJ_THREAD_DESC_SIZE (64)
#endif

/**
 * Thread structure, to thread's state when the thread is created by external
 * or native API.
 */
typedef long pj_thread_desc[PJ_THREAD_DESC_SIZE];

/**
 * Get process ID.
 * @return process ID.
 */
PJ_DECL(pj_uint32_t) pj_getpid(void);

/**
 * Create a new thread.
 *
 * @param pool          The memory pool from which the thread record
 *                      will be allocated from.
 * @param thread_name   The optional name to be assigned to the thread.
 * @param proc          Thread entry function.
 * @param arg           Argument to be passed to the thread entry function.
 * @param stack_size    The size of the stack for the new thread, or ZERO or
 *                      PJ_THREAD_DEFAULT_STACK_SIZE to let the
 *                      library choose the reasonable size for the stack.
 *                      For some systems, the stack will be allocated from
 *                      the pool, so the pool must have suitable capacity.
 * @param flags         Flags for thread creation, which is bitmask combination
 *                      from enum pj_thread_create_flags.
 * @param thread        Pointer to hold the newly created thread.
 *
 * @return              PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t)
pj_thread_create(pj_pool_t *pool, const char *thread_name, pj_thread_proc *proc, void *arg, pj_size_t stack_size,
                 unsigned flags, pj_thread_t **thread);

/**
 * Register a thread that was created by external or native API to PJLIB.
 * This function must be called in the context of the thread being registered.
 * When the thread is created by external function or API call,
 * it must be 'registered' to PJLIB using pj_thread_register(), so that it can
 * cooperate with PJLIB's framework. During registration, some data needs to
 * be maintained, and this data must remain available during the thread's
 * lifetime.
 *
 * @param thread_name   The optional name to be assigned to the thread.
 * @param desc          Thread descriptor, which must be available throughout
 *                      the lifetime of the thread.
 * @param thread        Pointer to hold the created thread handle.
 *
 * @return              PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_thread_register(const char *thread_name, pj_thread_desc desc, pj_thread_t **thread);

/**
 * Check if this thread has been registered to PJLIB.
 *
 * @return              Non-zero if it is registered.
 */
PJ_DECL(pj_bool_t) pj_thread_is_registered(void);

/**
 * Get thread priority value for the thread.
 *
 * @param thread        Thread handle.
 *
 * @return              Thread priority value, or -1 on error.
 */
PJ_DECL(int) pj_thread_get_prio(pj_thread_t *thread);

/**
 * Set the thread priority. The priority value must be in the priority
 * value range, which can be retrieved with #pj_thread_get_prio_min() and
 * #pj_thread_get_prio_max() functions.
 *
 * For Android, this function will only set the priority of the calling thread
 * (the thread param must be set to NULL or the calling thread handle).
 *
 * @param thread        Thread handle.
 * @param prio          New priority to be set to the thread.
 *
 * @return              PJ_SUCCESS on success or the error code.
 */
PJ_DECL(pj_status_t) pj_thread_set_prio(pj_thread_t *thread, int prio);

/**
 * Get the lowest priority value available for this thread.
 *
 * @param thread        Thread handle.
 * @return              Minimum thread priority value, or -1 on error.
 */
PJ_DECL(int) pj_thread_get_prio_min(pj_thread_t *thread);

/**
 * Get the highest priority value available for this thread.
 *
 * @param thread        Thread handle.
 * @return              Minimum thread priority value, or -1 on error.
 */
PJ_DECL(int) pj_thread_get_prio_max(pj_thread_t *thread);

/**
 * Return native handle from pj_thread_t for manipulation using native
 * OS APIs.
 *
 * @param thread        PJLIB thread descriptor.
 *
 * @return              Native thread handle. For example, when the
 *                      backend thread uses pthread, this function will
 *                      return pointer to pthread_t, and on Windows,
 *                      this function will return HANDLE.
 */
PJ_DECL(void *) pj_thread_get_os_handle(pj_thread_t *thread);

/**
 * Get thread name.
 *
 * @param thread    The thread handle.
 *
 * @return Thread name as null terminated string.
 */
PJ_DECL(const char *) pj_thread_get_name(pj_thread_t *thread);

/**
 * Resume a suspended thread.
 *
 * @param thread    The thread handle.
 *
 * @return zero on success.
 */
PJ_DECL(pj_status_t) pj_thread_resume(pj_thread_t *thread);

/**
 * Get the current thread.
 *
 * @return Thread handle of current thread.
 */
PJ_DECL(pj_thread_t *) pj_thread_this(void);

/**
 * Join thread, and block the caller thread until the specified thread exits.
 * If it is called from within the thread itself, it will return immediately
 * with failure status.
 * If the specified thread has already been dead, or it does not exist,
 * the function will return immediately with successful status.
 *
 * @param thread    The thread handle.
 *
 * @return PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pj_thread_join(pj_thread_t *thread);

/**
 * Destroy thread and release resources allocated for the thread.
 * However, the memory allocated for the pj_thread_t itself will only be released
 * when the pool used to create the thread is destroyed.
 *
 * @param thread    The thread handle.
 *
 * @return zero on success.
 */
PJ_DECL(pj_status_t) pj_thread_destroy(pj_thread_t *thread);

/**
 * Put the current thread to sleep for the specified miliseconds.
 *
 * @param msec Miliseconds delay.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_thread_sleep(unsigned msec);

/**
 * @def PJ_CHECK_STACK()
 * PJ_CHECK_STACK() macro is used to check the sanity of the stack.
 * The OS implementation may check that no stack overflow occurs, and
 * it also may collect statistic about stack usage.
 */
#if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK != 0

#define PJ_CHECK_STACK() pj_thread_check_stack(__FILE__, __LINE__)

/** @internal
 * The implementation of stack checking.
 */
PJ_DECL(void) pj_thread_check_stack(const char *file, int line);

/** @internal
 * Get maximum stack usage statistic.
 */
PJ_DECL(pj_uint32_t) pj_thread_get_stack_max_usage(pj_thread_t *thread);

/** @internal
 * Dump thread stack status.
 */
PJ_DECL(pj_status_t) pj_thread_get_stack_info(pj_thread_t *thread, const char **file, int *line);
#else

#define PJ_CHECK_STACK()
/** pj_thread_get_stack_max_usage() for the thread */
#define pj_thread_get_stack_max_usage(thread)  0
/** pj_thread_get_stack_info() for the thread */
#define pj_thread_get_stack_info(thread, f, l) (*(f) = "", *(l) = 0)
#endif /* PJ_OS_HAS_CHECK_STACK */

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_SYMBIAN_OS Symbian OS Specific
 * @ingroup PJ_OS
 * @{
 * Functionalities specific to Symbian OS.
 *
 * Symbian OS strongly discourages the use of polling since this wastes
 * CPU power, and instead provides Active Object and Active Scheduler
 * pattern to allow application (in this case, PJLIB) to register asynchronous
 * tasks. PJLIB port for Symbian complies to this recommended behavior.
 * As the result, few things have been changed in PJLIB for Symbian:
 *      - the timer heap (see @ref PJ_TIMER) is implemented with active
 *        object framework, and each timer entry registered to the timer
 *        heap will register an Active Object to the Active Scheduler.
 *        Because of this, polling the timer heap with pj_timer_heap_poll()
 *        is no longer necessary, and this function will just evaluate
 *        to nothing.
 *      - the ioqueue (see @ref PJ_IOQUEUE) is also implemented with
 *        active object framework, with each asynchronous operation will
 *        register an Active Object to the Active Scheduler. Because of
 *        this, polling the ioqueue with pj_ioqueue_poll() is no longer
 *        necessary, and this function will just evaluate to nothing.
 *
 * Since timer heap and ioqueue polling are no longer necessary, Symbian
 * application can now poll for all events by calling
 * \a User::WaitForAnyRequest() and \a CActiveScheduler::RunIfReady().
 * PJLIB provides a thin wrapper which calls these two functions,
 * called pj_symbianos_poll().
 */

/**
 * Wait the completion of any Symbian active objects. When the timeout
 * value is not specified (the \a ms_timeout argument is -1), this
 * function is a thin wrapper which calls \a User::WaitForAnyRequest()
 * and \a CActiveScheduler::RunIfReady(). If the timeout value is
 * specified, this function will schedule a timer entry to the timer
 * heap (which is an Active Object), to limit the wait time for event
 * occurences. Scheduling a timer entry is an expensive operation,
 * therefore application should only specify a timeout value when it's
 * really necessary (for example, when it's not sure there are other
 * Active Objects currently running in the application).
 *
 * @param priority      The minimum priority of the Active Objects to
 *                      poll, which values are from CActive::TPriority
 *                      constants. If -1 is given, CActive::EPriorityStandard.
 *                      priority will be used.
 * @param ms_timeout    Optional timeout to wait. Application should
 *                      specify -1 to let the function wait indefinitely
 *                      for any events.
 *
 * @return              PJ_TRUE if there have been any events executed
 *                      during the polling. This function will only return
 *                      PJ_FALSE if \a ms_timeout argument is specified
 *                      (i.e. the value is not -1) and there was no event
 *                      executed when the timeout timer elapsed.
 */
PJ_DECL(pj_bool_t) pj_symbianos_poll(int priority, int ms_timeout);

/**
 * This structure declares Symbian OS specific parameters that can be
 * specified when calling #pj_symbianos_set_params().
 */
typedef struct pj_symbianos_params {
    /**
     * Optional RSocketServ instance to be used by PJLIB. If this
     * value is NULL, PJLIB will create a new RSocketServ instance
     * when pj_init() is called.
     */
    void *rsocketserv;

    /**
     * Optional RConnection instance to be used by PJLIB when creating
     * sockets. If this value is NULL, no RConnection will be
     * specified when creating sockets.
     */
    void *rconnection;

    /**
     * Optional RHostResolver instance to be used by PJLIB. If this value
     * is NULL, a new RHostResolver instance will be created when
     * pj_init() is called.
     */
    void *rhostresolver;

    /**
     * Optional RHostResolver for IPv6 instance to be used by PJLIB.
     * If this value is NULL, a new RHostResolver instance will be created
     * when pj_init() is called.
     */
    void *rhostresolver6;

} pj_symbianos_params;

/**
 * Specify Symbian OS parameters to be used by PJLIB. This function MUST
 * be called before #pj_init() is called.
 *
 * @param prm           Symbian specific parameters.
 *
 * @return              PJ_SUCCESS if the parameters can be applied
 *                      successfully.
 */
PJ_DECL(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm);

/**
 *  Notify PJLIB that the access point connection has been down or unusable
 *  and PJLIB should not try to access the Symbian socket API (especially ones
 *  that send packets). Sending packet when RConnection is reconnected to
 *  different access point may cause the WaitForRequest() for the function to
 *  block indefinitely.
 *
 *  @param up           If set to PJ_FALSE it will cause PJLIB to not try
 *                      to access socket API, and error will be returned
 *                      immediately instead.
 */
PJ_DECL(void) pj_symbianos_set_connection_status(pj_bool_t up);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_TLS Thread Local Storage.
 * @ingroup PJ_OS
 * @{
 */

/**
 * Allocate thread local storage index. The initial value of the variable at
 * the index is zero.
 *
 * @param index     Pointer to hold the return value.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_thread_local_alloc(long *index);

/**
 * Deallocate thread local variable.
 *
 * @param index     The variable index.
 */
PJ_DECL(void) pj_thread_local_free(long index);

/**
 * Set the value of thread local variable.
 *
 * @param index     The index of the variable.
 * @param value     The value.
 */
PJ_DECL(pj_status_t) pj_thread_local_set(long index, void *value);

/**
 * Get the value of thread local variable.
 *
 * @param index     The index of the variable.
 * @return          The value.
 */
PJ_DECL(void *) pj_thread_local_get(long index);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_ATOMIC Atomic Variables
 * @ingroup PJ_OS
 * @{
 *
 * This module provides API to manipulate atomic variables.
 *
 * \section pj_atomic_examples_sec Examples
 *
 * For some example codes, please see:
 *  - @ref page_pjlib_atomic_test
 */

/**
 * Create atomic variable.
 *
 * @param pool      The pool.
 * @param initial   The initial value of the atomic variable.
 * @param atomic    Pointer to hold the atomic variable upon return.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_atomic_create(pj_pool_t *pool, pj_atomic_value_t initial, pj_atomic_t **atomic);

/**
 * Destroy atomic variable.
 *
 * @param atomic_var    the atomic variable.
 *
 * @return PJ_SUCCESS if success.
 */
PJ_DECL(pj_status_t) pj_atomic_destroy(pj_atomic_t *atomic_var);

/**
 * Set the value of an atomic type, and return the previous value.
 *
 * @param atomic_var    the atomic variable.
 * @param value         value to be set to the variable.
 */
PJ_DECL(void) pj_atomic_set(pj_atomic_t *atomic_var, pj_atomic_value_t value);

/**
 * Get the value of an atomic type.
 *
 * @param atomic_var    the atomic variable.
 *
 * @return the value of the atomic variable.
 */
PJ_DECL(pj_atomic_value_t) pj_atomic_get(pj_atomic_t *atomic_var);

/**
 * Increment the value of an atomic type.
 *
 * @param atomic_var    the atomic variable.
 */
PJ_DECL(void) pj_atomic_inc(pj_atomic_t *atomic_var);

/**
 * Increment the value of an atomic type and get the result.
 *
 * @param atomic_var    the atomic variable.
 *
 * @return              The incremented value.
 */
PJ_DECL(pj_atomic_value_t) pj_atomic_inc_and_get(pj_atomic_t *atomic_var);

/**
 * Decrement the value of an atomic type.
 *
 * @param atomic_var    the atomic variable.
 */
PJ_DECL(void) pj_atomic_dec(pj_atomic_t *atomic_var);

/**
 * Decrement the value of an atomic type and get the result.
 *
 * @param atomic_var    the atomic variable.
 *
 * @return              The decremented value.
 */
PJ_DECL(pj_atomic_value_t) pj_atomic_dec_and_get(pj_atomic_t *atomic_var);

/**
 * Add a value to an atomic type.
 *
 * @param atomic_var    The atomic variable.
 * @param value         Value to be added.
 */
PJ_DECL(void) pj_atomic_add(pj_atomic_t *atomic_var, pj_atomic_value_t value);

/**
 * Add a value to an atomic type and get the result.
 *
 * @param atomic_var    The atomic variable.
 * @param value         Value to be added.
 *
 * @return              The result after the addition.
 */
PJ_DECL(pj_atomic_value_t) pj_atomic_add_and_get(pj_atomic_t *atomic_var, pj_atomic_value_t value);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_MUTEX Mutexes.
 * @ingroup PJ_OS
 * @{
 *
 * Mutex manipulation. Alternatively, application can use higher abstraction
 * for lock objects, which provides uniform API for all kinds of lock
 * mechanisms, including mutex. See @ref PJ_LOCK for more information.
 */

/**
 * Mutex types:
 *  - PJ_MUTEX_DEFAULT: default mutex type, which is system dependent.
 *  - PJ_MUTEX_SIMPLE: non-recursive mutex.
 *  - PJ_MUTEX_RECURSE: recursive mutex.
 */
typedef enum pj_mutex_type_e { PJ_MUTEX_DEFAULT, PJ_MUTEX_SIMPLE, PJ_MUTEX_RECURSE } pj_mutex_type_e;

/**
 * Create mutex of the specified type.
 *
 * @param pool      The pool.
 * @param name      Name to be associated with the mutex (for debugging).
 * @param type      The type of the mutex, of type #pj_mutex_type_e.
 * @param mutex     Pointer to hold the returned mutex instance.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_create(pj_pool_t *pool, const char *name, int type, pj_mutex_t **mutex);

/**
 * Create simple, non-recursive mutex.
 * This function is a simple wrapper for #pj_mutex_create to create
 * non-recursive mutex.
 *
 * @param pool      The pool.
 * @param name      Mutex name.
 * @param mutex     Pointer to hold the returned mutex instance.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_create_simple(pj_pool_t *pool, const char *name, pj_mutex_t **mutex);

/**
 * Create recursive mutex.
 * This function is a simple wrapper for #pj_mutex_create to create
 * recursive mutex.
 *
 * @param pool      The pool.
 * @param name      Mutex name.
 * @param mutex     Pointer to hold the returned mutex instance.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_create_recursive(pj_pool_t *pool, const char *name, pj_mutex_t **mutex);

/**
 * Acquire mutex lock.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_lock(pj_mutex_t *mutex);

/**
 * Release mutex lock.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_unlock(pj_mutex_t *mutex);

/**
 * Try to acquire mutex lock.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code if the
 *                  lock couldn't be acquired.
 */
PJ_DECL(pj_status_t) pj_mutex_trylock(pj_mutex_t *mutex);

/**
 * Destroy mutex.
 *
 * @param mutex     Te mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_mutex_destroy(pj_mutex_t *mutex);

/**
 * Determine whether calling thread is owning the mutex (only available when
 * PJ_DEBUG is set).
 * @param mutex     The mutex.
 * @return          Non-zero if yes.
 */
PJ_DECL(pj_bool_t) pj_mutex_is_locked(pj_mutex_t *mutex);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_RW_MUTEX Reader/Writer Mutex
 * @ingroup PJ_OS
 * @{
 * Reader/writer mutex is a classic synchronization object where multiple
 * readers can acquire the mutex, but only a single writer can acquire the
 * mutex.
 */

/**
 * Opaque declaration for reader/writer mutex.
 * Reader/writer mutex is a classic synchronization object where multiple
 * readers can acquire the mutex, but only a single writer can acquire the
 * mutex.
 */
typedef struct pj_rwmutex_t pj_rwmutex_t;

/**
 * Create reader/writer mutex.
 *
 * @param pool      Pool to allocate memory for the mutex.
 * @param name      Name to be assigned to the mutex.
 * @param mutex     Pointer to receive the newly created mutex.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_create(pj_pool_t *pool, const char *name, pj_rwmutex_t **mutex);

/**
 * Lock the mutex for reading.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_lock_read(pj_rwmutex_t *mutex);

/**
 * Lock the mutex for writing.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_lock_write(pj_rwmutex_t *mutex);

/**
 * Release read lock.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_unlock_read(pj_rwmutex_t *mutex);

/**
 * Release write lock.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_unlock_write(pj_rwmutex_t *mutex);

/**
 * Destroy reader/writer mutex.
 *
 * @param mutex     The mutex.
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_rwmutex_destroy(pj_rwmutex_t *mutex);

/**
 * @}
 */

/* **************************************************************************/
/**
 * @defgroup PJ_CRIT_SEC Critical sections.
 * @ingroup PJ_OS
 * @{
 * Critical section protection can be used to protect regions where:
 *  - mutual exclusion protection is needed.
 *  - it's rather too expensive to create a mutex.
 *  - the time spent in the region is very very brief.
 *
 * Critical section is a global object, and it prevents any threads from
 * entering any regions that are protected by critical section once a thread
 * is already in the section.
 *
 * Critial section is \a not recursive!
 *
 * Application <b>MUST NOT</b> call any functions that may cause current
 * thread to block (such as allocating memory, performing I/O, locking mutex,
 * etc.) while holding the critical section.
 */
/**
 * Enter critical section.
 */
PJ_DECL(void) pj_enter_critical_section(void);

/**
 * Leave critical section.
 */
PJ_DECL(void) pj_leave_critical_section(void);

/**
 * @}
 */

/* **************************************************************************/
#if defined(PJ_HAS_SEMAPHORE) && PJ_HAS_SEMAPHORE != 0
/**
 * @defgroup PJ_SEM Semaphores.
 * @ingroup PJ_OS
 * @{
 *
 * This module provides abstraction for semaphores, where available.
 */

/**
 * Create semaphore.
 *
 * @param pool      The pool.
 * @param name      Name to be assigned to the semaphore (for logging purpose)
 * @param initial   The initial count of the semaphore.
 * @param max       The maximum count of the semaphore.
 * @param sem       Pointer to hold the semaphore created.
 *
 * @return          PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_sem_create(pj_pool_t *pool, const char *name, unsigned initial, unsigned max, pj_sem_t **sem);

/**
 * Wait for semaphore.
 *
 * @param sem   The semaphore.
 *
 * @return      PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_sem_wait(pj_sem_t *sem);

/**
 * Try wait for semaphore.
 *
 * @param sem   The semaphore.
 *
 * @return      PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_sem_trywait(pj_sem_t *sem);

/**
 * Release semaphore.
 *
 * @param sem   The semaphore.
 *
 * @return      PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_sem_post(pj_sem_t *sem);

/**
 * Destroy semaphore.
 *
 * @param sem   The semaphore.
 *
 * @return      PJ_SUCCESS on success, or the error code.
 */
PJ_DECL(pj_status_t) pj_sem_destroy(pj_sem_t *sem);

/**
 * @}
 */
#endif /* PJ_HAS_SEMAPHORE */

/* **************************************************************************/
#if defined(PJ_HAS_EVENT_OBJ) && PJ_HAS_EVENT_OBJ != 0
/**
 * @defgroup PJ_EVENT Event Object.
 * @ingroup PJ_OS
 * @{
 *
 * This module provides abstraction to event object (e.g. Win32 Event) where
 * available. Event objects can be used for synchronization among threads.
 */

/**
 * Create event object.
 *
 * @param pool          The pool.
 * @param name          The name of the event object (for logging purpose).
 * @param manual_reset  Specify whether the event is manual-reset
 * @param initial       Specify the initial state of the event object.
 * @param event         Pointer to hold the returned event object.
 *
 * @return event handle, or NULL if failed.
 */
PJ_DECL(pj_status_t)
pj_event_create(pj_pool_t *pool, const char *name, pj_bool_t manual_reset, pj_bool_t initial, pj_event_t **event);

/**
 * Wait for event to be signaled.
 *
 * @param event     The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_wait(pj_event_t *event);

/**
 * Try wait for event object to be signalled.
 *
 * @param event The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_trywait(pj_event_t *event);

/**
 * Set the event object state to signaled. For auto-reset event, this
 * will only release the first thread that are waiting on the event. For
 * manual reset event, the state remains signaled until the event is reset.
 * If there is no thread waiting on the event, the event object state
 * remains signaled.
 *
 * @param event     The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_set(pj_event_t *event);

/**
 * Set the event object to signaled state to release appropriate number of
 * waiting threads and then reset the event object to non-signaled. For
 * manual-reset event, this function will release all waiting threads. For
 * auto-reset event, this function will only release one waiting thread.
 *
 * @param event     The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_pulse(pj_event_t *event);

/**
 * Set the event object state to non-signaled.
 *
 * @param event     The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_reset(pj_event_t *event);

/**
 * Destroy the event object.
 *
 * @param event     The event object.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_event_destroy(pj_event_t *event);

/**
 * @}
 */
#endif /* PJ_HAS_EVENT_OBJ */

/* **************************************************************************/
/**
 * @addtogroup PJ_TIME Time Data Type and Manipulation.
 * @ingroup PJ_OS
 * @{
 * This module provides API for manipulating time.
 *
 * \section pj_time_examples_sec Examples
 *
 * For examples, please see:
 *  - \ref page_pjlib_sleep_test
 */

/**
 * Get current time of day in local representation.
 *
 * @param tv    Variable to store the result.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_gettimeofday(pj_time_val *tv);

/**
 * Parse time value into date/time representation.
 *
 * @param tv    The time.
 * @param pt    Variable to store the date time result.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_time_decode(const pj_time_val *tv, pj_parsed_time *pt);

/**
 * Encode date/time to time value.
 *
 * @param pt    The date/time.
 * @param tv    Variable to store time value result.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_time_encode(const pj_parsed_time *pt, pj_time_val *tv);

/**
 * Convert local time to GMT.
 *
 * @param tv    Time to convert.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_time_local_to_gmt(pj_time_val *tv);

/**
 * Convert GMT to local time.
 *
 * @param tv    Time to convert.
 *
 * @return zero if successfull.
 */
PJ_DECL(pj_status_t) pj_time_gmt_to_local(pj_time_val *tv);

/**
 * @}
 */

/* **************************************************************************/
#if defined(PJ_TERM_HAS_COLOR) && PJ_TERM_HAS_COLOR != 0

/**
 * @defgroup PJ_TERM Terminal
 * @ingroup PJ_OS
 * @{
 */

/**
 * Set current terminal color.
 *
 * @param color     The RGB color.
 *
 * @return zero on success.
 */
PJ_DECL(pj_status_t) pj_term_set_color(pj_color_t color);

/**
 * Get current terminal foreground color.
 *
 * @return RGB color.
 */
PJ_DECL(pj_color_t) pj_term_get_color(void);

/**
 * @}
 */

#endif /* PJ_TERM_HAS_COLOR */

/* **************************************************************************/
/**
 * @defgroup PJ_TIMESTAMP High Resolution Timestamp
 * @ingroup PJ_OS
 * @{
 *
 * PJLIB provides <b>High Resolution Timestamp</b> API to access highest
 * resolution timestamp value provided by the platform. The API is usefull
 * to measure precise elapsed time, and can be used in applications such
 * as profiling.
 *
 * The timestamp value is represented in cycles, and can be related to
 * normal time (in seconds or sub-seconds) using various functions provided.
 *
 * \section pj_timestamp_examples_sec Examples
 *
 * For examples, please see:
 *  - \ref page_pjlib_sleep_test
 *  - \ref page_pjlib_timestamp_test
 */

/*
 * High resolution timer.
 */
#if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0

/**
 * Get monotonic time since some unspecified starting point.
 *
 * @param tv    Variable to store the result.
 *
 * @return PJ_SUCCESS if successful.
 */
PJ_DECL(pj_status_t) pj_gettickcount(pj_time_val *tv);

/**
 * Acquire high resolution timer value. The time value are stored
 * in cycles.
 *
 * @param ts        High resolution timer value.
 * @return          PJ_SUCCESS or the appropriate error code.
 *
 * @see pj_get_timestamp_freq().
 */
PJ_DECL(pj_status_t) pj_get_timestamp(pj_timestamp *ts);

/**
 * Get high resolution timer frequency, in cycles per second.
 *
 * @param freq      Timer frequency, in cycles per second.
 * @return          PJ_SUCCESS or the appropriate error code.
 */
PJ_DECL(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq);

/**
 * Set timestamp from 32bit values.
 * @param t         The timestamp to be set.
 * @param hi        The high 32bit part.
 * @param lo        The low 32bit part.
 */
PJ_INLINE(void) pj_set_timestamp32(pj_timestamp *t, pj_uint32_t hi, pj_uint32_t lo)
{
    t->u32.hi = hi;
    t->u32.lo = lo;
}

/**
 * Compare timestamp t1 and t2.
 * @param t1        t1.
 * @param t2        t2.
 * @return          -1 if (t1 < t2), 1 if (t1 > t2), or 0 if (t1 == t2)
 */
PJ_INLINE(int) pj_cmp_timestamp(const pj_timestamp *t1, const pj_timestamp *t2)
{
#if PJ_HAS_INT64
    if (t1->u64 < t2->u64)
        return -1;
    else if (t1->u64 > t2->u64)
        return 1;
    else
        return 0;
#else
    if (t1->u32.hi < t2->u32.hi || (t1->u32.hi == t2->u32.hi && t1->u32.lo < t2->u32.lo))
        return -1;
    else if (t1->u32.hi > t2->u32.hi || (t1->u32.hi == t2->u32.hi && t1->u32.lo > t2->u32.lo))
        return 1;
    else
        return 0;
#endif
}

/**
 * Add timestamp t2 to t1.
 * @param t1        t1.
 * @param t2        t2.
 */
PJ_INLINE(void) pj_add_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
{
#if PJ_HAS_INT64
    t1->u64 += t2->u64;
#else
    pj_uint32_t old = t1->u32.lo;
    t1->u32.hi += t2->u32.hi;
    t1->u32.lo += t2->u32.lo;
    if (t1->u32.lo < old)
        ++t1->u32.hi;
#endif
}

/**
 * Add timestamp t2 to t1.
 * @param t1        t1.
 * @param t2        t2.
 */
PJ_INLINE(void) pj_add_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
{
#if PJ_HAS_INT64
    t1->u64 += t2;
#else
    pj_uint32_t old = t1->u32.lo;
    t1->u32.lo += t2;
    if (t1->u32.lo < old)
        ++t1->u32.hi;
#endif
}

/**
 * Substract timestamp t2 from t1.
 * @param t1        t1.
 * @param t2        t2.
 */
PJ_INLINE(void) pj_sub_timestamp(pj_timestamp *t1, const pj_timestamp *t2)
{
#if PJ_HAS_INT64
    t1->u64 -= t2->u64;
#else
    t1->u32.hi -= t2->u32.hi;
    if (t1->u32.lo >= t2->u32.lo)
        t1->u32.lo -= t2->u32.lo;
    else {
        t1->u32.lo -= t2->u32.lo;
        --t1->u32.hi;
    }
#endif
}

/**
 * Substract timestamp t2 from t1.
 * @param t1        t1.
 * @param t2        t2.
 */
PJ_INLINE(void) pj_sub_timestamp32(pj_timestamp *t1, pj_uint32_t t2)
{
#if PJ_HAS_INT64
    t1->u64 -= t2;
#else
    if (t1->u32.lo >= t2)
        t1->u32.lo -= t2;
    else {
        t1->u32.lo -= t2;
        --t1->u32.hi;
    }
#endif
}

/**
 * Get the timestamp difference between t2 and t1 (that is t2 minus t1),
 * and return a 32bit signed integer difference.
 */
PJ_INLINE(pj_int32_t) pj_timestamp_diff32(const pj_timestamp *t1, const pj_timestamp *t2)
{
    /* Be careful with the signess (I think!) */
#if PJ_HAS_INT64
    pj_int64_t diff = t2->u64 - t1->u64;
    return (pj_int32_t)diff;
#else
    pj_int32 diff = t2->u32.lo - t1->u32.lo;
    return diff;
#endif
}

/**
 * Calculate the elapsed time, and store it in pj_time_val.
 * This function calculates the elapsed time using highest precision
 * calculation that is available for current platform, considering
 * whether floating point or 64-bit precision arithmetic is available.
 * For maximum portability, application should prefer to use this function
 * rather than calculating the elapsed time by itself.
 *
 * @param start     The starting timestamp.
 * @param stop      The end timestamp.
 *
 * @return          Elapsed time as #pj_time_val.
 *
 * @see pj_elapsed_usec(), pj_elapsed_cycle(), pj_elapsed_nanosec()
 */
PJ_DECL(pj_time_val) pj_elapsed_time(const pj_timestamp *start, const pj_timestamp *stop);

/**
 * Calculate the elapsed time as 32-bit miliseconds.
 * This function calculates the elapsed time using highest precision
 * calculation that is available for current platform, considering
 * whether floating point or 64-bit precision arithmetic is available.
 * For maximum portability, application should prefer to use this function
 * rather than calculating the elapsed time by itself.
 *
 * @param start     The starting timestamp.
 * @param stop      The end timestamp.
 *
 * @return          Elapsed time in milisecond.
 *
 * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
 */
PJ_DECL(pj_uint32_t) pj_elapsed_msec(const pj_timestamp *start, const pj_timestamp *stop);

/**
 * Variant of #pj_elapsed_msec() which returns 64bit value.
 */
PJ_DECL(pj_uint64_t) pj_elapsed_msec64(const pj_timestamp *start, const pj_timestamp *stop);

/**
 * Calculate the elapsed time in 32-bit microseconds.
 * This function calculates the elapsed time using highest precision
 * calculation that is available for current platform, considering
 * whether floating point or 64-bit precision arithmetic is available.
 * For maximum portability, application should prefer to use this function
 * rather than calculating the elapsed time by itself.
 *
 * @param start     The starting timestamp.
 * @param stop      The end timestamp.
 *
 * @return          Elapsed time in microsecond.
 *
 * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
 */
PJ_DECL(pj_uint32_t) pj_elapsed_usec(const pj_timestamp *start, const pj_timestamp *stop);

/**
 * Calculate the elapsed time in 32-bit nanoseconds.
 * This function calculates the elapsed time using highest precision
 * calculation that is available for current platform, considering
 * whether floating point or 64-bit precision arithmetic is available.
 * For maximum portability, application should prefer to use this function
 * rather than calculating the elapsed time by itself.
 *
 * @param start     The starting timestamp.
 * @param stop      The end timestamp.
 *
 * @return          Elapsed time in nanoseconds.
 *
 * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_usec()
 */
PJ_DECL(pj_uint32_t) pj_elapsed_nanosec(const pj_timestamp *start, const pj_timestamp *stop);

/**
 * Calculate the elapsed time in 32-bit cycles.
 * This function calculates the elapsed time using highest precision
 * calculation that is available for current platform, considering
 * whether floating point or 64-bit precision arithmetic is available.
 * For maximum portability, application should prefer to use this function
 * rather than calculating the elapsed time by itself.
 *
 * @param start     The starting timestamp.
 * @param stop      The end timestamp.
 *
 * @return          Elapsed time in cycles.
 *
 * @see pj_elapsed_usec(), pj_elapsed_time(), pj_elapsed_nanosec()
 */
PJ_DECL(pj_uint32_t) pj_elapsed_cycle(const pj_timestamp *start, const pj_timestamp *stop);

#endif /* PJ_HAS_HIGH_RES_TIMER */

/** @} */

/* **************************************************************************/
/**
 * @defgroup PJ_APP_OS Application execution
 * @ingroup PJ_OS
 * @{
 */

/**
 * Type for application main function.
 */
typedef int (*pj_main_func_ptr)(int argc, char *argv[]);

/**
 * Run the application. This function has to be called in the main thread
 * and after doing the necessary initialization according to the flags
 * provided, it will call main_func() function.
 *
 * @param main_func Application's main function.
 * @param argc      Number of arguments from the main() function, which
 *                  will be passed to main_func() function.
 * @param argv      The arguments from the main() function, which will
 *                  be passed to main_func() function.
 * @param flags     Flags for application execution, currently must be 0.
 *
 * @return          main_func()'s return value.
 */
PJ_DECL(int) pj_run_app(pj_main_func_ptr main_func, int argc, char *argv[], unsigned flags);

/** @} */

/* **************************************************************************/
/**
 * Internal PJLIB function to initialize the threading subsystem.
 * @return          PJ_SUCCESS or the appropriate error code.
 */
pj_status_t pj_thread_init(void);

PJ_END_DECL

#endif /* __PJ_OS_H__ */
