#ifndef RBIMPL_INTERN_THREAD_H /*-*-C++-*-vi:se ft=cpp:*/ #define RBIMPL_INTERN_THREAD_H /** * @file * @author Ruby developers * @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 */