/* cilk_fiber.h                  -*-C++-*-
 *
 *************************************************************************
 *
 *  @copyright
 *  Copyright (C) 2012-2013, Intel Corporation
 *  All rights reserved.
 *  
 *  @copyright
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the
 *      distribution.
 *    * Neither the name of Intel Corporation nor the names of its
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *  
 *  @copyright
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 *  WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 **************************************************************************/

/**
 * @file cilk_fiber.h
 *
 * @brief Abstraction of a "fiber": A coprocess-like stack and auxiliary data
 */

#ifndef INCLUDED_CILK_FIBER_DOT_H
#define INCLUDED_CILK_FIBER_DOT_H

#include <cilk/common.h>
#ifdef __cplusplus
#   include <cstddef>
#else
#   include <stddef.h>
#endif

#include "bug.h"
#include "cilk-tbb-interop.h"
#include "spin_mutex.h"
#include "internal/abi.h"       // Define __cilkrts_stack_frame

/**
 * @brief Debugging level for Cilk fiber code.
 *
 * A value of 0 means no debugging.
 * Higher values generate more debugging output.
 */
#define FIBER_DEBUG 0

/**
 * @brief Flag for validating reference counts.
 * 
 * Set to 1 to assert that fiber reference counts are reasonable.
 */
#define FIBER_CHECK_REF_COUNTS 1

/**
 * @brief Flag to determine whether fibers support reference counting.
 * We require reference counting only on Windows, for exception
 * processing.  Unix does not need reference counting.
 */
#if defined(_WIN32)
#   define NEED_FIBER_REF_COUNTS 1
#endif

/**
 * @brief Flag to enable support for the
 * cilk_fiber_get_current_fiber() method.
 *
 * I'd like this flag to be 0.  However, the cilk_fiber test depends
 * on being able to call this method.
 */
#if !defined(SUPPORT_GET_CURRENT_FIBER)
#   define SUPPORT_GET_CURRENT_FIBER 0
#endif

/**
 * @brief Switch for enabling "fast path" check for fibers, which
 * doesn't go to the heap or OS until checking the ancestors first.
 *
 * Doing this check seems to make the stress test in
 * cilk_fiber_pool.t.cpp run faster.  But it doesn't seem to make much
 * difference in other benchmarks, so it is disabled by default.
 */
#define USE_FIBER_TRY_ALLOCATE_FROM_POOL 0


__CILKRTS_BEGIN_EXTERN_C

/// @brief Forward reference to fiber pool.
typedef struct cilk_fiber_pool cilk_fiber_pool;

/** @brief Opaque data structure representing a fiber */
typedef struct cilk_fiber cilk_fiber;

/** @brief Function pointer type for use as a fiber's "main" procedure */
typedef void (*cilk_fiber_proc)(cilk_fiber*);

/** @brief Data structure associated with each fiber. */
typedef struct cilk_fiber_data
{
    __STDNS size_t          stack_size;       /**< Size of stack for fiber    */
    __cilkrts_worker*       owner;            /**< Worker using this fiber    */
    __cilkrts_stack_frame*  resume_sf;        /**< Stack frame to resume      */
    __cilk_tbb_pfn_stack_op stack_op_routine; /**< Cilk/TBB interop callback  */
    void*                   stack_op_data;    /**< Data for Cilk/TBB callback */
    void*                   client_data;      /**< Data managed by client     */

#ifdef _WIN32
    char *initial_sp;       /**<  Initalized in fiber_stub */
# ifdef _WIN64
    char *steal_frame_sp;   /**< RSP for frame stealing work */
                            // Needed for exception handling so we can
                            // identify when about to unwind off stack
# endif
#endif

} cilk_fiber_data;

/** @brief Pool of cilk_fiber for fiber reuse
 *
 * Pools form a hierarchy, with each pool pointing to its parent.  When the
 * pool undeflows, it gets a fiber from its parent.  When a pool overflows,
 * it returns some fibers to its parent.  If the root pool underflows, it
 * allocates and initializes a new fiber from the heap but only if the total
 * is less than max_size; otherwise, fiber creation fails.
 */
struct cilk_fiber_pool
{
    spin_mutex*      lock;       ///< Mutual exclusion for pool operations 
    __STDNS size_t   stack_size; ///< Size of stacks for fibers in this pool.
    cilk_fiber_pool* parent;     ///< @brief Parent pool.
                                 ///< If this pool is empty, get from parent 

