Your IP : 18.223.241.235


Current Path : /opt/alt/ruby31/include/ruby/internal/intern/
Upload File :
Current File : //opt/alt/ruby31/include/ruby/internal/intern/thread.h

#ifndef RBIMPL_INTERN_THREAD_H                       /*-*-C++-*-vi:se ft=cpp:*/
#define RBIMPL_INTERN_THREAD_H
/**
 * @file
 * @author     Ruby developers <ruby-core@ruby-lang.org>
 * @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.
 * @warning    Symbols   prefixed  with   either  `RBIMPL`   or  `rbimpl`   are
 *             implementation details.   Don't take  them as canon.  They could
 *             rapidly appear then vanish.  The name (path) of this header file
 *             is also an  implementation detail.  Do not expect  it to persist
 *             at the place it is now.  Developers are free to move it anywhere
 *             anytime at will.
 * @note       To  ruby-core:  remember  that   this  header  can  be  possibly
 *             recursively included  from extension  libraries written  in C++.
 *             Do not  expect for  instance `__VA_ARGS__` is  always available.
 *             We assume C99  for ruby itself but we don't  assume languages of
 *             extension libraries.  They could be written in C++98.
 * @brief      Public APIs related to ::rb_cThread.
 */
#include "ruby/internal/attr/nonnull.h"
#include "ruby/internal/cast.h"
#include "ruby/internal/config.h"
#include "ruby/internal/dllexport.h"
#include "ruby/internal/value.h"

RBIMPL_SYMBOL_EXPORT_BEGIN()

struct timeval;

/* thread.c */

/**
 * Tries to switch  to another thread.  This function blocks  until the current
 * thread re-acquires the GVL.
 *
 * @exception  rb_eInterrupt  Operation interrupted.
 */
void rb_thread_schedule(void);

/**
 * Blocks the  current thread until  the given file  descriptor is ready  to be
 * read.
 *
 * @param[in]  fd                    A file descriptor.
 * @exception  rb_eIOError           Closed stream.
 * @exception  rb_eSystemCallError   Situations like EBADF.
 */
int rb_thread_wait_fd(int fd);

/**
 * Identical to rb_thread_wait_fd(), except it  blocks the current thread until
 * the given file descriptor is ready to be written.
 *
 * @param[in]  fd                    A file descriptor.
 * @exception  rb_eIOError           Closed stream.
 * @exception  rb_eSystemCallError   Situations like EBADF.
 */
int rb_thread_fd_writable(int fd);

/**
 * Notifies a closing of a file  descriptor to other threads.  Multiple threads
 * can wait for the given file descriptor  at once.  If such file descriptor is
 * closed, threads need to start propagating their exceptions.  This is the API
 * to kick that process.
 *
 * @param[in]  fd  A file descriptor.
 * @note       This function blocks  until all the threads waiting  for such fd
 *             have woken up.
 */
void rb_thread_fd_close(int fd);

/**
 * Checks if  the thread this  function is running is  the only thread  that is
 * currently alive.
 *
 * @retval  1  Yes it is.
 * @retval  0  No it isn't.
 *
 * @internal
 *
 * Above description is in fact inaccurate.  There are Ractors these days.
 */
int rb_thread_alone(void);

/**
 * Blocks for the given period of time.
 *
 * @warning    This function can be interrupted by signals.
 * @param[in]  sec            Duration in seconds.
 * @exception  rb_eInterrupt  Interrupted.
 */
void rb_thread_sleep(int sec);

/**
 * Blocks indefinitely.
 *
 * @exception  rb_eInterrupt  Interrupted.
 */
void rb_thread_sleep_forever(void);

/**
 * Identical  to  rb_thread_sleep_forever(),  except the  thread  calling  this
 * function is considered "dead" when our deadlock checker is triggered.
 *
 * @exception  rb_eInterrupt  Interrupted.
 */
void rb_thread_sleep_deadly(void);

/**
 * Stops the current thread.  This is not the end of the thread's lifecycle.  A
 * stopped thread can later be woken up.
 *
 * @exception  rb_eThreadError  Stopping this thread would deadlock.
 * @retval     ::RUBY_Qnil      Always.
 *
 * @internal
 *
 * The return value makes no sense at all.
 */
VALUE rb_thread_stop(void);

/**
 * Marks a given thread as eligible for scheduling.
 *
 * @note  It may still remain blocked on I/O.
 * @note  This does not invoke the scheduler itself.
 *
 * @param[out]  thread           Thread in question to wake up.
 * @exception   rb_eThreadError  Stop flogging a dead horse.
 * @return      The passed thread.
 * @post        The passed thread is made runnable.
 */
