
/*
 * Copyright (C) Igor Sysoev
 * Copyright (C) NGINX, Inc.
 */

#ifndef _NXT_UNIX_THREAD_H_INCLUDED_
#define _NXT_UNIX_THREAD_H_INCLUDED_


/*
 * Thread Specific Data
 *
 * The interface unifies two TSD implementations: the __thread storage
 * class and pthread specific data.  It works also in non-threaded mode.
 * The interface is optimized for the __thread storage class and non-threaded
 * mode, since the __thread storage is faster and is supported in modern
 * versions of Linux, FreeBSD, Solaris, and MacOSX.  Pthread specific data
 * is considered as a fallback option.
 *
 * The underlining interfaces are different: pthread data must be allocated
 * by hand and may be accessed only by using pointers whereas __thread data
 * allocation is transparent and it is accessed directly.
 *
 * pthread_getspecific() is usually faster than pthread_setspecific()
 * (much faster on MacOSX), so there is no nxt_thread_set_data() interface
 * for this reason.  It is better to store frequently alterable thread
 * log pointer in nxt_thread_t, but not in a dedicated key.
 */

#if (NXT_HAVE_THREAD_STORAGE_CLASS)

#define                                                                       \
nxt_thread_extern_data(type, tsd)                                             \
    NXT_EXPORT extern __thread type  tsd

#define                                                                       \
nxt_thread_declare_data(type, tsd)                                            \
    __thread type  tsd

#define                                                                       \
nxt_thread_init_data(tsd)

#define                                                                       \
nxt_thread_get_data(tsd)                                                      \
    &tsd


#else /* NXT_HAVE_PTHREAD_SPECIFIC_DATA */

/*
 * nxt_thread_get_data() is used as
 *    p = nxt_thread_get_data(tsd),
 * but the tsd address is actually required.  This could be resolved by macro
 *    #define nxt_thread_get_data(tsd)  nxt_thread_get_data_addr(&tsd)
 * or by definition nxt_thread_specific_data_t as an array.
 *
 * On Linux and Solaris pthread_key_t is unsigned integer.
 * On FreeBSD, NetBSD, OpenBSD, and HP-UX pthread_key_t is integer.
 * On MacOSX and AIX pthread_key_t is unsigned long integer.
 * On Cygwin pthread_key_t is pointer to void.
 */

typedef struct {
    nxt_atomic_t             key;
    size_t                   size;
} nxt_thread_specific_data_t[1];


#define                                                                       \
nxt_thread_extern_data(type, tsd)                                             \
    NXT_EXPORT extern nxt_thread_specific_data_t  tsd

#define                                                                       \
nxt_thread_declare_data(type, tsd)                                            \
    nxt_thread_specific_data_t tsd = { { (nxt_atomic_int_t) -1, sizeof(type) } }

NXT_EXPORT void nxt_thread_init_data(nxt_thread_specific_data_t tsd);

#define                                                                       \
nxt_thread_get_data(tsd)                                                      \
    pthread_getspecific((pthread_key_t) tsd->key)

#endif


typedef void (*nxt_thread_start_t)(void *data);

typedef struct {
    nxt_thread_start_t       start;
    nxt_event_engine_t       *engine;
    nxt_work_t               work;
} nxt_thread_link_t;


NXT_EXPORT nxt_int_t nxt_thread_create(nxt_thread_handle_t *handle,
    nxt_thread_link_t *link);
NXT_EXPORT nxt_thread_t *nxt_thread_init(void);
NXT_EXPORT void nxt_thread_exit(nxt_thread_t *thr);
NXT_EXPORT void nxt_thread_cancel(nxt_thread_handle_t handle);
NXT_EXPORT void nxt_thread_wait(nxt_thread_handle_t handle);


#define                                                                       \
nxt_thread_handle()                                                           \
    pthread_self()


typedef pthread_mutex_t      nxt_thread_mutex_t;

NXT_EXPORT nxt_int_t nxt_thread_mutex_create(nxt_thread_mutex_t *mtx);
NXT_EXPORT void nxt_thread_mutex_destroy(nxt_thread_mutex_t *mtx);
NXT_EXPORT nxt_int_t nxt_thread_mutex_lock(nxt_thread_mutex_t *mtx);
NXT_EXPORT nxt_bool_t nxt_thread_mutex_trylock(nxt_thread_mutex_t *mtx);
NXT_EXPORT nxt_int_t nxt_thread_mutex_unlock(nxt_thread_mutex_t *mtx);


typedef pthread_cond_t       nxt_thread_cond_t;

NXT_EXPORT nxt_int_t nxt_thread_cond_create(nxt_thread_cond_t *cond);
NXT_EXPORT void nxt_thread_cond_destroy(nxt_thread_cond_t *cond);
NXT_EXPORT nxt_int_t nxt_thread_cond_signal(nxt_thread_cond_t *cond);
NXT_EXPORT nxt_err_t nxt_thread_cond_wait(nxt_thread_cond_t *cond,
    nxt_thread_mutex_t *mtx, nxt_nsec_t timeout);


#if (NXT_HAVE_PTHREAD_YIELD)
#define                                                                       \
nxt_thread_yield()                                                            \
    pthread_yield()

#elif (NXT_HAVE_PTHREAD_YIELD_NP)
#define                                                                       \
nxt_thread_yield()                                                            \
    pthread_yield_np()

#else
#define                                                                       \
nxt_thread_yield()                                                            \
    nxt_sched_yield()

#endif


#if (PTHREAD_STACK_MIN)
#define NXT_THREAD_STACK_MIN  PTHREAD_STACK_MIN

#else
#define NXT_THREAD_STACK_MIN  sysconf(_SC_THREAD_STACK_MIN)
#endif


struct nxt_thread_s {
    nxt_log_t                *log;
    nxt_log_t                main_log;

    nxt_task_t               *task;

    nxt_tid_t                tid;
    nxt_thread_handle_t      handle;
    nxt_thread_link_t        *link;
    nxt_thread_pool_t        *thread_pool;

    nxt_thread_time_t        time;

    nxt_runtime_t            *runtime;
    nxt_event_engine_t       *engine;
    void                     *data;

#if 0
    /*
     * Although pointer to a current fiber should be a property of
     * engine->fibers, its placement here eliminates 2 memory accesses.
     */
    nxt_fiber_t              *fiber;
#endif

    nxt_random_t             random;
};


#endif /* _NXT_UNIX_THREAD_H_INCLUDED_ */