    // Describes inactive fibers stored in the pool.
    cilk_fiber**     fibers;     ///< Array of max_size fiber pointers 
    unsigned         max_size;   ///< Limit on number of fibers in pool 
    unsigned         size;       ///< Number of fibers currently in the pool

    // Statistics on active fibers that were allocated from this pool,
    // but no longer in the pool.
    int              total;      ///< @brief Fibers allocated - fiber deallocated from pool
                                 ///< total may be negative for non-root pools.
    int              high_water; ///< High water mark of total fibers
    int              alloc_max;  ///< Limit on number of fibers allocated from the heap/OS
};

/** @brief Initializes a cilk_fiber_pool structure
 *
 * @param pool         - The address of the pool that is to be initialized
 * @param parent       - The address of this pool's parent, or NULL for root pool
 * @param stack_size   - Size of stacks for fibers allocated from this pool.
 * @param buffer_size  - The maximum number of fibers that may be pooled.
 * @param alloc_max    - Limit on # of fibers this pool can allocate from the heap.
 * @param is_shared    - True if accessing this pool needs a lock, false otherwise.
 */
void cilk_fiber_pool_init(cilk_fiber_pool* pool,
                          cilk_fiber_pool* parent,
                          size_t           stack_size,
                          unsigned         buffer_size,
                          int              alloc_max,
                          int              is_shared);

/** @brief Sets the maximum number of fibers to allocate from a root pool.
 *
 * @param root_pool              - A root fiber pool
 * @param max_fibers_to_allocate - The limit on # of fibers to allocate.
 *
 * Sets the maximum number of fibers that can be allocated from this
 * pool and all its descendants.  This pool must be a root pool.
 */
void cilk_fiber_pool_set_fiber_limit(cilk_fiber_pool* root_pool,
                                     unsigned max_fibers_to_allocate);

/** @brief De-initalizes a cilk_fiber_pool
 *
 * @param pool - The address of the pool that is to be destroyed
 */
void cilk_fiber_pool_destroy(cilk_fiber_pool* pool);

/** @brief Allocates a new cilk_fiber.
 *
 * If the specified pool is empty, this method may choose to either
 * allocate a fiber from the heap (if pool->total < pool->alloc_max),
 * or retrieve a fiber from the parent pool.
 *
 * @note If a non-null fiber is returned, @c cilk_fiber_reset_state
 * should be called on this fiber before using it.
 *
 * An allocated fiber begins with a reference count of 1.
 * This method may lock @c pool or one of its ancestors.
 *
 * @pre pool should not be NULL.
 *
 * @param pool         The fiber pool from which to retrieve a fiber.
 * @return             An allocated fiber, or NULL if failed to allocate.
 */
cilk_fiber* cilk_fiber_allocate(cilk_fiber_pool* pool);

/** @brief Allocate and initialize a new cilk_fiber using memory from
 * the heap and/or OS.
 *
 * The allocated fiber begins with a reference count of 1.
 *
 * @param stack_size   The size (in bytes) to be allocated for the fiber's
 *                     stack.
 * @return             An initialized fiber.  This method should not return NULL
 *                     unless some exceptional condition has occurred.
 */
cilk_fiber* cilk_fiber_allocate_from_heap(size_t stack_size);


/** @brief Resets an fiber object just allocated from a pool with the
 * specified proc.
 *
 * After this call, cilk_fiber_data object associated with this fiber
 * is filled with zeros.
 *
 * This function can be called only on a fiber that has been allocated
 * from a pool, but never used.
 *
 * @param fiber        The fiber to reset and initialize. 
 * @param start_proc   The function to run when switching to the fiber.  If
 *                     null, the fiber can be used with cilk_fiber_run_proc()
 *                     but not with cilk_fiber_resume().
 */
void cilk_fiber_reset_state(cilk_fiber* fiber,
                            cilk_fiber_proc start_proc);