VALUE rb_thread_wakeup(VALUE thread);

/**
 * Identical  to rb_thread_wakeup(),  except  it doesn't  raise  on an  already
 * killed thread.
 *
 * @param[out]  thread     A thread to wake up.
 * @retval      RUBY_Qnil  `thread` is already killed.
 * @retval      otherwise  `thread` is alive.
 * @post        The passed thread is made runnable, unless killed.
 */
VALUE rb_thread_wakeup_alive(VALUE thread);

/**
 * This is a rb_thread_wakeup() + rb_thread_schedule() combo.
 *
 * @note        There is no  guarantee that this function yields  to the passed
 *              thread.  It may still remain blocked on I/O.
 * @param[out]  thread           Thread in question to wake up.
 * @exception   rb_eThreadError  Stop flogging a dead horse.
 * @return      The passed thread.
 */
VALUE rb_thread_run(VALUE thread);

/**
 * Terminates the given thread.  Unlike a stopped thread, a killed thread could
 * never be revived.   This function does return, when passed  e.g.  an already
 * killed thread.   But if  the passed  thread is  the only  one, or  a special
 * thread called "main", then it also terminates the entire process.
 *
 * @param[out]  thread          The thread to terminate.
 * @exception   rb_eFatal       The passed thread is the running thread.
 * @exception   rb_eSystemExit  The passed thread is the last thread.
 * @return      The passed thread.
 * @post        Either the passed thread, or the process entirely, is killed.
 *
 * @internal
 *
 * It seems killing the main thread also kills the entire process even if there
 * are multiple running ractors.  No idea why.
 */
VALUE rb_thread_kill(VALUE thread);

RBIMPL_ATTR_NONNULL((1))
/**
 * Creates a Ruby thread that is backended by a C function.
 *
 * @param[in]      f                    The function to run on a thread.
 * @param[in,out]  g                    Passed through to `f`.
 * @exception      rb_eThreadError      Could not create a ruby thread.
 * @exception      rb_eSystemCallError  Situations like `EPERM`.
 * @return         Allocated instance of ::rb_cThread.
 * @note           This doesn't wait for anything.
 */
VALUE rb_thread_create(VALUE (*f)(void *g), void *g);

/**
 * Identical to rb_thread_sleep(), except it takes struct `timeval` instead.
 *
 * @warning    This function can be interrupted by signals.
 * @param[in]  time           Duration.
 * @exception  rb_eInterrupt  Interrupted.
 */
void rb_thread_wait_for(struct timeval time);

/**
 * Obtains the "current" thread.
 *
 * @return  The current thread  of the current ractor of  the current execution
 *          context.
 * @pre     This function must be called from a thread controlled by ruby.
 */
VALUE rb_thread_current(void);

/**
 * Obtains the "main" thread.  There are threads called main.  Historically the
 * (only) main thread was the one which  runs when the process boots.  Now that
 * we have Ractor, there are more than one main threads.
 *
 * @return  The  main thread  of the  current ractor  of the  current execution
 *          context.
 * @pre     This function must be called from a thread controlled by ruby.
 */
VALUE rb_thread_main(void);

/**
 * This  badly named  function reads  from a  Fiber local  storage.  When  this
 * function was  born there  was no  such thing  like a  Fiber.  The  world was
 * innocent.  But now...  This is a Fiber local storage.  Sorry.
 *
 * @param[in]  thread     Thread that the target Fiber is running.
 * @param[in]  key        The name of the Fiber local storage to read.
 * @retval     RUBY_Qnil  No such storage.
 * @retval     otherwise  The value stored at `key`.
 * @note       There in fact are "true"  thread local storage, but Ruby doesn't
 *             provide any interface of them to you, C programmers.
 */
VALUE rb_thread_local_aref(VALUE thread, ID key);

/**
 * This  badly named  function  writes to  a Fiber  local  storage.  When  this
 * function was  born there  was no  such thing  like a  Fiber.  The  world was
 * innocent.  But now...  This is a Fiber local storage.  Sorry.
 *
 * @param[in]  thread           Thread that the target Fiber is running.
 * @param[in]  key              The name of the Fiber local storage to write.
 * @param[in]  val              The new value of the storage.
 * @exception  rb_eFrozenError  `thread` is frozen.
 * @return     The passed `val` as-is.
 * @post       Fiber local storage `key` has value of `val`.
 * @note       There in fact are "true"  thread local storage, but Ruby doesn't
 *             provide any interface of them to you, C programmers.
 */
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val);

/**
 * A `pthread_atfork(3posix)`-like  API.  Ruby  expects its child  processes to
 * call this function at the very beginning of their processes.  If you plan to
 * fork a process don't forget to call it.
 */
