mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
17d260a87f
This was a public method, so we should probably keep it.
492 lines
18 KiB
C++
492 lines
18 KiB
C++
#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 */
|