/** @brief Remove a reference from this fiber, possibly deallocating it.
 *
 * This fiber is deallocated only when there are no other references
 * to it.  Deallocation happens either by returning the fiber to the
 * specified pool, or returning it to the heap.
 *
 * A fiber that is currently executing should not remove the last
 * reference to itself.
 *
 * When a fiber is deallocated, destructors are not called for the
 * objects (if any) still on its stack.  The fiber's stack and fiber
 * data is returned to the stack pool but the client fiber data is not
 * deallocated.
 *
 * If the pool overflows because of a deallocation, then some fibers
 * will be returned to the parent pool.  If the root pool overflows,
 * then the fiber is returned to the heap.
 *
 * @param fiber   The Cilk fiber to remove a reference to.
 * @param pool    The fiber pool to which the fiber should be returned.  The
 *                caller is assumed to have exclusive access to the pool
 *                either because there is no contention for it or because
 *                its lock has been acquired.  If pool is NULL, any
 *                deallocated fiber is destroyed and returned to the
 *                heap.
 *
 * @return        Final reference count.  If the count is 0, the fiber was
 *                returned to a pool or the heap.
 */
int cilk_fiber_remove_reference(cilk_fiber *fiber, cilk_fiber_pool *pool);

/** @brief Allocates and intializes this thread's main fiber
 *
 * Each thread has an "implicit" main fiber that control's the
 * thread's initial stack.  This function makes this fiber visible to
 * the client and allocates the Cilk-specific aspects of the implicit
 * fiber.  A call to this function must be paired with a call to
 *   cilk_fiber_deallocate_fiber_from_thread() 
 * or a memory leak (or worse) will result.
 *
 * A fiber allocated from a thread begins with a reference count of 2.
 * One is for being allocated, and one is for being active.
 * (A fiber created from a thread is automatically currently executing.)
 * The matching calls above each decrement the reference count by 1.
 *
 * @return  A fiber for the currently executing thread.
 */
cilk_fiber* cilk_fiber_allocate_from_thread(void);

/** @brief Remove  a fiber created from a thread,
 * possibly deallocating it.
 *
 * Same as cilk_fiber_remove_reference, except that it works on fibers
 * created via cilk_fiber_allocate_from_thread().
 *
 * Fibers created from a thread are never returned to a pool.
 *
 * @param fiber   The Cilk fiber to remove a reference from.
 * @return        Final reference count.  If the count is 0, the fiber was
 *                returned to the heap.
 */
int cilk_fiber_remove_reference_from_thread(cilk_fiber *fiber);

/** @brief Deallocate a fiber created from a thread,
 * possibly destroying it.
 *
 * This method decrements the reference count of the fiber by 2, and
 * destroys the fiber struct if the reference count is 0.
 *
 * OS-specific cleanup for the fiber executes unconditionally with 
 * this method.  The destruction of the actual object, however, does
 * not occur unless the reference count is 0.
 *
 * @param fiber   The cilk_fiber to deallocate from a thread.
 * @return        Final reference count.  If the count is 0, the fiber was
 *                returned to the heap.
 */
int cilk_fiber_deallocate_from_thread(cilk_fiber *fiber);

/** @brief Returns true if this fiber is allocated from a thread.
 */
int cilk_fiber_is_allocated_from_thread(cilk_fiber *fiber);


/** @brief Suspend execution on current fiber resumes other fiber.
 *
 * Suspends the current fiber and transfers control to a new fiber.  Execution
 * on the new fiber resumes from the point at which fiber suspended itself to
 * run a different fiber.  If fiber was freshly allocated, then runs the
 * start_proc function specified at allocation.  This function returns when
 * another fiber resumes the self fiber.  Note that the state of the
 * floating-point control register (i.e., the register that controls rounding
 * mode, etc.) is valid but indeterminate on return -- different
 * implementations will have different results.
 *
 * When the @c self fiber is resumed, execution proceeds as though
 * this function call returns.
 *
 * This operation increments the reference count of @p other.
 * This operation decrements the reference count of @p self.
 *
 * @param self               Fiber to switch from.  Must equal current fiber.
 * @param other              Fiber to switch to.
 */
void cilk_fiber_suspend_self_and_resume_other(cilk_fiber* self,
                                              cilk_fiber* other);

