#ifndef RUBY_THREAD_H                                /*-*-C++-*-vi:se ft=cpp:*/
#define RUBY_THREAD_H 1
/**
 * @file
 * @author     $Author: matz $
 * @date       Tue Jul 10 17:35:43 JST 2012
 * @copyright  Copyright (C) 2007 Yukihiro Matsumoto
 * @copyright  This  file  is   a  part  of  the   programming  language  Ruby.
 *             Permission  is hereby  granted,  to  either redistribute  and/or
 *             modify this file, provided that  the conditions mentioned in the
 *             file COPYING are met.  Consult the file for details.
 */
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/intern/thread.h" /* rb_unblock_function_t */
#include "ruby/internal/dllexport.h"

/**
 * @name Flags for rb_nogvl()
 *
 * @{
 */

/**
 * Passing this flag to `rb_nogvl()` prevents it from processing interrupts after
 * the given function returns. Instead, `rb_nogvl()` returns without calling the
 * function if an interrupt is detected before entering the blocking region.
 * This allows the caller to check interrupts later, after it has accounted for
 * any side effects of the function. For instance consider following callback
 * function:
 *
 * ```CXX
 * static inline int fd; // set elsewhere.
 * static inline auto callback(auto buf) {
 *   auto tmp = ruby_xmalloc(BUFSIZ);
 *   auto ret = ruby_xmalloc(sizeof(ssize_t));  // (a)
 *   auto n = read(fd, tmp, BUFSIZ);            // (b)
 *   memcpy(buf, tmp, n);                       // (c)
 *   memcpy(ret, n, sizeof(n));
 *   ruby_xfree(tmp);
 *   return ret;
 * }
 * ```
 *
 * Here, if `read(2)` is interrupted at (b), the function can observe `EINTR` and
 * clean up. But if a Ruby interrupt is processed at (c), where `read(2)` is
 * already done, the read data can be lost. To reroute this kind of problem you
 * should set this flag and check interrupts later, at a point where it is safe
 * to do so with respect to the function's side effects.
 */
#define RB_NOGVL_INTR_FAIL       (0x1)

/**
 * Passing this flag to `rb_nogvl()` prevents it from entering the blocking
 * region if the current thread has pending interrupts, even if they are masked
 * by `Thread.handle_interrupt`. In that case `rb_nogvl()` returns 0 without
 * calling the given function; `errno` is 0, the same as the existing skip path
 * used by `RB_NOGVL_INTR_FAIL` (the function was never called).
 */
#define RB_NOGVL_PENDING_INTR_FAIL  (0x8)

/**
 * Passing  this  flag   to  rb_nogvl()  indicates  that  the   passed  UBF  is
 * async-signal-safe.   An UBF  could  be  async safe,  and  that makes  things
 * simpler.   However async  unsafe UBFs  are just  okay.  If  unsure, you  can
 * safely leave it unspecified.
 *
 * @internal
 *
 * This makes sense only in case of POSIX threads.
 */
#define RB_NOGVL_UBF_ASYNC_SAFE  (0x2)

/**
 * Passing  this  flag   to  rb_nogvl()  indicates  that  the   passed function
 * is safe to offload to a background thread or work pool. In other words, the
 * function is safe to run using a fiber scheduler's `blocking_operation_wait`.
 * hook.
 *
 * If your function depends on thread-local storage, or thread-specific data
 * operations/data structures, you should not set this flag, as
 * these operations may behave differently (or fail) when run in a different
 * thread/context (e.g. unlocking a mutex).
 */
#define RB_NOGVL_OFFLOAD_SAFE  (0x4)

/** @} */

RBIMPL_SYMBOL_EXPORT_BEGIN()

RBIMPL_ATTR_NONNULL((1))
/**
 * (Re-)acquires the GVL.   This manoeuvre makes it possible  for an out-of-GVL
 * routine to one-shot call a ruby method.
 *
 * What this function does:
 *
 *  1. Blocks until it acquires the GVL.
 *  2. Calls the passed function.
 *  3. Releases the GVL.
 *  4. Returns what was returned form the passed function.
 *
 * @param[in]      func   What to call with GVL.
 * @param[in,out]  data1  Passed as-is to `func`.
 * @return         What was returned from `func`.
 * @warning        `func` must not return a Ruby object.  If it did such return
 *                 value would escape from GC's scope; would not be marked.
 * @warning        Global escapes from this  function just yield whatever fatal
 *                 undefined behaviours.   You must make sure  that `func` does
 *                 not   raise,   by   properly   rescuing   everything   using
 *                 e.g. rb_protect().
 * @warning        You  cannot convert  a non-Ruby  thread into  a Ruby  thread
 *                 using this API.  This function  makes sense only from inside
 *                 of a rb_thread_call_without_gvl()'s callback.
 */
