Class: Thread
Overview
Threads are the Ruby implementation for a concurrent programming model.
Programs that require multiple threads of execution are a perfect candidate for Ruby’s Thread class.
For example, we can create a new thread separate from the main thread’s execution using ::new.
thr = Thread.new { puts "What's the big deal" }
Then we are able to pause the execution of the main thread and allow our new thread to finish, using #join:
thr.join #=> "What's the big deal"
If we don’t call thr.join
before the main thread terminates, then all other threads including thr
will be killed.
Alternatively, you can use an array for handling multiple threads at once, like in the following example:
threads = []
threads << Thread.new { puts "What's the big deal" }
threads << Thread.new { 3.times { puts "Threads are fun!" } }
After creating a few threads we wait for them all to finish consecutively.
threads.each { |thr| thr.join }
To retrieve the last value of a thread, use #value
thr = Thread.new { sleep 1; "Useful value" }
thr.value #=> "Useful value"
Thread initialization
In order to create new threads, Ruby provides ::new, ::start, and ::fork. A block must be provided with each of these methods, otherwise a ThreadError will be raised.
When subclassing the Thread class, the initialize
method of your subclass will be ignored by ::start and ::fork. Otherwise, be sure to call super in your initialize
method.
=== Thread termination
For terminating threads, Ruby provides a variety of ways to do this.
The class method ::kill, is meant to exit a given thread:
thr = Thread.new { sleep }
Thread.kill(thr) # sends exit() to thr
Alternatively, you can use the instance method #exit, or any of its aliases #kill or #terminate.
thr.exit
=== Thread status
Ruby provides a few instance methods for querying the state of a given thread. To get a string with the current thread’s state use #status
thr = Thread.new { sleep }
thr.status # => "sleep"
thr.exit
thr.status # => false
You can also use #alive? to tell if the thread is running or sleeping, and #stop? if the thread is dead or sleeping.
=== Thread variables and scope
Since threads are created with blocks, the same rules apply to other Ruby blocks for variable scope. Any local variables created within this block are accessible to only this thread.
==== Fiber-local vs. Thread-local
Each fiber has its own bucket for Thread#[] storage. When you set a new fiber-local it is only accessible within this Fiber. To illustrate:
Thread.new {
Thread.current[:foo] = "bar"
Fiber.new {
p Thread.current[:foo] # => nil
}.resume
}.join
This example uses #[] for getting and #[]= for setting fiber-locals, you can also use #keys to list the fiber-locals for a given thread and #key? to check if a fiber-local exists.
When it comes to thread-locals, they are accessible within the entire scope of the thread. Given the following example:
Thread.new{
Thread.current.thread_variable_set(:foo, 1)
p Thread.current.thread_variable_get(:foo) # => 1
Fiber.new{
Thread.current.thread_variable_set(:foo, 2) p Thread.current.thread_variable_get(:foo) # => 2
}.resume
p Thread.current.thread_variable_get(:foo) # => 2
}.join
You can see that the thread-local +:foo+ carried over into the fiber
and was changed to +2+ by the end of the thread.
This example makes use of #thread_variable_set to create new
thread-locals, and #thread_variable_get to reference them.
There is also #thread_variables to list all thread-locals, and
#thread_variable? to check if a given thread-local exists.
=== Exception handling
When an unhandled exception is raised inside a thread, it will
terminate. By default, this exception will not propagate to other
threads. The exception is stored and when another thread calls #value
or #join, the exception will be re-raised in that thread.
t = Thread.new{ raise 'something went wrong' }
t.value #=> RuntimeError: something went wrong
An exception can be raised from outside the thread using the
Thread#raise instance method, which takes the same parameters as
Kernel#raise.
Setting Thread.abort_on_exception = true, Thread#abort_on_exception =
true, or $DEBUG = true will cause a subsequent unhandled exception
raised in a thread to be automatically re-raised in the main thread.
With the addition of the class method ::handle_interrupt, you can now handle exceptions asynchronously with threads.
=== Scheduling
Ruby provides a few ways to support scheduling threads in your program.
The first way is by using the class method ::stop, to put the current running thread to sleep and schedule the execution of another thread.
Once a thread is asleep, you can use the instance method #wakeup to mark your thread as eligible for scheduling.
You can also try ::pass, which attempts to pass execution to another thread but is dependent on the OS whether a running thread will switch or not. The same goes for #priority, which lets you hint to the thread scheduler which threads you want to take precedence when passing execution. This method is also dependent on the OS and may be ignored on some platforms.
Direct Known Subclasses
Defined Under Namespace
Classes: Backtrace, ConditionVariable, Mutex, Queue, SizedQueue
Class Method Summary collapse
-
.abort_on_exception ⇒ Boolean
Returns the status of the global “abort on exception” condition.
-
.abort_on_exception=(boolean) ⇒ Boolean
When set to
true
, if any thread is aborted by an exception, the raised exception will be re-raised in the main thread. -
.current ⇒ Object
Returns the currently executing thread.
-
.each_caller_location {|loc| ... } ⇒ nil
Yields each frame of the current execution stack as a backtrace location object.
-
.exit ⇒ Object
Terminates the currently running thread and schedules another thread to be run.
-
.fork(args) ⇒ Object
Basically the same as ::new.
-
.handle_interrupt(hash) { ... } ⇒ Object
Changes asynchronous interrupt timing.
-
.ignore_deadlock ⇒ Boolean
Returns the status of the global “ignore deadlock” condition.
-
.ignore_deadlock=(boolean) ⇒ Boolean
Returns the new state.
-
.kill(thread) ⇒ Object
Causes the given
thread
to exit, see also Thread::exit. -
.list ⇒ Array
Returns an array of Thread objects for all threads that are either runnable or stopped.
-
.main ⇒ Object
Returns the main thread.
-
.new(*args) ⇒ Object
Thread.new(*args, &proc) -> thread Thread.new(*args) { |args| … } -> thread.
-
.pass ⇒ nil
Give the thread scheduler a hint to pass execution to another thread.
-
.pending_interrupt?(error = nil) ⇒ Boolean
Returns whether or not the asynchronous queue is empty.
-
.report_on_exception ⇒ Boolean
Returns the status of the global “report on exception” condition.
-
.report_on_exception=(boolean) ⇒ Boolean
Returns the new state.
-
.start(args) ⇒ Object
Basically the same as ::new.
-
.stop ⇒ nil
Stops execution of the current thread, putting it into a “sleep” state, and schedules execution of another thread.
Instance Method Summary collapse
-
#[](sym) ⇒ Object?
Attribute Reference—Returns the value of a fiber-local variable (current thread’s root fiber if not explicitly inside a Fiber), using either a symbol or a string name.
-
#[]=(sym) ⇒ Object
Attribute Assignment—Sets or creates the value of a fiber-local variable, using either a symbol or a string.
-
#abort_on_exception ⇒ Boolean
Returns the status of the thread-local “abort on exception” condition for this
thr
. -
#abort_on_exception=(boolean) ⇒ Boolean
When set to
true
, if thisthr
is aborted by an exception, the raised exception will be re-raised in the main thread. -
#add_trace_func(proc) ⇒ Proc
Adds proc as a handler for tracing.
-
#alive? ⇒ Boolean
Returns
true
ifthr
is running or sleeping. -
#backtrace ⇒ Array?
Returns the current backtrace of the target thread.
-
#backtrace_locations(*args) ⇒ Object
Returns the execution stack for the target thread—an array containing backtrace location objects.
-
#exit ⇒ Object
Terminates
thr
and schedules another thread to be run, returning the terminated Thread. -
#fetch(*args) ⇒ Object
Returns a fiber-local for the given key.
-
#group ⇒ nil
Returns the ThreadGroup which contains the given thread.
-
#initialize(args) ⇒ Object
constructor
:nodoc:.
-
#join(*args) ⇒ Object
The calling thread will suspend execution and run this
thr
. -
#key?(sym) ⇒ Boolean
Returns
true
if the given string (or symbol) exists as a fiber-local variable. -
#keys ⇒ Array
Returns an array of the names of the fiber-local variables (as Symbols).
-
#kill ⇒ Object
Terminates
thr
and schedules another thread to be run, returning the terminated Thread. -
#name ⇒ String
show the name of the thread.
-
#name=(name) ⇒ String
set given name to the ruby thread.
-
#native_thread_id ⇒ Integer
Return the native thread ID which is used by the Ruby thread.
-
#pending_interrupt?(error = nil) ⇒ Boolean
Returns whether or not the asynchronous queue is empty for the target thread.
-
#priority ⇒ Integer
Returns the priority of thr.
-
#priority=(integer) ⇒ Object
Sets the priority of thr to integer.
-
#raise(*args) ⇒ Object
Raises an exception from the given thread.
-
#report_on_exception ⇒ Boolean
Returns the status of the thread-local “report on exception” condition for this
thr
. -
#report_on_exception=(boolean) ⇒ Boolean
When set to
true
, a message is printed on $stderr if an exception kills thisthr
. -
#run ⇒ Object
Wakes up
thr
, making it eligible for scheduling. -
#set_trace_func(trace) ⇒ Object
Establishes proc on thr as the handler for tracing, or disables tracing if the parameter is
nil
. -
#status ⇒ String, ...
Returns the status of
thr
. -
#stop? ⇒ Boolean
Returns
true
ifthr
is dead or sleeping. -
#terminate ⇒ Object
Terminates
thr
and schedules another thread to be run, returning the terminated Thread. -
#thread_variable?(key) ⇒ Boolean
Returns
true
if the given string (or symbol) exists as a thread-local variable. -
#thread_variable_get(key) ⇒ Object?
Returns the value of a thread local variable that has been set.
-
#thread_variable_set(key, value) ⇒ Object
Sets a thread local with
key
tovalue
. -
#thread_variables ⇒ Array
Returns an array of the names of the thread-local variables (as Symbols).
-
#to_s ⇒ String
(also: #inspect)
Dump the name, id, and status of thr to a string.
-
#value ⇒ Object
Waits for
thr
to complete, using #join, and returns its value or raises the exception which terminated the thread. -
#wakeup ⇒ Object
Marks a given thread as eligible for scheduling, however it may still remain blocked on I/O.
Constructor Details
#initialize(args) ⇒ Object
:nodoc:
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 |
# File 'thread.c', line 948
static VALUE
thread_initialize(VALUE thread, VALUE args)
{
rb_thread_t *th = rb_thread_ptr(thread);
if (!rb_block_given_p()) {
rb_raise(rb_eThreadError, "must be called with a block");
}
else if (th->invoke_type != thread_invoke_type_none) {
VALUE loc = threadptr_invoke_proc_location(th);
if (!NIL_P(loc)) {
rb_raise(rb_eThreadError,
"already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
}
else {
rb_raise(rb_eThreadError, "already initialized thread");
}
}
else {
struct thread_create_params params = {
.type = thread_invoke_type_proc,
.args = args,
.proc = rb_block_proc(),
};
return thread_create_core(thread, ¶ms);
}
}
|
Class Method Details
.abort_on_exception ⇒ Boolean
Returns the status of the global “abort on exception” condition.
The default is false
.
When set to true
, if any thread is aborted by an exception, the raised exception will be re-raised in the main thread.
Can also be specified by the global $DEBUG flag or command line option -d
.
See also ::abort_on_exception=.
There is also an instance level method to set this for a specific thread, see #abort_on_exception.
2988 2989 2990 2991 2992 |
# File 'thread.c', line 2988
static VALUE
rb_thread_s_abort_exc(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
}
|
.abort_on_exception=(boolean) ⇒ Boolean
When set to true
, if any thread is aborted by an exception, the raised exception will be re-raised in the main thread. Returns the new state.
Thread.abort_on_exception = true
t1 = Thread.new do
puts "In new thread"
raise "Exception from thread"
end
sleep(1)
puts "not reached"
This will produce:
In new thread
prog.rb:4: Exception from thread (RuntimeError)
from prog.rb:2:in `initialize'
from prog.rb:2:in `new'
from prog.rb:2
See also ::abort_on_exception.
There is also an instance level method to set this for a specific thread, see #abort_on_exception=.
3025 3026 3027 3028 3029 3030 |
# File 'thread.c', line 3025
static VALUE
rb_thread_s_abort_exc_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
return val;
}
|
.current ⇒ Object
Returns the currently executing thread.
Thread.current #=> #<Thread:0x401bdf4c run>
2942 2943 2944 2945 2946 |
# File 'thread.c', line 2942
static VALUE
thread_s_current(VALUE klass)
{
return rb_thread_current();
}
|
.each_caller_location {|loc| ... } ⇒ nil
Yields each frame of the current execution stack as a backtrace location object.
1273 1274 1275 1276 1277 1278 |
# File 'vm_backtrace.c', line 1273
static VALUE
each_caller_location(VALUE unused)
{
rb_ec_partial_backtrace_object(GET_EC(), 2, ALL_BACKTRACE_LINES, NULL, FALSE, TRUE);
return Qnil;
}
|
.exit ⇒ Object
Terminates the currently running thread and schedules another thread to be run.
If this thread is already marked to be killed, ::exit returns the Thread.
If this is the main thread, or the last thread, exit the process.
2780 2781 2782 2783 2784 2785 |
# File 'thread.c', line 2780
static VALUE
rb_thread_exit(VALUE _)
{
rb_thread_t *th = GET_THREAD();
return rb_thread_kill(th->self);
}
|
.start([args]) {|args| ... } ⇒ Object .fork([args]) {|args| ... } ⇒ Object
Basically the same as ::new. However, if class Thread is subclassed, then calling start
in that subclass will not invoke the subclass’s initialize
method.
925 926 927 928 929 930 931 932 933 934 |
# File 'thread.c', line 925
static VALUE
thread_start(VALUE klass, VALUE args)
{
struct thread_create_params params = {
.type = thread_invoke_type_proc,
.args = args,
.proc = rb_block_proc(),
};
return thread_create_core(rb_thread_alloc(klass), ¶ms);
}
|
.handle_interrupt(hash) { ... } ⇒ Object
Changes asynchronous interrupt timing.
interrupt means asynchronous event and corresponding procedure by Thread#raise, Thread#kill, signal trap (not supported yet) and main thread termination (if main thread terminates, then all other thread will be killed).
The given hash
has pairs like ExceptionClass => :TimingSymbol
. Where the ExceptionClass is the interrupt handled by the given block. The TimingSymbol can be one of the following symbols:
:immediate
-
Invoke interrupts immediately.
:on_blocking
-
Invoke interrupts while BlockingOperation.
:never
-
Never invoke all interrupts.
BlockingOperation means that the operation will block the calling thread, such as read and write. On CRuby implementation, BlockingOperation is any operation executed without GVL.
Masked asynchronous interrupts are delayed until they are enabled. This method is similar to sigprocmask(3).
NOTE
Asynchronous interrupts are difficult to use.
If you need to communicate between threads, please consider to use another way such as Queue.
Or use them with deep understanding about this method.
Usage
In this example, we can guard from Thread#raise exceptions.
Using the :never
TimingSymbol the RuntimeError exception will always be ignored in the first block of the main thread. In the second ::handle_interrupt block we can purposefully handle RuntimeError exceptions.
th = Thread.new do
Thread.handle_interrupt(RuntimeError => :never) {
begin
# You can write resource allocation code safely.
Thread.handle_interrupt(RuntimeError => :immediate) {
# ...
}
ensure
# You can write resource deallocation code safely.
end
}
end
Thread.pass
# ...
th.raise "stop"
While we are ignoring the RuntimeError exception, it’s safe to write our resource allocation code. Then, the ensure block is where we can safely deallocate your resources.
Guarding from Timeout::Error
In the next example, we will guard from the Timeout::Error exception. This will help prevent from leaking resources when Timeout::Error exceptions occur during normal ensure clause. For this example we use the help of the standard library Timeout, from lib/timeout.rb
require 'timeout'
Thread.handle_interrupt(Timeout::Error => :never) {
timeout(10){
# Timeout::Error doesn't occur here
Thread.handle_interrupt(Timeout::Error => :on_blocking) {
# possible to be killed by Timeout::Error
# while blocking operation
}
# Timeout::Error doesn't occur here
}
}
In the first part of the timeout
block, we can rely on Timeout::Error being ignored. Then in the Timeout::Error => :on_blocking
block, any operation that will block the calling thread is susceptible to a Timeout::Error exception being raised.
Stack control settings
It’s possible to stack multiple levels of ::handle_interrupt blocks in order to control more than one ExceptionClass and TimingSymbol at a time.
Thread.handle_interrupt(FooError => :never) {
Thread.handle_interrupt(BarError => :never) {
# FooError and BarError are prohibited.
}
}
Inheritance with ExceptionClass
All exceptions inherited from the ExceptionClass parameter will be considered.
Thread.handle_interrupt(Exception => :never) {
# all exceptions inherited from Exception are prohibited.
}
For handling all interrupts, use Object
and not Exception
as the ExceptionClass, as kill/terminate interrupts are not handled by Exception
.
2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 |
# File 'thread.c', line 2229
static VALUE
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
{
VALUE mask = Qundef;
rb_execution_context_t * volatile ec = GET_EC();
rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
volatile VALUE r = Qnil;
enum ruby_tag_type state;
if (!rb_block_given_p()) {
rb_raise(rb_eArgError, "block is needed.");
}
mask_arg = rb_to_hash_type(mask_arg);
if (OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
mask = Qnil;
}
rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
if (UNDEF_P(mask)) {
return rb_yield(Qnil);
}
if (!RTEST(mask)) {
mask = mask_arg;
}
else if (RB_TYPE_P(mask, T_HASH)) {
OBJ_FREEZE_RAW(mask);
}
rb_ary_push(th->pending_interrupt_mask_stack, mask);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
RUBY_VM_SET_INTERRUPT(th->ec);
}
EC_PUSH_TAG(th->ec);
if ((state = EC_EXEC_TAG()) == TAG_NONE) {
r = rb_yield(Qnil);
}
EC_POP_TAG();
rb_ary_pop(th->pending_interrupt_mask_stack);
if (!rb_threadptr_pending_interrupt_empty_p(th)) {
th->pending_interrupt_queue_checked = 0;
RUBY_VM_SET_INTERRUPT(th->ec);
}
RUBY_VM_CHECK_INTS(th->ec);
if (state) {
EC_JUMP_TAG(th->ec, state);
}
return r;
}
|
.ignore_deadlock ⇒ Boolean
Returns the status of the global “ignore deadlock” condition. The default is false
, so that deadlock conditions are not ignored.
See also ::ignore_deadlock=.
3174 3175 3176 3177 3178 |
# File 'thread.c', line 3174
static VALUE
rb_thread_s_ignore_deadlock(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
}
|
.ignore_deadlock=(boolean) ⇒ Boolean
Returns the new state. When set to true
, the VM will not check for deadlock conditions. It is only useful to set this if your application can break a deadlock condition via some other means, such as a signal.
Thread.ignore_deadlock = true
queue = Thread::Queue.new
trap(:SIGUSR1){queue.push "Received signal"}
# raises fatal error unless ignoring deadlock
puts queue.pop
See also ::ignore_deadlock.
3201 3202 3203 3204 3205 3206 |
# File 'thread.c', line 3201
static VALUE
rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
return val;
}
|
.kill(thread) ⇒ Object
2761 2762 2763 2764 2765 |
# File 'thread.c', line 2761
static VALUE
rb_thread_s_kill(VALUE obj, VALUE th)
{
return rb_thread_kill(th);
}
|
.list ⇒ Array
Returns an array of Thread objects for all threads that are either runnable or stopped.
Thread.new { sleep(200) }
Thread.new { 1000000.times {|i| i*i } }
Thread.new { Thread.stop }
Thread.list.each {|t| p t}
This will produce:
#<Thread:0x401b3e84 sleep>
#<Thread:0x401b3f38 run>
#<Thread:0x401b3fb0 sleep>
#<Thread:0x401bdf4c run>
2921 2922 2923 2924 2925 |
# File 'thread.c', line 2921
static VALUE
thread_list(VALUE _)
{
return rb_thread_list();
}
|
.main ⇒ Object
Returns the main thread.
2961 2962 2963 2964 2965 |
# File 'thread.c', line 2961
static VALUE
rb_thread_s_main(VALUE klass)
{
return rb_thread_main();
}
|
.new(*args) ⇒ Object
Thread.new(*args, &proc) -> thread
Thread.new(*args) { |args| ... } -> thread
Creates a new thread executing the given block.
Any +args+ given to ::new will be passed to the block:
arr = [] a, b, c = 1, 2, 3 Thread.new(a,b,c) { |d,e,f| arr << d << e << f }.join arr #=> [1, 2, 3]
A ThreadError exception is raised if ::new is called without a block.
If you're going to subclass Thread, be sure to call super in your
+initialize+ method, otherwise a ThreadError will be raised.
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 |
# File 'thread.c', line 896
static VALUE
thread_s_new(int argc, VALUE *argv, VALUE klass)
{
rb_thread_t *th;
VALUE thread = rb_thread_alloc(klass);
if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
rb_raise(rb_eThreadError, "can't alloc thread");
}
rb_obj_call_init_kw(thread, argc, argv, RB_PASS_CALLED_KEYWORDS);
th = rb_thread_ptr(thread);
if (!threadptr_initialized(th)) {
rb_raise(rb_eThreadError, "uninitialized thread - check `%"PRIsVALUE"#initialize'",
klass);
}
return thread;
}
|
.pass ⇒ nil
Give the thread scheduler a hint to pass execution to another thread. A running thread may or may not switch, it depends on OS and processor.
1903 1904 1905 1906 1907 1908 |
# File 'thread.c', line 1903
static VALUE
thread_s_pass(VALUE klass)
{
rb_thread_schedule();
return Qnil;
}
|
.pending_interrupt?(error = nil) ⇒ Boolean
Returns whether or not the asynchronous queue is empty.
Since Thread::handle_interrupt can be used to defer asynchronous events, this method can be used to determine if there are any deferred events.
If you find this method returns true, then you may finish :never
blocks.
For example, the following method processes deferred asynchronous events immediately.
def Thread.kick_interrupt_immediately
Thread.handle_interrupt(Object => :immediate) {
Thread.pass
}
end
If error
is given, then check only for error
type deferred events.
Usage
th = Thread.new{
Thread.handle_interrupt(RuntimeError => :on_blocking){
while true
...
# reach safe point to invoke interrupt
if Thread.pending_interrupt?
Thread.handle_interrupt(Object => :immediate){}
end
...
end
}
}
...
th.raise # stop thread
This example can also be written as the following, which you should use to avoid asynchronous interrupts.
flag = true
th = Thread.new{
Thread.handle_interrupt(RuntimeError => :on_blocking){
while true
...
# reach safe point to invoke interrupt
break if flag == false
...
end
}
}
...
flag = false # stop thread
2378 2379 2380 2381 2382 |
# File 'thread.c', line 2378
static VALUE
rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
{
return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
}
|
.report_on_exception ⇒ Boolean
Returns the status of the global “report on exception” condition.
The default is true
since Ruby 2.5.
All threads created when this flag is true will report a message on $stderr if an exception kills the thread.
Thread.new { 1.times { raise } }
will produce this output on $stderr:
#<Thread:...> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
2: from -e:1:in `block in <main>'
1: from -e:1:in `times'
This is done to catch errors in threads early. In some cases, you might not want this output. There are multiple ways to avoid the extra output:
-
If the exception is not intended, the best is to fix the cause of the exception so it does not happen anymore.
-
If the exception is intended, it might be better to rescue it closer to where it is raised rather then let it kill the Thread.
-
If it is guaranteed the Thread will be joined with Thread#join or Thread#value, then it is safe to disable this report with
Thread.current.report_on_exception = false
when starting the Thread. However, this might handle the exception much later, or not at all if the Thread is never joined due to the parent thread being blocked, etc.
See also ::report_on_exception=.
There is also an instance level method to set this for a specific thread, see #report_on_exception=.
3118 3119 3120 3121 3122 |
# File 'thread.c', line 3118
static VALUE
rb_thread_s_report_exc(VALUE _)
{
return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
}
|
.report_on_exception=(boolean) ⇒ Boolean
Returns the new state. When set to true
, all threads created afterwards will inherit the condition and report a message on $stderr if an exception kills a thread:
Thread.report_on_exception = true
t1 = Thread.new do
puts "In new thread"
raise "Exception from thread"
end
sleep(1)
puts "In the main thread"
This will produce:
In new thread
#<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
In the main thread
See also ::report_on_exception.
There is also an instance level method to set this for a specific thread, see #report_on_exception=.
3155 3156 3157 3158 3159 3160 |
# File 'thread.c', line 3155
static VALUE
rb_thread_s_report_exc_set(VALUE self, VALUE val)
{
GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
return val;
}
|
.start([args]) {|args| ... } ⇒ Object .fork([args]) {|args| ... } ⇒ Object
Basically the same as ::new. However, if class Thread is subclassed, then calling start
in that subclass will not invoke the subclass’s initialize
method.
925 926 927 928 929 930 931 932 933 934 |
# File 'thread.c', line 925
static VALUE
thread_start(VALUE klass, VALUE args)
{
struct thread_create_params params = {
.type = thread_invoke_type_proc,
.args = args,
.proc = rb_block_proc(),
};
return thread_create_core(rb_thread_alloc(klass), ¶ms);
}
|
.stop ⇒ nil
2886 2887 2888 2889 2890 |
# File 'thread.c', line 2886
static VALUE
thread_stop(VALUE _)
{
return rb_thread_stop();
}
|
Instance Method Details
#[](sym) ⇒ Object?
Attribute Reference—Returns the value of a fiber-local variable (current thread’s root fiber if not explicitly inside a Fiber), using either a symbol or a string name. If the specified variable does not exist, returns nil
.
[
Thread.new { Thread.current["name"] = "A" },
Thread.new { Thread.current[:name] = "B" },
Thread.new { Thread.current["name"] = "C" }
].each do |th|
th.join
puts "#{th.inspect}: #{th[:name]}"
end
This will produce:
#<Thread:0x00000002a54220 dead>: A
#<Thread:0x00000002a541a8 dead>: B
#<Thread:0x00000002a54130 dead>: C
Thread#[] and Thread#[]= are not thread-local but fiber-local. This confusion did not exist in Ruby 1.8 because fibers are only available since Ruby 1.9. Ruby 1.9 chooses that the methods behaves fiber-local to save following idiom for dynamic scope.
def meth(newvalue)
begin
oldvalue = Thread.current[:name]
Thread.current[:name] = newvalue
yield
ensure
Thread.current[:name] = oldvalue
end
end
The idiom may not work as dynamic scope if the methods are thread-local and a given block switches fiber.
f = Fiber.new {
meth(1) {
Fiber.yield
}
}
meth(2) {
f.resume
}
f.resume
p Thread.current[:name]
#=> nil if fiber-local
#=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
For thread-local variables, please see #thread_variable_get and #thread_variable_set.
3582 3583 3584 3585 3586 3587 3588 |
# File 'thread.c', line 3582
static VALUE
rb_thread_aref(VALUE thread, VALUE key)
{
ID id = rb_check_id(&key);
if (!id) return Qnil;
return rb_thread_local_aref(thread, id);
}
|
#[]=(sym) ⇒ Object
Attribute Assignment—Sets or creates the value of a fiber-local variable, using either a symbol or a string.
See also Thread#[].
For thread-local variables, please see #thread_variable_set and #thread_variable_get.
3687 3688 3689 3690 3691 |
# File 'thread.c', line 3687
static VALUE
rb_thread_aset(VALUE self, VALUE id, VALUE val)
{
return rb_thread_local_aset(self, rb_to_id(id), val);
}
|
#abort_on_exception ⇒ Boolean
Returns the status of the thread-local “abort on exception” condition for this thr
.
The default is false
.
See also #abort_on_exception=.
There is also a class level method to set this for all threads, see ::abort_on_exception.
3048 3049 3050 3051 3052 |
# File 'thread.c', line 3048
static VALUE
rb_thread_abort_exc(VALUE thread)
{
return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
}
|
#abort_on_exception=(boolean) ⇒ Boolean
When set to true
, if this thr
is aborted by an exception, the raised exception will be re-raised in the main thread.
See also #abort_on_exception.
There is also a class level method to set this for all threads, see ::abort_on_exception=.
3068 3069 3070 3071 3072 3073 |
# File 'thread.c', line 3068
static VALUE
rb_thread_abort_exc_set(VALUE thread, VALUE val)
{
rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
return val;
}
|
#add_trace_func(proc) ⇒ Proc
Adds proc as a handler for tracing.
See Thread#set_trace_func and Kernel#set_trace_func.
614 615 616 617 618 619 |
# File 'vm_trace.c', line 614
static VALUE
thread_add_trace_func_m(VALUE obj, VALUE trace)
{
thread_add_trace_func(GET_EC(), rb_thread_ptr(obj), trace);
return trace;
}
|
#alive? ⇒ Boolean
3357 3358 3359 3360 3361 |
# File 'thread.c', line 3357
static VALUE
rb_thread_alive_p(VALUE thread)
{
return RBOOL(!thread_finished(rb_thread_ptr(thread)));
}
|
#backtrace ⇒ Array?
Returns the current backtrace of the target thread.
5321 5322 5323 5324 5325 |
# File 'thread.c', line 5321
static VALUE
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
{
return rb_vm_thread_backtrace(argc, argv, thval);
}
|
#backtrace_locations(*args) ⇒ Object
Returns the execution stack for the target thread—an array containing backtrace location objects.
See Thread::Backtrace::Location for more information.
This method behaves similarly to Kernel#caller_locations except it applies to a specific thread.
5338 5339 5340 5341 5342 |
# File 'thread.c', line 5338
static VALUE
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
{
return rb_vm_thread_backtrace_locations(argc, argv, thval);
}
|
#exit ⇒ Object #kill ⇒ Object #terminate ⇒ Object
Terminates thr
and schedules another thread to be run, returning the terminated Thread. If this is the main thread, or the last thread, exits the process.
2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 |
# File 'thread.c', line 2709
VALUE
rb_thread_kill(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (target_th->to_kill || target_th->status == THREAD_KILLED) {
return thread;
}
if (target_th == target_th->vm->ractor.main_thread) {
rb_exit(EXIT_SUCCESS);
}
RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
if (target_th == GET_THREAD()) {
/* kill myself immediately */
rb_threadptr_to_kill(target_th);
}
else {
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
rb_threadptr_interrupt(target_th);
}
return thread;
}
|
#fetch(sym) ⇒ Object #fetch(sym) { ... } ⇒ Object #fetch(sym, default) ⇒ Object
Returns a fiber-local for the given key. If the key can’t be found, there are several options: With no other arguments, it will raise a KeyError exception; if default is given, then that will be returned; if the optional code block is specified, then that will be run and its result returned. See Thread#[] and Hash#fetch.
3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 |
# File 'thread.c', line 3603
static VALUE
rb_thread_fetch(int argc, VALUE *argv, VALUE self)
{
VALUE key, val;
ID id;
rb_thread_t *target_th = rb_thread_ptr(self);
int block_given;
rb_check_arity(argc, 1, 2);
key = argv[0];
block_given = rb_block_given_p();
if (block_given && argc == 2) {
rb_warn("block supersedes default value argument");
}
id = rb_check_id(&key);
if (id == recursive_key) {
return target_th->ec->local_storage_recursive_hash;
}
else if (id && target_th->ec->local_storage &&
rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
return val;
}
else if (block_given) {
return rb_yield(key);
}
else if (argc == 1) {
rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
}
else {
return argv[1];
}
}
|
#group ⇒ nil
3262 3263 3264 3265 3266 |
# File 'thread.c', line 3262
VALUE
rb_thread_group(VALUE thread)
{
return rb_thread_ptr(thread)->thgroup;
}
|
#join ⇒ Object #join(limit) ⇒ Object
The calling thread will suspend execution and run this thr
.
Does not return until thr
exits or until the given limit
seconds have passed.
If the time limit expires, nil
will be returned, otherwise thr
is returned.
Any threads not joined will be killed when the main program exits.
If thr
had previously raised an exception and the ::abort_on_exception or $DEBUG flags are not set, (so the exception has not yet been processed), it will be processed at this time.
a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
x.join # Let thread x finish, thread a will be killed on exit.
#=> "axyz"
The following example illustrates the limit
parameter.
y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
puts "Waiting" until y.join(0.15)
This will produce:
tick...
Waiting
tick...
Waiting
tick...
tick...
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 |
# File 'thread.c', line 1178
static VALUE
thread_join_m(int argc, VALUE *argv, VALUE self)
{
VALUE timeout = Qnil;
rb_hrtime_t rel = 0, *limit = 0;
if (rb_check_arity(argc, 0, 1)) {
timeout = argv[0];
}
// Convert the timeout eagerly, so it's always converted and deterministic
/*
* This supports INFINITY and negative values, so we can't use
* rb_time_interval right now...
*/
if (NIL_P(timeout)) {
/* unlimited */
}
else if (FIXNUM_P(timeout)) {
rel = rb_sec2hrtime(NUM2TIMET(timeout));
limit = &rel;
}
else {
limit = double2hrtime(&rel, rb_num2dbl(timeout));
}
return thread_join(rb_thread_ptr(self), timeout, limit);
}
|
#key?(sym) ⇒ Boolean
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 |
# File 'thread.c', line 3768
static VALUE
rb_thread_key_p(VALUE self, VALUE key)
{
VALUE val;
ID id = rb_check_id(&key);
struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
if (!id || local_storage == NULL) {
return Qfalse;
}
return RBOOL(rb_id_table_lookup(local_storage, id, &val));
}
|
#keys ⇒ Array
3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 |
# File 'thread.c', line 3809
static VALUE
rb_thread_keys(VALUE self)
{
struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
VALUE ary = rb_ary_new();
if (local_storage) {
rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
}
return ary;
}
|
#exit ⇒ Object #kill ⇒ Object #terminate ⇒ Object
Terminates thr
and schedules another thread to be run, returning the terminated Thread. If this is the main thread, or the last thread, exits the process.
2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 |
# File 'thread.c', line 2709
VALUE
rb_thread_kill(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (target_th->to_kill || target_th->status == THREAD_KILLED) {
return thread;
}
if (target_th == target_th->vm->ractor.main_thread) {
rb_exit(EXIT_SUCCESS);
}
RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
if (target_th == GET_THREAD()) {
/* kill myself immediately */
rb_threadptr_to_kill(target_th);
}
else {
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
rb_threadptr_interrupt(target_th);
}
return thread;
}
|
#name ⇒ String
show the name of the thread.
3395 3396 3397 3398 3399 |
# File 'thread.c', line 3395
static VALUE
rb_thread_getname(VALUE thread)
{
return rb_thread_ptr(thread)->name;
}
|
#name=(name) ⇒ String
set given name to the ruby thread. On some platform, it may set the name to pthread and/or kernel.
3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 |
# File 'thread.c', line 3409
static VALUE
rb_thread_setname(VALUE thread, VALUE name)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (!NIL_P(name)) {
rb_encoding *enc;
StringValueCStr(name);
enc = rb_enc_get(name);
if (!rb_enc_asciicompat(enc)) {
rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
rb_enc_name(enc));
}
name = rb_str_new_frozen(name);
}
target_th->name = name;
if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
native_set_another_thread_name(target_th->nt->thread_id, name);
}
return name;
}
|
#native_thread_id ⇒ Integer
Return the native thread ID which is used by the Ruby thread.
The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3))
-
On Linux it is TID returned by gettid(2).
-
On macOS it is the system-wide unique integral ID of thread returned by pthread_threadid_np(3).
-
On FreeBSD it is the unique integral ID of the thread returned by pthread_getthreadid_np(3).
-
On Windows it is the thread identifier returned by GetThreadId().
-
On other platforms, it raises NotImplementedError.
NOTE: If the thread is not associated yet or already deassociated with a native thread, it returns nil. If the Ruby implementation uses M:N thread model, the ID may change depending on the timing.
3454 3455 3456 3457 3458 3459 3460 |
# File 'thread.c', line 3454
static VALUE
rb_thread_native_thread_id(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (rb_threadptr_dead(target_th)) return Qnil;
return native_thread_native_thread_id(target_th);
}
|
#pending_interrupt?(error = nil) ⇒ Boolean
Returns whether or not the asynchronous queue is empty for the target thread.
If error
is given, then check only for error
type deferred events.
See ::pending_interrupt? for more information.
2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 |
# File 'thread.c', line 2298
static VALUE
rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
{
rb_thread_t *target_th = rb_thread_ptr(target_thread);
if (!target_th->pending_interrupt_queue) {
return Qfalse;
}
if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
return Qfalse;
}
if (rb_check_arity(argc, 0, 1)) {
VALUE err = argv[0];
if (!rb_obj_is_kind_of(err, rb_cModule)) {
rb_raise(rb_eTypeError, "class or module required for rescue clause");
}
return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
}
else {
return Qtrue;
}
}
|
#priority ⇒ Integer
Returns the priority of thr. Default is inherited from the current thread which creating the new thread, or zero for the initial main thread; higher-priority thread will run more frequently than lower-priority threads (but lower-priority threads can also run).
This is just hint for Ruby thread scheduler. It may be ignored on some platform.
Thread.current.priority #=> 0
3905 3906 3907 3908 3909 |
# File 'thread.c', line 3905
static VALUE
rb_thread_priority(VALUE thread)
{
return INT2NUM(rb_thread_ptr(thread)->priority);
}
|
#priority=(integer) ⇒ Object
Sets the priority of thr to integer. Higher-priority threads will run more frequently than lower-priority threads (but lower-priority threads can also run).
This is just hint for Ruby thread scheduler. It may be ignored on some platform.
count1 = count2 = 0
a = Thread.new do
loop { count1 += 1 }
end
a.priority = -1
b = Thread.new do
loop { count2 += 1 }
end
b.priority = -2
sleep 1 #=> 1
count1 #=> 622504
count2 #=> 5832
3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 |
# File 'thread.c', line 3938
static VALUE
rb_thread_priority_set(VALUE thread, VALUE prio)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
int priority;
#if USE_NATIVE_THREAD_PRIORITY
target_th->priority = NUM2INT(prio);
native_thread_apply_priority(th);
#else
priority = NUM2INT(prio);
if (priority > RUBY_THREAD_PRIORITY_MAX) {
priority = RUBY_THREAD_PRIORITY_MAX;
}
else if (priority < RUBY_THREAD_PRIORITY_MIN) {
priority = RUBY_THREAD_PRIORITY_MIN;
}
target_th->priority = (int8_t)priority;
#endif
return INT2NUM(target_th->priority);
}
|
#raise ⇒ Object #raise(string) ⇒ Object #raise(exception[, string [, array]]) ⇒ Object
Raises an exception from the given thread. The caller does not have to be thr
. See Kernel#raise for more information.
Thread.abort_on_exception = true
a = Thread.new { sleep(200) }
a.raise("Gotcha")
This will produce:
prog.rb:3: Gotcha (RuntimeError)
from prog.rb:2:in `initialize'
from prog.rb:2:in `new'
from prog.rb:2
2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 |
# File 'thread.c', line 2681
static VALUE
thread_raise_m(int argc, VALUE *argv, VALUE self)
{
rb_thread_t *target_th = rb_thread_ptr(self);
const rb_thread_t *current_th = GET_THREAD();
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_raise(target_th, argc, argv);
/* To perform Thread.current.raise as Kernel.raise */
if (current_th == target_th) {
RUBY_VM_CHECK_INTS(target_th->ec);
}
return Qnil;
}
|
#report_on_exception ⇒ Boolean
Returns the status of the thread-local “report on exception” condition for this thr
.
The default value when creating a Thread is the value of the global flag Thread.report_on_exception.
See also #report_on_exception=.
There is also a class level method to set this for all new threads, see ::report_on_exception=.
3225 3226 3227 3228 3229 |
# File 'thread.c', line 3225
static VALUE
rb_thread_report_exc(VALUE thread)
{
return RBOOL(rb_thread_ptr(thread)->report_on_exception);
}
|
#report_on_exception=(boolean) ⇒ Boolean
When set to true
, a message is printed on $stderr if an exception kills this thr
. See ::report_on_exception for details.
See also #report_on_exception.
There is also a class level method to set this for all new threads, see ::report_on_exception=.
3245 3246 3247 3248 3249 3250 |
# File 'thread.c', line 3245
static VALUE
rb_thread_report_exc_set(VALUE thread, VALUE val)
{
rb_thread_ptr(thread)->report_on_exception = RTEST(val);
return val;
}
|
#run ⇒ Object
2851 2852 2853 2854 2855 2856 2857 |
# File 'thread.c', line 2851
VALUE
rb_thread_run(VALUE thread)
{
rb_thread_wakeup(thread);
rb_thread_schedule();
return thread;
}
|
#set_trace_func(proc) ⇒ Proc #set_trace_func(nil) ⇒ nil
Establishes proc on thr as the handler for tracing, or disables tracing if the parameter is nil
.
See Kernel#set_trace_func.
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
# File 'vm_trace.c', line 632
static VALUE
thread_set_trace_func_m(VALUE target_thread, VALUE trace)
{
rb_execution_context_t *ec = GET_EC();
rb_thread_t *target_th = rb_thread_ptr(target_thread);
rb_threadptr_remove_event_hook(ec, target_th, call_trace_func, Qundef);
if (NIL_P(trace)) {
return Qnil;
}
else {
thread_add_trace_func(ec, target_th, trace);
return trace;
}
}
|
#status ⇒ String, ...
Returns the status of thr
.
"sleep"
-
Returned if this thread is sleeping or waiting on I/O
"run"
-
When this thread is executing
"aborting"
-
If this thread is aborting
false
-
When this thread is terminated normally
nil
-
If terminated with an exception.
a = Thread.new { raise("die now") } b = Thread.new { Thread.stop } c = Thread.new { Thread.exit } d = Thread.new { sleep } d.kill #=> #<Thread:0x401b3678 aborting> a.status #=> nil b.status #=> "sleep" c.status #=> false d.status #=> "aborting" Thread.current.status #=> "run"
See also the instance methods #alive? and #stop?
3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 |
# File 'thread.c', line 3323
static VALUE
rb_thread_status(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (rb_threadptr_dead(target_th)) {
if (!NIL_P(target_th->ec->errinfo) &&
!FIXNUM_P(target_th->ec->errinfo)) {
return Qnil;
}
else {
return Qfalse;
}
}
else {
return rb_str_new2(thread_status_name(target_th, FALSE));
}
}
|
#stop? ⇒ Boolean
3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 |
# File 'thread.c', line 3377
static VALUE
rb_thread_stop_p(VALUE thread)
{
rb_thread_t *th = rb_thread_ptr(thread);
if (rb_threadptr_dead(th)) {
return Qtrue;
}
return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
}
|
#exit ⇒ Object #kill ⇒ Object #terminate ⇒ Object
Terminates thr
and schedules another thread to be run, returning the terminated Thread. If this is the main thread, or the last thread, exits the process.
2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 |
# File 'thread.c', line 2709
VALUE
rb_thread_kill(VALUE thread)
{
rb_thread_t *target_th = rb_thread_ptr(thread);
if (target_th->to_kill || target_th->status == THREAD_KILLED) {
return thread;
}
if (target_th == target_th->vm->ractor.main_thread) {
rb_exit(EXIT_SUCCESS);
}
RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
if (target_th == GET_THREAD()) {
/* kill myself immediately */
rb_threadptr_to_kill(target_th);
}
else {
threadptr_check_pending_interrupt_queue(target_th);
rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
rb_threadptr_interrupt(target_th);
}
return thread;
}
|
#thread_variable?(key) ⇒ Boolean
Returns true
if the given string (or symbol) exists as a thread-local variable.
me = Thread.current
me.thread_variable_set(:oliver, "a")
me.thread_variable?(:oliver) #=> true
me.thread_variable?(:stanley) #=> false
Note that these are not fiber local variables. Please see Thread#[] and Thread#thread_variable_get for more details.
3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 |
# File 'thread.c', line 3877
static VALUE
rb_thread_variable_p(VALUE thread, VALUE key)
{
VALUE locals;
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return Qfalse;
}
locals = rb_thread_local_storage(thread);
return RBOOL(rb_hash_lookup(locals, rb_to_symbol(key)) != Qnil);
}
|
#thread_variable_get(key) ⇒ Object?
Returns the value of a thread local variable that has been set. Note that these are different than fiber local values. For fiber local values, please see Thread#[] and Thread#[]=.
Thread local values are carried along with threads, and do not respect fibers. For example:
Thread.new {
Thread.current.thread_variable_set("foo", "bar") # set a thread local
Thread.current["foo"] = "bar" # set a fiber local
Fiber.new {
Fiber.yield [
Thread.current.thread_variable_get("foo"), # get the thread local
Thread.current["foo"], # get the fiber local
]
}.resume
}.join.value # => ['bar', nil]
The value “bar” is returned for the thread local, where nil is returned for the fiber local. The fiber is executed in the same thread, so the thread local values are available.
3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 |
# File 'thread.c', line 3721
static VALUE
rb_thread_variable_get(VALUE thread, VALUE key)
{
VALUE locals;
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return Qnil;
}
locals = rb_thread_local_storage(thread);
return rb_hash_aref(locals, rb_to_symbol(key));
}
|
#thread_variable_set(key, value) ⇒ Object
Sets a thread local with key
to value
. Note that these are local to threads, and not to fibers. Please see Thread#thread_variable_get and Thread#[] for more information.
3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 |
# File 'thread.c', line 3742
static VALUE
rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
{
VALUE locals;
if (OBJ_FROZEN(thread)) {
rb_frozen_error_raise(thread, "can't modify frozen thread locals");
}
locals = rb_thread_local_storage(thread);
return rb_hash_aset(locals, rb_to_symbol(key), val);
}
|
#thread_variables ⇒ Array
Returns an array of the names of the thread-local variables (as Symbols).
thr = Thread.new do
Thread.current.thread_variable_set(:cat, 'meow')
Thread.current.thread_variable_set("dog", 'woof')
end
thr.join #=> #<Thread:0x401b3f10 dead>
thr.thread_variables #=> [:dog, :cat]
Note that these are not fiber local variables. Please see Thread#[] and Thread#thread_variable_get for more details.
3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 |
# File 'thread.c', line 3845
static VALUE
rb_thread_variables(VALUE thread)
{
VALUE locals;
VALUE ary;
ary = rb_ary_new();
if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
return ary;
}
locals = rb_thread_local_storage(thread);
rb_hash_foreach(locals, keys_i, ary);
return ary;
}
|
#to_s ⇒ String Also known as: inspect
Dump the name, id, and status of thr to a string.
3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 |
# File 'thread.c', line 3472
static VALUE
rb_thread_to_s(VALUE thread)
{
VALUE cname = rb_class_path(rb_obj_class(thread));
rb_thread_t *target_th = rb_thread_ptr(thread);
const char *status;
VALUE str, loc;
status = thread_status_name(target_th, TRUE);
str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
if (!NIL_P(target_th->name)) {
rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
}
if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
}
rb_str_catf(str, " %s>", status);
return str;
}
|
#value ⇒ Object
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 |
# File 'thread.c', line 1221
static VALUE
thread_value(VALUE self)
{
rb_thread_t *th = rb_thread_ptr(self);
thread_join(th, Qnil, 0);
if (UNDEF_P(th->value)) {
// If the thread is dead because we forked th->value is still Qundef.
return Qnil;
}
return th->value;
}
|
#wakeup ⇒ Object
2804 2805 2806 2807 2808 2809 2810 2811 |
# File 'thread.c', line 2804
VALUE
rb_thread_wakeup(VALUE thread)
{
if (!RTEST(rb_thread_wakeup_alive(thread))) {
rb_raise(rb_eThreadError, "killed thread");
}
return thread;
}
|