/** @brief Removes a reference from the currently executing fiber and
 * resumes other fiber.
 *
 * Removes a reference from @p self and transfer control to @p other
 * fiber.  Execution on @p other resumes from the point at which @p
 * other suspended itself to run a different fiber.  If @p other fiber
 * was freshly allocated, then runs the function specified at
 * creation.
 *
 *
 * This operation increments the reference count of @p other.
 * 
 * This operation conceptually decrements the reference count of
 * @p self twice, once to suspend it, and once to remove a reference to
 * it.  Then, if the count is 0, it is returned to the specified pool
 * or destroyed.
 *
 * @pre @p self is the currently executing fiber.
 *
 * @param self               Fiber to remove reference switch from. 
 * @param self_pool          Pool to which the current fiber should be returned
 * @param other              Fiber to switch to.
 */
NORETURN
cilk_fiber_remove_reference_from_self_and_resume_other(cilk_fiber*      self,
                                                       cilk_fiber_pool* self_pool,
                                                       cilk_fiber*      other);

/** @brief Set the proc method to execute immediately after a switch
 * to this fiber.
 *
 * The @c post_switch_proc method executes immediately after switching
 * away form @p self fiber to some other fiber, but before @c self
 * gets cleaned up.
 * 
 * @note A fiber can have only one post_switch_proc method at a time.
 * If this method is called multiple times before switching to the
 * fiber, only the last proc method will execute.
 *
 * @param self              Fiber.
 * @param post_switch_proc  Proc method to execute immediately after switching to this fiber.
 */
void cilk_fiber_set_post_switch_proc(cilk_fiber* self, cilk_fiber_proc post_switch_proc);

/** @brief Invoke TBB stack op for this fiber.
 *
 * @param fiber Fiber to invoke stack op for.
 * @param op    The stack op to invoke
 */
void cilk_fiber_invoke_tbb_stack_op(cilk_fiber* fiber, __cilk_tbb_stack_op op);

/** @brief Returns the fiber data associated with the specified fiber.
 *
 * The returned struct is owned by the fiber and is deallocated automatically
 * when the fiber is destroyed.  However, the client_data field is owned by
 * the client and must be deallocated separately.  When called for a
 * newly-allocated fiber, the returned data is zero-filled.
 *
 * @param fiber   The fiber for which data is being requested.
 * @return        The fiber data for the specified fiber
 */
cilk_fiber_data* cilk_fiber_get_data(cilk_fiber* fiber);

/** @brief Retrieve the owner field from the fiber.
 *
 *  This method is provided for convenience.  One can also get the
 *  fiber data, and then get the owner field.
 */
__CILKRTS_INLINE
__cilkrts_worker* cilk_fiber_get_owner(cilk_fiber* fiber)
{
    // TBD: We really want a static assert here, that this cast is
    // doing the right thing.
    cilk_fiber_data* fdata = (cilk_fiber_data*)fiber;
    return fdata->owner;
}

/** @brief Sets the owner field of a fiber.
 *
 *  This method is provided for convenience.  One can also get the
 *  fiber data, and then get the owner field.
 */
__CILKRTS_INLINE
void cilk_fiber_set_owner(cilk_fiber* fiber, __cilkrts_worker* owner) 
{
    // TBD: We really want a static assert here, that this cast is
    // doing the right thing.
    cilk_fiber_data* fdata = (cilk_fiber_data*)fiber;
    fdata->owner = owner;
}
    
/** @brief Returns true if this fiber is resumable.
 *
 * A fiber is considered resumable when it is not currently being
 * executed.
 *
 * This function is used by Windows exception code.
 * @param fiber   The fiber to check.
 * @return        Nonzero value if fiber is resumable.
 */
int cilk_fiber_is_resumable(cilk_fiber* fiber);

/**
 * @brief Returns the base of this fiber's stack.
 *
 * On some platforms (e.g., Windows), the fiber must have started
 * running before we can get this information.
 *
 * @param fiber   The fiber to get the stack pointer from.
 * @return        The base of the stack, or NULL if this
 *                information is not available yet.
 */
char* cilk_fiber_get_stack_base(cilk_fiber* fiber);


/****************************************************************************
 * TBB interop functions
 * **************************************************************************/
/**
 * @brief Set the TBB callback information for a stack
 *
 * @param fiber The fiber to set the TBB callback information for
 * @param o     The TBB callback thunk.  Specifies the callback address and
 *              context value.
 */
void cilk_fiber_set_stack_op(cilk_fiber *fiber,
                             __cilk_tbb_stack_op_thunk o);
                     
