/*
 * Copyright (c) 2009, 2010, 2012, ETH Zurich.
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
 */

#ifndef LIBBARRELFISH_THREADS_PRIV_H
#define LIBBARRELFISH_THREADS_PRIV_H

#include <aos/dispatcher_arch.h>
#include <aos/except.h>

/// Maximum number of thread-local storage keys
#define MAX_TLS         16

/** \brief TLS dynamic thread vector data structure
 *
 * See: ELF handling for thread-local storage. Ulrich Drepper, Dec 2005.
 * http://www.akkadia.org/drepper/tls.pdf
 */
struct tls_dtv {
    uintptr_t gen; ///< Generation count
    void *dtv[0];  ///< Variable-length array of pointers to TLS blocks
};

enum thread_state {
    THREAD_STATE_NULL = 0,
    THREAD_STATE_RUNNABLE,
    THREAD_STATE_BLOCKED,
    THREAD_STATE_EXITED
};

/** \brief A thread of execution / thread control block (TCB)
 *
 * NB: on some ABIs (namely x86_{32,64}), the TLS blocks for initially-loaded
 * (i.e. not dlopen()ed) modules _precede_ this structure in memory. Therefore
 * it's not safe to directly malloc() or free() a thread structure.
 */
struct thread {
    /* XXX: The offsets of the first two fields (self pointer and dispatcher
     * pointer) are depended upon by the ABI and/or assembly code. Don't change!
     */
    struct thread       *self;              ///< Points to itself
    dispatcher_handle_t disp;               ///< Dispatcher affinity
    struct tls_dtv      *tls_dtv;           ///< TLS thread vector
    struct thread       *next, *prev;       ///< Next/prev threads in list
    arch_registers_state_t regs  __attribute__ ((aligned (16)));            ///< Register state snapshot
    void                *stack;             ///< Malloced stack area
    void                *stack_top;         ///< Stack bounds
    void                *exception_stack;   ///< Stack for exception handling
    void                *exception_stack_top; ///< Bounds of exception stack
    exception_handler_fn exception_handler; ///< Exception handler, or NULL
    void                *userptr;           ///< User's thread local pointer
    void                *userptrs[MAX_TLS]; ///< User's thread local pointers
    uintptr_t           yield_epoch;        ///< Yield epoch
    void                *wakeup_reason;     ///< Value returned from block()
    coreid_t            coreid;             ///< XXX: Core ID affinity
    int                 return_value;       ///< Value returned on exit
    struct thread_cond  exit_condition;     ///< Thread exit condition
    struct thread_mutex exit_lock;          ///< Protects exited state
    enum thread_state   state;              ///< Thread state
    bool                paused;             ///< Thread is paused (not runnable)
    bool                detached;           ///< true if detached
    bool                joining;            ///< true if someone is joining
    bool                in_exception;       ///< true if running exception handler
#if defined(__x86_64__)
    uint16_t            thread_seg_selector; ///< Segment selector for TCB
#endif
    void                *slab;              ///< Base of slab block containing this TCB
    uintptr_t           id;                 ///< User-defined thread identifier

    uint32_t            token_number;	    ///< RPC next token
    uint32_t            token;	            ///< Token to be received
    struct waitset_chanstate *channel;      ///< on right channel

    bool    rpc_in_progress;	            ///< RPC in progress
    errval_t    async_error;                ///< RPC async error
    uint32_t    outgoing_token;             ///< Token of outgoing message
    struct waitset_chanstate *local_trigger; ///< Trigger for a local thread event
};

void thread_enqueue(struct thread *thread, struct thread **queue);
struct thread *thread_dequeue(struct thread **queue);
void thread_remove_from_queue(struct thread **queue, struct thread *thread);

/* must only be called by dispatcher, while disabled */
void thread_init_disabled(dispatcher_handle_t handle, bool init_domain);

/// Returns true if there is non-threaded work to be done on this dispatcher
/// (ie. if we still need to run)
static inline bool havework_disabled(dispatcher_handle_t handle)
{
    struct dispatcher_generic *disp = get_dispatcher_generic(handle);
    return disp->runq != NULL
            || disp->polled_channels != NULL
            || disp->notificators != NULL
            ;
}

void *thread_block(struct thread **queue);
void *thread_block_disabled(dispatcher_handle_t handle, struct thread **queue);
void *thread_block_and_release_spinlock_disabled(dispatcher_handle_t handle,
                                                 struct thread **queue,
                                                 spinlock_t *spinlock);
struct thread *thread_unblock_one(struct thread **queue, void *reason);
struct thread *thread_unblock_one_disabled(dispatcher_handle_t handle,
                                           struct thread **queue, void *reason);
struct thread *thread_unblock_all_disabled(dispatcher_handle_t handle,
                                           struct thread **queue, void *reason);

struct thread *thread_create_unrunnable(thread_func_t start_func, void *arg,
                                        size_t stacksize);

void thread_init_remote(dispatcher_handle_t handle, struct thread *thread);

void thread_run_disabled(dispatcher_handle_t handle);
void thread_deliver_exception_disabled(dispatcher_handle_t handle,
                                       enum exception_type type, int subtype,
                                       void *addr, arch_registers_state_t *regs);

#endif // LIBBARRELFISH_THREADS_PRIV_H
