ruby-changes:67776
From: =E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3 <ko1@a...>
Date: Fri, 10 Sep 2021 20:01:53 +0900 (JST)
Subject: [ruby-changes:67776] 45803af58c (master): include/ruby/internal/intern/thread.h: add doxygen
https://git.ruby-lang.org/ruby.git/commit/?id=45803af58c From 45803af58cc5fefa7acff8ae1fa955487347d309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= <shyouhei@r...> Date: Wed, 10 Feb 2021 13:57:45 +0900 Subject: include/ruby/internal/intern/thread.h: add doxygen Must not be a bad idea to improve documents. [ci skip] --- include/ruby/internal/intern/thread.h | 441 ++++++++++++++++++++++++++++++++-- 1 file changed, 424 insertions(+), 17 deletions(-) diff --git a/include/ruby/internal/intern/thread.h b/include/ruby/internal/intern/thread.h index 828f595..c9f4767 100644 --- a/include/ruby/internal/intern/thread.h +++ b/include/ruby/internal/intern/thread.h @@ -20,8 +20,9 @@ https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/intern/thread.h#L20 * extension libraries. They could be written in C++98. * @brief Public APIs related to ::rb_cThread. */ -#include "ruby/internal/config.h" +#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" @@ -30,45 +31,451 @@ RBIMPL_SYMBOL_EXPORT_BEGIN() https://github.com/ruby/ruby/blob/trunk/include/ruby/internal/intern/thread.h#L31 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); -int rb_thread_wait_fd(int); -int rb_thread_fd_writable(int); -void rb_thread_fd_close(int); + +/** + * 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_eSystemCalleError 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_eSystemCalleError 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); -void rb_thread_sleep(int); + +/** + * 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); -VALUE rb_thread_wakeup(VALUE); -VALUE rb_thread_wakeup_alive(VALUE); -VALUE rb_thread_run(VALUE); -VALUE rb_thread_kill(VALUE); -VALUE rb_thread_create(VALUE (*)(void *), void*); -void rb_thread_wait_for(struct timeval); + +/** + * 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); -VALUE rb_thread_local_aref(VALUE, ID); -VALUE rb_thread_local_aset(VALUE, ID, VALUE); + +/** + * 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); -VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE); -VALUE rb_exec_recursive_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE); -VALUE rb_exe (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/