/**
 * @brief Save the TBB callback address and context value in
 * thread-local storage.
 *
 * We'll use it later when the thread binds to a worker.
 *
 * @param o The TBB callback thunk which is to be saved.
 */
void cilk_fiber_tbb_interop_save_stack_op_info(__cilk_tbb_stack_op_thunk o);

/**
 * @brief Move TBB stack-op info from thread-local storage and store
 * it into the fiber.
 *
 * Called when we bind a thread to the runtime.  If there is any TBB
 * interop information in thread-local storage, bind it to the stack
 * now.
 *
 * @pre \c fiber should not be NULL.
 * @param fiber The fiber that should take over the TBB interop information.
 */
void cilk_fiber_tbb_interop_use_saved_stack_op_info(cilk_fiber *fiber);

/**
 * @brief Free any TBB interop information saved in thread-local storage
 */
void cilk_fiber_tbb_interop_free_stack_op_info(void);

/**
 * @brief Migrate any TBB interop information from a cilk_fiber to
 * thread-local storage.
 *
 * Returns immediately if no TBB interop information has been
 * associated with the stack.
 *
 * @param fiber The cilk_fiber who's TBB interop information should be
 * saved in thread-local storage.
 */
void cilk_fiber_tbb_interop_save_info_from_stack(cilk_fiber* fiber);


#if SUPPORT_GET_CURRENT_FIBER
/** @brief Returns the fiber associated with the currently executing thread
 *
 * @note This function is currently used only for testing the Cilk
 * runtime.
 *
 * @return Fiber associated with the currently executing thread or NULL if no
 *         fiber was associated with this thread.
 */
cilk_fiber* cilk_fiber_get_current_fiber(void);
#endif


#if NEED_FIBER_REF_COUNTS 
/** @brief Returns true if this fiber has reference count > 0.
 * 
 * @param fiber   The fiber to check for references.
 * @return        Nonzero value if the fiber has references.
 */
int cilk_fiber_has_references(cilk_fiber *fiber);

/** @brief Returns the value of the reference count.
 *
 * @param fiber   The fiber to check for references.
 * @return        The value of the reference count of fiber.
 */
int cilk_fiber_get_ref_count(cilk_fiber *fiber);

/** @brief Adds a reference to this fiber.
 *
 *  Increments the reference count of a current fiber.  Fibers with
 *  nonzero reference count will not be freed or returned to a fiber
 *  pool.
 *
 * @param fiber   The fiber to add a reference to.
 */
void cilk_fiber_add_reference(cilk_fiber *fiber);

#endif // NEED_FIBER_REF_COUNTS

__CILKRTS_END_EXTERN_C

#ifdef __cplusplus
// Some C++ implementation details

/// Opaque declaration of a cilk_fiber_sysdep object.
struct cilk_fiber_sysdep;

/**
 * cilk_fiber is a base-class for system-dependent fiber implementations.
 */
struct cilk_fiber : protected cilk_fiber_data
{
  protected:
    // This is a rare acceptable use of protected inheritence and protected
    // variable access: when the base class and derived class collaborate
    // tightly to comprise a single component.

    /// For overloading constructor of cilk_fiber. 
    enum from_thread_t { from_thread = 1 };

    // Boolean flags capturing the status of the fiber.
    // Each one can be set independently.
    // A default fiber is constructed with a flag value of 0.
    static const int RESUMABLE             = 0x01;  ///< True if the fiber is in a suspended state and can be resumed.
    static const int ALLOCATED_FROM_THREAD = 0x02;  ///< True if fiber was allocated from a thread.

    cilk_fiber_proc  m_start_proc;        ///< Function to run on start up/reset
    cilk_fiber_proc  m_post_switch_proc;  ///< Function that executes when we first switch to a new fiber from a different one.

    cilk_fiber*      m_pending_remove_ref;///< Fiber to possibly delete on start up or resume
    cilk_fiber_pool* m_pending_pool;      ///< Pool where m_pending_remove_ref should go if it is deleted.
    unsigned         m_flags;             ///< Captures the status of this fiber. 

#if NEED_FIBER_REF_COUNTS
    volatile long    m_outstanding_references;  ///< Counts references to this fiber.
#endif

    /// Creates a fiber with NULL data.
    cilk_fiber();

    /**
     * @brief Creates a fiber with user-specified arguments.
     *
     * @param stack_size   Size of stack to use for this fiber.
     */
    cilk_fiber(std::size_t stack_size);

