
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 AXUTIL_THREAD_H
#define AXUTIL_THREAD_H

/**
  * @file axutil_thread.h
  * @brief axis2 thread api
  */

#include <axutil_allocator.h>
#include <axutil_utils_defines.h>
#include <axutil_error.h>

#ifdef __cplusplus
extern "C"
{
#endif

    /**
     * @defgroup axutil_thread thread
     * @ingroup axis2_util
     * @{
     */

    /**
     * Thread callbacks from axis2 functions must be declared with AXIS2_THREAD_FUNC
     * so that they follow the platform's calling convention.
     */
    /*#define AXIS2_THREAD_FUNC */

    /** Thread structure. */
    typedef struct axutil_thread_t axutil_thread_t;

    /** Thread attributes structure. */
    typedef struct axutil_threadattr_t axutil_threadattr_t;

    /** Control variable for one-time atomic variables.  */
    typedef struct axutil_thread_once_t axutil_thread_once_t;

    /**
     * The prototype for any AXIS2 thread worker functions.
     */
    typedef void *(
        AXIS2_THREAD_FUNC * axutil_thread_start_t)(
            axutil_thread_t *,
            void *);

    /** Thread private address space. */
    typedef struct axutil_threadkey_t axutil_threadkey_t;

    /* Thread Function definitions */

    /**
     * Create and initialize a new threadattr variable
     * @param cont The pool to use
     * @return Newly created thread attribute
     */
    AXIS2_EXTERN axutil_threadattr_t *AXIS2_CALL
    axutil_threadattr_create(
        axutil_allocator_t * allocator);

    /**
     * Set if newly created threads should be created in detached state.
     * @param attr The threadattr to affect
     * @param on Non-zero if detached threads should be created.
     * @return The status of the operation
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_threadattr_detach_set(
        axutil_threadattr_t * attr,
        axis2_bool_t detached);

    /**
     * Get the detach state for this threadattr.
     * @param attr The threadattr to reference
     * @return AXIS2_TRUE if threads are to be detached, or AXIS2_FALSE
     * if threads are to be joinable.
     */
    AXIS2_EXTERN axis2_bool_t AXIS2_CALL
    axutil_threadattr_is_detach(
        axutil_threadattr_t * attr,
        axutil_allocator_t * allocator);

    /**
     * Create a new thread of execution
     * @param attr The threadattr to use to determine how to create the thread
     * @param func The function to start the new thread in
     * @param data Any data to be passed to the starting function
     * @param cont The pool to use
     * @return The newly created thread handle.
     */
    AXIS2_EXTERN axutil_thread_t *AXIS2_CALL
    axutil_thread_create(
        axutil_allocator_t * allocator,
        axutil_threadattr_t * attr,
        axutil_thread_start_t func,
        void *data);

    /**
     * Stop the current thread
     * @param thd The thread to stop
     * @return The status of the operation
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_exit(
        axutil_thread_t * thd,
        axutil_allocator_t * allocator);

    /**
     * Block until the desired thread stops executing.
     * @param thd The thread to join
     * @return The status of the operation
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_join(
        axutil_thread_t * thd);

    /**
     * force the current thread to yield the processor
     */
    AXIS2_EXTERN void AXIS2_CALL
    axutil_thread_yield(void
    );

    /**
     * function is used to allocate a new key. This key now becomes valid for all threads in our process. 
     * When a key is created, the value it points to defaults to NULL. Later on each thread may change 
     * its copy of the value as it wishes.
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_key_create(
        axutil_threadkey_t * axis2_key);
    /**
     * This function is used to get the value of a given key
     * @return void*. A key's value is simply a void pointer (void*)
     */
    AXIS2_EXTERN void *AXIS2_CALL
    axutil_thread_getspecific(
        axutil_threadkey_t * axis2_key);

    /**
     * This function is used to get the value of a given key
     * @param keys value. A key's value is simply a void pointer (void*), so we can 
     *        store in it anything that we want
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_setspecific(
        axutil_threadkey_t * axis2_key,
        void *value);

    /**
     * This function is used free the tls key.
     */
    AXIS2_EXTERN void AXIS2_CALL
    axutil_thread_key_free(
        axutil_threadkey_t * axis2_key);

    /**
     * Initialize the control variable for axutil_thread_once.
     * @param control The control variable to initialize
     * @return The status of the operation
     */
    AXIS2_EXTERN axutil_thread_once_t *AXIS2_CALL
    axutil_thread_once_init(
        axutil_allocator_t * allocator);

    /**
     * Run the specified function one time, regardless of how many threads
     * call it.
     * @param control The control variable.  The same variable should
     *                be passed in each time the function is tried to be
     *                called.  This is how the underlying functions determine
     *                if the function has ever been called before.
     * @param func The function to call.
     * @return The status of the operation
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_once(
        axutil_thread_once_t * control,
        void(*func)(void));

    /**
     * detach a thread
     * @param thd The thread to detach
     * @return The status of the operation
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_detach(
        axutil_thread_t * thd);


    /*************************Thread locking functions*****************************/

    /** Opaque thread-local mutex structure */
    typedef struct axutil_thread_mutex_t axutil_thread_mutex_t;

#define AXIS2_THREAD_MUTEX_DEFAULT  0x0   /**< platform-optimal lock behavior */

#define AXIS2_THREAD_MUTEX_NESTED   0x1   /**< enable nested (recursive) locks */

#define AXIS2_THREAD_MUTEX_UNNESTED 0x2   /**< disable nested locks */

    /**
     * Create and initialize a mutex that can be used to synchronize threads.
     * @param allocator Memory allocator to allocate memory for the mutex
     * @warning Be cautious in using AXIS2_THREAD_MUTEX_DEFAULT.  While this is the
     * most optimal mutex based on a given platform's performance characteristics,
     * it will behave as either a nested or an unnested lock.
     */
    AXIS2_EXTERN axutil_thread_mutex_t *AXIS2_CALL

    axutil_thread_mutex_create(
        axutil_allocator_t * allocator,
        unsigned int flags);

    /**
     * Acquire the lock for the given mutex. If the mutex is already locked,
     * the current thread will be put to sleep until the lock becomes available.
     * @param mutex the mutex on which to acquire the lock.
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_mutex_lock(
        axutil_thread_mutex_t * mutex);

    /**
     * Attempt to acquire the lock for the given mutex. If the mutex has already
     * been acquired, the call returns immediately
     * @param mutex the mutex on which to attempt the lock acquiring.
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_mutex_trylock(
        axutil_thread_mutex_t * mutex);

    /**
     * Release the lock for the given mutex.
     * @param mutex the mutex from which to release the lock.
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_mutex_unlock(
        axutil_thread_mutex_t * mutex);

    /**
     * Destroy the mutex and free the memory associated with the lock.
     * @param mutex the mutex to destroy.
     */
    AXIS2_EXTERN axis2_status_t AXIS2_CALL
    axutil_thread_mutex_destroy(
        axutil_thread_mutex_t * mutex);

    /** @} */
#ifdef __cplusplus
}
#endif

#endif                          /* AXIS2_THREAD_H */