void rb_thread_atfork(void);

/**
 * :FIXME: situation  of this function  is unclear.   It seems nobody  uses it.
 * Maybe a good idea to KonMari.
 */
void rb_thread_atfork_before_exec(void);

/**
 * "Recursion" API entry  point.  This basically calls the  given function with
 * the given arguments, but additionally with  recursion flag.  The flag is set
 * to 1  if the  execution have  already experienced  the passed  `g` parameter
 * before.
 *
 * @param[in]      f  The function that possibly recurs.
 * @param[in,out]  g  Passed as-is to `f`.
 * @param[in,out]  h  Passed as-is to `f`.
 * @return         The return value of f.
 */
VALUE rb_exec_recursive(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);

/**
 * Identical to rb_exec_recursive(), except it  checks for the recursion on the
 * ordered pair of `{ g, p }` instead of just `g`.
 *
 * @param[in]      f  The function that possibly recurs.
 * @param[in,out]  g  Passed as-is to `f`.
 * @param[in]      p  Paired object for recursion detection.
 * @param[in,out]  h  Passed as-is to `f`.
 */
VALUE rb_exec_recursive_paired(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);

/**
 * Identical  to  rb_exec_recursive(),  except   it  calls  `f`  for  outermost
 * recursion only.  Inner recursions yield calls to rb_throw_obj().
 *
 * @param[in]      f  The function that possibly recurs.
 * @param[in,out]  g  Passed as-is to `f`.
 * @param[in,out]  h  Passed as-is to `f`.
 * @return         The return value of f.
 *
 * @internal
 *
 * It seems  nobody uses the "it  calls rb_throw_obj()" part of  this function.
 * @shyouhei doesn't understand the needs.
 */
VALUE rb_exec_recursive_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h);

/**
 * Identical to  rb_exec_recursive_outer(), except it checks  for the recursion
 * on the ordered pair of `{ g, p }`  instead of just `g`.  It can also be seen
 * as a  routine identical to  rb_exec_recursive_paired(), except it  calls `f`
 * for   outermost   recursion  only.    Inner   recursions   yield  calls   to
 * rb_throw_obj().
 *
 * @param[in]      f  The function that possibly recurs.
 * @param[in,out]  g  Passed as-is to `f`.
 * @param[in]      p  Paired object for recursion detection.
 * @param[in,out]  h  Passed as-is to `f`.
 *
 * @internal
 *
 * It seems  nobody uses the "it  calls rb_throw_obj()" part of  this function.
 * @shyouhei doesn't understand the needs.
 */
VALUE rb_exec_recursive_paired_outer(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h);

/**
 * This is  the type of UBFs.   An UBF is  a function that unblocks  a blocking
 * region.  For instance when a thread is blocking due to `pselect(3posix)`, it
 * is highly expected that `pthread_kill(3posix)` can interrupt the system call
 * and  the  thread  could  revive.   Or  when a  thread  is  blocking  due  to
 * `waitpid(3posix)`, it  is highly  expected that  killing the  waited process
 * should suffice.  An UBF is a function that does such things.  Designing your
 * own UBF  needs deep understanding  of why  your blocking region  blocks, how
 * threads work in ruby, and a matter of luck.  It often is the case you simply
 * cannot cancel something that had already begun.
 *
 * @see rb_thread_call_without_gvl()
 */
typedef void rb_unblock_function_t(void *);

/**
 * @private
 *
 * This is an implementation detail.  Must be a mistake to be here.
 *
 * @internal
 *
 * Why is  this function type different  from what rb_thread_call_without_gvl()
 * takes?
 */
typedef VALUE rb_blocking_function_t(void *);

/**
 * Checks for  interrupts.  In ruby,  signals are  masked by default.   You can
 * call this function at  will to check if there are  pending signals.  In case
 * there are, they would be handled in this function.
 *
 * If your  extension library has a  function that takes a  long time, consider
 * calling it periodically.
 *
 * @note  It might switch to another thread.
 */
void rb_thread_check_ints(void);

/**
 * Checks if the  thread's execution was recently interrupted.   If called from
 * that thread, this function can be used to detect spurious wake-ups.
 *
 * @param[in]  thval      Thread in question.
 * @retval     0          The thread was not interrupted.
 * @retval     otherwise  The thread was interrupted recently.
 *
 * @internal
 *
 * Above description is not a lie.  But  actually the return value is an opaque
 * trap vector.  If you know which bit means which, you can know what happened.
 */
int rb_thread_interrupted(VALUE thval);