    /// Empty destructor.
    ~cilk_fiber();

    /**
     * @brief Performs any actions that happen after switching from
     * one fiber to another.
     *
     * These actions are:
     *   1. Execute m_post_switch_proc on a fiber.
     *   2. Do any pending deallocations from the previous fiber.
     */
    void do_post_switch_actions();

    /**
     *@brief Helper method that converts a @c cilk_fiber object into a
     * @c cilk_fiber_sysdep object.
     *
     * The @c cilk_fiber_sysdep object contains the system-dependent parts
     * of the implementation of a @\c cilk_fiber.
     *
     * We could have @c cilk_fiber_sysdep inherit from @c cilk_fiber and
     * then use virtual functions.  But since a given platform only uses
     * one definition of @c cilk_fiber_sysdep at a time, we statically
     * cast between them.
     */
    inline cilk_fiber_sysdep* sysdep();

    /**
     * @brief Set resumable flag to specified state.
     */
    inline void set_resumable(bool state) {
        m_flags = state ?  (m_flags | RESUMABLE) : (m_flags & (~RESUMABLE));
    }

    /**
     *@brief Set the allocated_from_thread flag. 
     */
    inline void set_allocated_from_thread(bool state) {
        m_flags = state ?  (m_flags | ALLOCATED_FROM_THREAD) : (m_flags & (~ALLOCATED_FROM_THREAD));
    }

  public:

    /**
     * @brief Allocates and initializes a new cilk_fiber, either from
     * the specified pool or from the heap.
     *
     * @pre pool should not be NULL.
     */
    static cilk_fiber* allocate(cilk_fiber_pool* pool);

    /**
     * @brief Allocates a fiber from the heap.
     */
    static cilk_fiber* allocate_from_heap(size_t stack_size);

    /**
     * @brief Return a fiber to the heap.
     */
    void deallocate_to_heap();

    /**
     * @brief Reset the state of a fiber just allocated from a pool.
     */
    void reset_state(cilk_fiber_proc start_proc);

    /**
     * @brief Remove a reference from this fiber, possibly
     * deallocating it if the reference count becomes 0.
     *
     * @param pool The fiber pool to which this fiber should be returned.
     * @return     The final reference count.
     */
    int remove_reference(cilk_fiber_pool* pool);

    /**
     * @brief Deallocate the fiber by returning it to the pool.
     * @pre This method should only be called if the reference count
     * is 0.
     *
     * @param pool The fiber pool to return this fiber to. If NULL,
     *   fiber is returned to the heap.
     */
    void deallocate_self(cilk_fiber_pool *pool);

    /** @brief Allocates and intializes this thread's main fiber. */
    static cilk_fiber* allocate_from_thread();

    /** @brief Deallocate a fiber created from a thread,
     * possibly destroying it.
     *
     * This method decrements the reference count of this fiber by 2,
     * and destroys the fiber if the reference count is 0.
     *
     * OS-specific cleanup for the fiber executes unconditionally with for
     * this method.  The destruction of the actual object, however, does
     * not occur unless the reference count is 0.
     *
     * @return        Final reference count.  If the count is 0, the fiber was
     *                returned to the heap.
     */
    int deallocate_from_thread();

    /** @brief Removes a reference from this fiber.
     *
     * This method deallocates this fiber if the reference count
     * becomes 0.
     *
     * @pre     This fiber must be allocated from a thread.
     * @return  The final reference count of this fiber.
     */
    int remove_reference_from_thread();

#if SUPPORT_GET_CURRENT_FIBER
    /** @brief Get the current fiber from TLS.
     *
     * @note This function is only used for testing the runtime.
     */
    static cilk_fiber* get_current_fiber();
#endif

    /** @brief Suspend execution on current fiber resumes other fiber.
     * 
     * Control returns after resuming execution of the self fiber.
     */ 
    void suspend_self_and_resume_other(cilk_fiber* other);


    /** @brief Removes a reference from the currently executing fiber
     * and resumes other fiber.
     *
     *  This fiber may be returned to a pool or deallocated.
     */
    NORETURN remove_reference_from_self_and_resume_other(cilk_fiber_pool* self_pool,
                                                         cilk_fiber*      other);