void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1);

RBIMPL_ATTR_NONNULL((1))
/**
 * Allows the passed function to run in parallel with other Ruby threads.
 *
 * What this function does:
 *
 *  1. Checks (and handles) pending interrupts.
 *  2. Releases the GVL. (Others can run here in parallel...)
 *  3. Calls the passed function.
 *  4. Blocks until it re-acquires the GVL.
 *  5. Checks interrupts that happened between 2 to 4.
 *
 * In case  other threads  interfaced with  this thread  using rb_thread_kill()
 * etc., the  passed UBF  is additionally called.   See ::rb_unblock_function_t
 * for details.
 *
 * Unlike rb_thread_call_without_gvl2()  this function  also reacts  to signals
 * etc.
 *
 * @param[in]      func   A function to call without GVL.
 * @param[in,out]  data1  Passed as-is to `func`.
 * @param[in]      ubf    An UBF to cancel `func`.
 * @param[in,out]  data2  Passed as-is to `ubf`.
 * @return         What `func` returned, or 0 in case `ubf` cancelled `func`.
 * @warning        You cannot use  most of Ruby C APIs like  calling methods or
 *                 raising exceptions from  any of the functions  passed to it.
 *                 If that  is dead necessary use  rb_thread_call_with_gvl() to
 *                 re-acquire the GVL.
 * @warning        In short, this API is difficult.  @ko1 recommends you to use
 *                 other ways if any.  We lack experiences to use this API.  If
 *                 you  find any  corner cases  etc., please  report it  to the
 *                 devs.
 * @warning        Releasing and re-acquiring the GVL are expensive operations.
 *                 For a short-running `func`, it  might be faster to just call
 *                 `func` with blocking everything  else.  Be sure to benchmark
 *                 your code to see if it is actually worth releasing the GVL.
 */
void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1,
                                 rb_unblock_function_t *ubf, void *data2);

RBIMPL_ATTR_NONNULL((1))
/**
 * Identical to rb_thread_call_without_gvl(), except it does not interface with
 * signals etc.  As described in  #RB_NOGVL_INTR_FAIL, interrupts can hurt you.
 * In case this function detects an interrupt, it returns immediately.  You can
 * record progress  of your  callback and  check it  after returning  from this
 * function.
 *
 * What this function does:
 *
 *  1. Checks for pending interrupts and if any, just returns.
 *  2. Releases the GVL. (Others can run here in parallel...)
 *  3. Calls the passed function.
 *  4. Blocks until it re-acquires the GVL.
 *
 * @param[in]      func   A function to call without GVL.
 * @param[in,out]  data1  Passed as-is to `func`.
 * @param[in]      ubf    An UBF to cancel `func`.
 * @param[in,out]  data2  Passed as-is to `ubf`.
 * @return         What `func` returned, or 0 in case `func` did not return.
 */
void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
                                  rb_unblock_function_t *ubf, void *data2);

/*
 * XXX: unstable/unapproved - out-of-tree code should NOT not depend
 * on this until it hits Ruby 2.6.1
 */

RBIMPL_ATTR_NONNULL((1))
/**
 * Identical  to  rb_thread_call_without_gvl(),  except it  additionally  takes
 * "flags" that change the behaviour.
 *
 * @param[in]      func   A function to call without GVL.
 * @param[in,out]  data1  Passed as-is to `func`.
 * @param[in]      ubf    An UBF to cancel `func`.
 * @param[in,out]  data2  Passed as-is to `ubf`.
 * @param[in]      flags  Flags.
 * @return         What `func` returned, or 0 in case `func` did not return.
 */
void *rb_nogvl(void *(*func)(void *), void *data1,
               rb_unblock_function_t *ubf, void *data2,
               int flags);

/**
 * @private
 *
 * @deprecated  This macro once was a thing in the old days, but makes no sense
 *              any  longer today.   Exists  here  for backwards  compatibility
 *              only.  You can safely forget about it.
 */
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_AFTER 0x01

/**
 * @private
 * @deprecated  It seems even in the old days it made no sense...?
 */