/**
 * A special  UBF for blocking IO  operations.  You need deep  understanding of
 * what this  actually do before using.   Basically you should not  use it from
 * extension libraries.  It is too easy to mess up.
 */
#define RUBY_UBF_IO RBIMPL_CAST((rb_unblock_function_t *)-1)

/**
 * A special UBF for blocking  process operations.  You need deep understanding
 * of what this actually do before using.  Basically you should not use it from
 * extension libraries.  It is too easy to mess up.
 */
#define RUBY_UBF_PROCESS RBIMPL_CAST((rb_unblock_function_t *)-1)

/* thread_sync.c */

/**
 * Creates a mutex.
 *
 * @return An allocated instance of rb_cMutex.
 */
VALUE rb_mutex_new(void);

/**
 * Queries if there are any threads that holds the lock.
 *
 * @param[in]  mutex  The mutex in question.
 * @retval     RUBY_Qtrue  The mutex is locked by someone.
 * @retval     RUBY_Qfalse The mutex is not locked by anyone.
 */
VALUE rb_mutex_locked_p(VALUE mutex);

/**
 * Attempts to lock the mutex, without  waiting for other threads to unlock it.
 * Failure in locking the mutex can be detected by the return value.
 *
 * @param[out]  mutex        The mutex to lock.
 * @retval      RUBY_Qtrue   Successfully locked by the current thread.
 * @retval      RUBY_Qfalse  Otherwise.
 * @note        This  function also  returns  ::RUBY_Qfalse when  the mutex  is
 *              already owned by the calling thread itself.
 */
VALUE rb_mutex_trylock(VALUE mutex);

/**
 * Attempts to lock the mutex.  It waits until the mutex gets available.
 *
 * @param[out]  mutex            The mutex to lock.
 * @exception   rb_eThreadError  Recursive deadlock situation.
 * @return      The passed mutex.
 * @post        The mutex is owned by the current thread.
 */
VALUE rb_mutex_lock(VALUE mutex);

/**
 * Releases the mutex.
 *
 * @param[out]  mutex            The mutex to unlock.
 * @exception   rb_eThreadError  The mutex is not owned by the current thread.
 * @return      The passed mutex.
 * @post        Upon successful return  the passed mutex is no  longer owned by
 *              the current thread.
 */
VALUE rb_mutex_unlock(VALUE mutex);

/**
 * Releases  the lock  held in  the mutex  and waits  for the  period of  time;
 * reacquires the lock on wakeup.
 *
 * @pre         The lock has to be owned by the current thread beforehand.
 * @param[out]  self             The target mutex.
 * @param[in]   timeout          Duration, in seconds, in ::rb_cNumeric.
 * @exception   rb_eArgError     `timeout` is negative.
 * @exception   rb_eRangeError   `timeout` is out of range of `time_t`.
 * @exception   rb_eThreadError  The mutex is not owned by the current thread.
 * @return      Number of seconds it actually slept.
 * @warning     It is a  failure not to check the return  value.  This function
 *              can return spuriously for various reasons.  Maybe other threads
 *              can  rb_thread_wakeup().   Maybe  an  end user  can  press  the
 *              Control and C  key from the interactive console.   On the other
 *              hand it  can also  take longer than  the specified.   The mutex
 *              could be locked by someone else.  It waits then.
 * @post        Upon successful return the passed mutex is owned by the current
 *              thread.
 *
 * @internal
 *
 * This  function is  called from  `ConditionVariable#wait`.   So it  is not  a
 * deprecated feature.   However @shyouhei  have never  seen any  similar mutex
 * primitive available in any other languages than Ruby.
 *
 * EDIT: In 2021,  @shyouhei asked @ko1 in person about  this API.  He answered
 * that it is his invention.  The  motivation behind its design is to eliminate
 * needs of condition variables as  primitives.  Unlike other languages, Ruby's
 * `ConditionVariable` class was written in pure-Ruby initially.  We don't have
 * to implement  machine-native condition  variables in  assembly each  time we
 * port Ruby to a new architecture.  This function made it possible.  "I felt I
 * was a genius when this idea came to me", said @ko1.
 *
 * `rb_cConditionVariable` is now written in C for speed, though.
 */
VALUE rb_mutex_sleep(VALUE self, VALUE timeout);

/**
 * Obtains the  lock, runs the passed  function, and releases the  lock when it
 * completes.
 *
 * @param[out]     mutex  The mutex to lock.
 * @param[in]      func   What to do during the mutex is locked.
 * @param[in,out]  arg    Passed as-is to `func`.
 */
VALUE rb_mutex_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg);

RBIMPL_SYMBOL_EXPORT_END()

#endif /* RBIMPL_INTERN_THREAD_H */

?>