    /** @brief Set the proc method to execute immediately after a switch
     * to this fiber.
     *
     * @param post_switch_proc Proc method to execute immediately
     * after switching to this fiber.
     */
    inline void set_post_switch_proc(cilk_fiber_proc post_switch_proc) {
        m_post_switch_proc = post_switch_proc;
    }

    /** @brief Returns true if this fiber is resumable.
     *
     * A fiber is considered resumable when it is not currently being
     * executed.
     */
    inline bool is_resumable(void) {
        return (m_flags & RESUMABLE);
    }
    
    /** @brief Returns true if fiber was allocated from a thread. */   
    inline bool is_allocated_from_thread(void) {
        return (m_flags & ALLOCATED_FROM_THREAD);
    }

    /**
     *@brief Get the address at the base of the stack for this fiber.
     */
    inline char* get_stack_base();
    
    /** @brief Return the data for this fiber. */ 
    cilk_fiber_data*       get_data()       { return this; }

    /** @brief Return the data for this fiber. */ 
    cilk_fiber_data const* get_data() const { return this; }

    
#if NEED_FIBER_REF_COUNTS
    /** @brief Verifies that this fiber's reference count equals v. */
    inline void assert_ref_count_equals(long v) {
    #if FIBER_CHECK_REF_COUNTS
        CILK_ASSERT(m_outstanding_references >= v);
    #endif
    }

    /** @brief Verifies that this fiber's reference count is at least v. */
    inline void assert_ref_count_at_least(long v) {
    #if FIBER_CHECK_REF_COUNTS
        CILK_ASSERT(m_outstanding_references >= v);
    #endif
    }

    /** @brief Get reference count. */
    inline long get_ref_count()        { return m_outstanding_references; }

    /** @brief Initialize reference count.
     *  Operation is not atomic.
     */
    inline void init_ref_count(long v) { m_outstanding_references = v; }

    // For Windows, updates to the fiber reference count need to be
    // atomic, because exceptions can live on a stack that we are not
    // currently executing on.  Thus, we can update the reference
    // count of a fiber we are not currently executing on.

    /** @brief Increment reference count for this fiber [Windows]. */
    inline void inc_ref_count()            { atomic_inc_ref_count(); }

    /** @brief Decrement reference count for this fiber [Windows]. */
    inline long dec_ref_count()            { return atomic_dec_ref_count(); }

    /** @brief Subtract v from the reference count for this fiber [Windows]. */
    inline long sub_from_ref_count(long v) { return atomic_sub_from_ref_count(v); }
#else  // NEED_FIBER_REF_COUNTS

    // Without reference counting, we have placeholder methods.
    inline void init_ref_count(long v) { }

    inline void inc_ref_count() { }
    
    // With no reference counting, dec_ref_count always return 0.
    // Thus, anyone checking is always the "last" one.
    inline long dec_ref_count() { return 0; }
    inline long sub_from_ref_count(long v) { return 0; }

    // The assert methods do nothing.
    inline void assert_ref_count_equals(long v) { }
    inline void assert_ref_count_at_least(long v) { }
#endif    

    /**
     * @brief Call TBB to tell it about an "interesting" event.
     *
     * @param op    Value specifying the event to track.
     */
    void invoke_tbb_stack_op(__cilk_tbb_stack_op op);

private:

    /**
     * @brief Helper method: try to allocate a fiber from this pool or
     * its ancestors without going to the OS / heap.
     *
     * Returns allocated pool, or NULL if no pool is found.
     *
     * If pool contains a suitable fiber. Return it.  Otherwise, try to
     * recursively grab a fiber from the parent pool, if there is one.
     *
     * This method will not allocate a fiber from the heap.
     */
    static cilk_fiber* try_allocate_from_pool_recursive(cilk_fiber_pool* pool);
    
    
#if NEED_FIBER_REF_COUNTS
    /**
     * @brief Atomic increment of reference count. 
     */
    void atomic_inc_ref_count();

    /**
     * @brief Atomic decrement of reference count.
     */
    long atomic_dec_ref_count();

    /**
     * @brief Atomic subtract of v from reference count.
     * @param v Value to subtract.
     */    
    long atomic_sub_from_ref_count(long v);
#endif // NEED_FIBER_REF_COUNTS
    
};

#endif // __cplusplus

#endif // ! defined(INCLUDED_CILK_FIBER_DOT_H)