#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_

/**
 * Declare the current Ruby thread should acquire a dedicated
 * native thread on M:N thread scheduler.
 *
 * If a C extension (or a library which the extension relies on) should
 * keep to run on a native thread (e.g. using thread-local-storage),
 * this function allocates a dedicated native thread for the thread.
 *
 * @return `false` if the thread already running on a dedicated native
 *         thread. Otherwise `true`.
 */
bool rb_thread_lock_native_thread(void);

/**
 * Triggered when a new thread is started.
 *
 * @note       The callback will be called *without* the GVL held.
 */
#define RUBY_INTERNAL_THREAD_EVENT_STARTED    1 << 0

/**
* Triggered when a thread attempt to acquire the GVL.
*
* @note       The callback will be called *without* the GVL held.
*/
#define RUBY_INTERNAL_THREAD_EVENT_READY      1 << 1 /** acquiring GVL */

/**
 * Triggered when a thread successfully acquired the GVL.
 *
 * @note       The callback will be called *with* the GVL held.
 */
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED    1 << 2 /** acquired GVL */

/**
 * Triggered when a thread released the GVL.
 *
 * @note       The callback will be called *without* the GVL held.
 */
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED  1 << 3 /** released GVL */

/**
 * Triggered when a thread exits.
 *
 * @note       The callback will be called *without* the GVL held.
 */
#define RUBY_INTERNAL_THREAD_EVENT_EXITED     1 << 4 /** thread terminated */

#define RUBY_INTERNAL_THREAD_EVENT_MASK       0xff /** All Thread events */

typedef struct rb_internal_thread_event_data {
   VALUE thread;
} rb_internal_thread_event_data_t;

typedef void (*rb_internal_thread_event_callback)(rb_event_flag_t event,
              const rb_internal_thread_event_data_t *event_data,
              void *user_data);
typedef struct rb_internal_thread_event_hook rb_internal_thread_event_hook_t;

/**
 * Registers a thread event hook function.
 *
 * @param[in]  func    A callback.
 * @param[in]  events  A set of events that `func` should run.
 * @param[in]  data    Passed as-is to `func`.
 * @return     An opaque pointer to the hook, to unregister it later.
 * @note       This functionality is a noop on Windows and WebAssembly.
 * @note       The callback will be called without the GVL held, except for the
 *             RESUMED event.
 * @note       Callbacks are not guaranteed to be executed on the native threads
 *             that corresponds to the Ruby thread. To identify which Ruby thread
 *             the event refers to, you must use `event_data->thread`.
 * @warning    This function MUST not be called from a thread event callback.
 */
rb_internal_thread_event_hook_t *rb_internal_thread_add_event_hook(
        rb_internal_thread_event_callback func, rb_event_flag_t events,
        void *data);


/**
 * Unregister the passed hook.
 *
 * @param[in]  hook.  The hook to unregister.
 * @return     Whether the hook was found and unregistered.
 * @note       This functionality is a noop on Windows and WebAssembly.
 * @warning    This function MUST not be called from a thread event callback.
*/
bool rb_internal_thread_remove_event_hook(
        rb_internal_thread_event_hook_t * hook);


typedef int rb_internal_thread_specific_key_t;
#define RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX 8
/**
 * Create a key to store thread specific data.
 *
 * These APIs are designed for tools using
 * rb_internal_thread_event_hook APIs.
 *
 * Note that only `RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX` keys
 * can be created. raises `ThreadError` if exceeded.
 *
 * Usage:
 *   // at initialize time:
 *   int tool_key; // gvar
 *   Init_tool() {
 *     tool_key = rb_internal_thread_specific_key_create();
 *   }
 *
 *   // at any timing:
 *   rb_internal_thread_specific_set(thread, tool_key, per_thread_data);
 *   ...
 *   per_thread_data = rb_internal_thread_specific_get(thread, tool_key);
 */
rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void);

/**
 * Get thread and tool specific data.
 *
 * This function is async signal safe and thread safe.
 */
void *rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key);

/**
 * Set thread and tool specific data.
 *
 * This function is async signal safe and thread safe.
 */
void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data);

/**
 * Whether the current thread is holding the GVL.
 *
 * @return true if the current thread is holding the GVL, false otherwise.
 */
int ruby_thread_has_gvl_p(void);

RBIMPL_SYMBOL_EXPORT_END()

#endif /* RUBY_THREAD_H */
