From 4c9874991152b36467af408df03aaf9dba70c8a1 Mon Sep 17 00:00:00 2001 From: dave Date: Sun, 28 Dec 2003 00:02:59 +0000 Subject: Thread/ThradGroup RDoc git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5325 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- eval.c | 601 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 601 insertions(+) (limited to 'eval.c') diff --git a/eval.c b/eval.c index e588461012..d6d8e57107 100644 --- a/eval.c +++ b/eval.c @@ -9215,6 +9215,47 @@ rb_thread_join(th, limit) return Qtrue; } + +/* + * call-seq: + * thr.join => thr + * thr.join(limit) => thr + * + * The calling thread will suspend execution and run thr. Does not + * return until thr exits or until 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 and $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 x thread finish, a will be killed on exit. + * + * produces: + * + * 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) + * + * produces: + * + * tick... + * Waiting + * tick... + * Waitingtick... + * + * + * tick... + */ + static VALUE rb_thread_join_m(argc, argv, thread) int argc; @@ -9232,18 +9273,59 @@ rb_thread_join_m(argc, argv, thread) return thread; } + +/* + * call-seq: + * Thread.current => thread + * + * Returns the currently executing thread. + * + * Thread.current #=> # + */ + VALUE rb_thread_current() { return curr_thread->thread; } + +/* + * call-seq: + * Thread.main => thread + * + * Returns the main thread for the process. + * + * Thread.main #=> # + */ + VALUE rb_thread_main() { return main_thread->thread; } + +/* + * call-seq: + * Thread.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} + * + * produces: + * + * # + * # + * # + * # + */ + VALUE rb_thread_list() { @@ -9265,6 +9347,22 @@ rb_thread_list() return ary; } + +/* + * call-seq: + * thr.wakeup => thr + * + * Marks thr as eligible for scheduling (it may still remain blocked on + * I/O, however). Does not invoke the scheduler (see Thread#run). + * + * c = Thread.new { Thread.stop; puts "hey!" } + * c.wakeup + * + * produces: + * + * hey! + */ + VALUE rb_thread_wakeup(thread) VALUE thread; @@ -9278,6 +9376,27 @@ rb_thread_wakeup(thread) return thread; } + +/* + * call-seq: + * thr.run => thr + * + * Wakes up thr, making it eligible for scheduling. If not in a critical + * section, then invokes the scheduler. + * + * a = Thread.new { puts "a"; Thread.stop; puts "c" } + * Thread.pass + * puts "Got here" + * a.run + * a.join + * + * produces: + * + * a + * Got here + * c + */ + VALUE rb_thread_run(thread) VALUE thread; @@ -9288,6 +9407,19 @@ rb_thread_run(thread) return thread; } + +/* + * call-seq: + * thr.exit => thr or nil + * thr.kill => thr or nil + * thr.terminate => thr or nil + * + * Terminates thr 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, exits + * the process. + */ + VALUE rb_thread_kill(thread) VALUE thread; @@ -9307,6 +9439,21 @@ rb_thread_kill(thread) return thread; } + +/* + * call-seq: + * Thread.kill(thread) => thread + * + * Causes the given thread to exit (see Thread::exit). + * + * count = 0 + * a = Thread.new { loop { count += 1 } } + * sleep(0.1) #=> 0 + * Thread.kill(a) #=> # + * count #=> 93947 + * a.alive? #=> false + */ + static VALUE rb_thread_s_kill(obj, th) VALUE obj, th; @@ -9314,12 +9461,44 @@ rb_thread_s_kill(obj, th) return rb_thread_kill(th); } + +/* + * call-seq: + * Thread.exit => thread + * + * 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. + */ + static VALUE rb_thread_exit() { return rb_thread_kill(curr_thread->thread); } + +/* + * call-seq: + * Thread.pass => nil + * + * Invokes the thread scheduler to pass execution to another thread. + * + * a = Thread.new { print "a"; Thread.pass; + * print "b"; Thread.pass; + * print "c" } + * b = Thread.new { print "x"; Thread.pass; + * print "y"; Thread.pass; + * print "z" } + * a.join + * b.join + * + * produces: + * + * axbycz + */ + static VALUE rb_thread_pass() { @@ -9327,6 +9506,26 @@ rb_thread_pass() return Qnil; } + +/* + * call-seq: + * Thread.stop => nil + * + * Stops execution of the current thread, putting it into a ``sleep'' state, + * and schedules execution of another thread. Resets the ``critical'' condition + * to false. + * + * a = Thread.new { print "a"; Thread.stop; print "c" } + * Thread.pass + * print "b" + * a.run + * a.join + * + * produces: + * + * abc + */ + VALUE rb_thread_stop() { @@ -9388,6 +9587,17 @@ rb_thread_sleep_forever() rb_thread_schedule(); } + +/* + * call-seq: + * thr.priority => integer + * + * Returns the priority of thr. Default is zero; higher-priority threads + * will run before lower-priority threads. + * + * Thread.current.priority #=> 0 + */ + static VALUE rb_thread_priority(thread) VALUE thread; @@ -9395,6 +9605,30 @@ rb_thread_priority(thread) return INT2NUM(rb_thread_check(thread)->priority); } + +/* + * call-seq: + * thr.priority= integer => thr + * + * Sets the priority of thr to integer. Higher-priority threads + * will run before lower-priority threads. + * + * 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 + * Thread.critical = 1 + * count1 #=> 622504 + * count2 #=> 5832 + */ + static VALUE rb_thread_priority_set(thread, prio) VALUE thread, prio; @@ -9409,6 +9643,19 @@ rb_thread_priority_set(thread, prio) return prio; } + +/* + * call-seq: + * thr.safe_level => integer + * + * Returns the safe level in effect for thr. Setting thread-local safe + * levels can help when implementing sandboxes which run insecure code. + * + * thr = Thread.new { $SAFE = 3; sleep } + * Thread.current.safe_level #=> 0 + * thr.safe_level #=> 3 + */ + static VALUE rb_thread_safe_level(thread) VALUE thread; @@ -9425,12 +9672,50 @@ rb_thread_safe_level(thread) static int ruby_thread_abort; static VALUE thgroup_default; + +/* + * call-seq: + * Thread.abort_on_exception => true or false + * + * Returns the status of the global ``abort on exception'' condition. The + * default is false. When set to true, or if the + * global $DEBUG flag is true (perhaps because the + * command line option -d was specified) all threads will abort + * (the process will exit(0)) if an exception is raised in any + * thread. See also Thread::abort_on_exception=. + */ + static VALUE rb_thread_s_abort_exc() { return ruby_thread_abort?Qtrue:Qfalse; } + +/* + * call-seq: + * Thread.abort_on_exception= boolean => true or false + * + * When set to true, all threads will abort if an exception is + * raised. 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" + * + * produces: + * + * 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 + */ + static VALUE rb_thread_s_abort_exc_set(self, val) VALUE self, val; @@ -9440,6 +9725,16 @@ rb_thread_s_abort_exc_set(self, val) return val; } + +/* + * call-seq: + * thr.abort_on_exception => true or false + * + * Returns the status of the thread-local ``abort on exception'' condition for + * thr. The default is false. See also + * Thread::abort_on_exception=. + */ + static VALUE rb_thread_abort_exc(thread) VALUE thread; @@ -9447,6 +9742,16 @@ rb_thread_abort_exc(thread) return rb_thread_check(thread)->abort?Qtrue:Qfalse; } + +/* + * call-seq: + * thr.abort_on_exception= boolean => true or false + * + * When set to true, causes all threads (including the main + * program) to abort if an exception is raised in thr. The process will + * effectively exit(0). + */ + static VALUE rb_thread_abort_exc_set(thread, val) VALUE thread, val; @@ -9456,6 +9761,17 @@ rb_thread_abort_exc_set(thread, val) return val; } + +/* + * call-seq: + * thr.group => thgrp or nil + * + * Returns the ThreadGroup which contains thr, or nil if + * the thread is not a member of any group. + * + * Thread.main.group #=> # + */ + VALUE rb_thread_group(thread) VALUE thread; @@ -9792,6 +10108,25 @@ rb_thread_s_new(argc, argv, klass) return th->thread; } + +/* + * call-seq: + * Thread.new([arg]*) {|args| block } => thread + * + * Creates and runs a new thread to execute the instructions given in + * block. Any arguments passed to Thread::new are passed + * into the block. + * + * x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" } + * a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" } + * x.join # Let the threads finish before + * a.join # main thread exits... + * + * produces: + * + * abxyzc + */ + static VALUE rb_thread_initialize(thread, args) VALUE thread, args; @@ -9802,6 +10137,17 @@ rb_thread_initialize(thread, args) return rb_thread_start_0(rb_thread_yield, args, rb_thread_check(thread)); } + +/* + * call-seq: + * Thread.start([args]*) {|args| block } => thread + * Thread.fork([args]*) {|args| block } => thread + * + * Basically the same as Thread::new. However, if class + * Thread is subclassed, then calling start in that + * subclass will not invoke the subclass's initialize method. + */ + static VALUE rb_thread_start(klass, args) VALUE klass, args; @@ -9812,6 +10158,18 @@ rb_thread_start(klass, args) return rb_thread_start_0(rb_thread_yield, args, rb_thread_alloc(klass)); } + +/* + * call-seq: + * thr.value => obj + * + * Waits for thr to complete (via Thread#join) and returns + * its value. + * + * a = Thread.new { 2 + 2 } + * a.value #=> 4 + */ + static VALUE rb_thread_value(thread) VALUE thread; @@ -9823,6 +10181,30 @@ rb_thread_value(thread) return th->result; } + +/* + * call-seq: + * thr.status => string, false or nil + * + * Returns the status of thr: ``sleep'' if thr is + * sleeping or waiting on I/O, ``run'' if thr is executing, + * ``aborting'' if thr is aborting, false if + * thr terminated normally, and nil if thr + * terminated with an exception. + * + * a = Thread.new { raise("die now") } + * b = Thread.new { Thread.stop } + * c = Thread.new { Thread.exit } + * d = Thread.new { sleep } + * Thread.critical = true + * d.kill #=> # + * a.status #=> nil + * b.status #=> "sleep" + * c.status #=> false + * d.status #=> "aborting" + * Thread.current.status #=> "run" + */ + static VALUE rb_thread_status(thread) VALUE thread; @@ -9838,6 +10220,19 @@ rb_thread_status(thread) return rb_str_new2(thread_status_name(th->status)); } + +/* + * call-seq: + * thr.alive? => true or false + * + * Returns true if thr is running or sleeping. + * + * thr = Thread.new { } + * thr.join #=> # + * Thread.current.alive? #=> true + * thr.alive? #=> false + */ + static VALUE rb_thread_alive_p(thread) VALUE thread; @@ -9848,6 +10243,19 @@ rb_thread_alive_p(thread) return Qtrue; } + +/* + * call-seq: + * thr.stop? => true or false + * + * Returns true if thr is dead or sleeping. + * + * a = Thread.new { Thread.stop } + * b = Thread.current + * a.stop? #=> true + * b.stop? #=> false + */ + static VALUE rb_thread_stop_p(thread) VALUE thread; @@ -9906,12 +10314,35 @@ rb_thread_cleanup() int rb_thread_critical; + +/* + * call-seq: + * Thread.critical => true or false + * + * Returns the status of the global ``thread critical'' condition. + */ + static VALUE rb_thread_critical_get() { return rb_thread_critical?Qtrue:Qfalse; } + +/* + * call-seq: + * Thread.critical= boolean => true or false + * + * Sets the status of the global ``thread critical'' condition and returns + * it. When set to true, prohibits scheduling of any existing + * thread. Does not block new threads from being created and run. Certain + * thread operations (such as stopping or killing a thread, sleeping in the + * current thread, and raising an exception) may cause a thread to be scheduled + * even when in a critical section. Thread::critical is not + * intended for daily use: it is primarily there to support folks writing + * threading libraries. + */ + static VALUE rb_thread_critical_set(obj, val) VALUE obj, val; @@ -10004,6 +10435,26 @@ rb_thread_raise(argc, argv, th) return Qnil; /* not reached */ } + +/* + * call-seq: + * thr.raise(exception) + * + * Raises an exception (see Kernel::raise) from thr. The + * caller does not have to be thr. + * + * Thread.abort_on_exception = true + * a = Thread.new { sleep(200) } + * a.raise("Gotcha") + * + * produces: + * + * prog.rb:3: Gotcha (RuntimeError) + * from prog.rb:2:in `initialize' + * from prog.rb:2:in `new' + * from prog.rb:2 + */ + static VALUE rb_thread_raise_m(argc, argv, thread) int argc; @@ -10038,6 +10489,28 @@ rb_thread_local_aref(thread, id) return Qnil; } + +/* + * call-seq: + * thr[sym] => obj or nil + * + * Attribute Reference---Returns the value of a thread-local variable, using + * either a symbol or a string name. If the specified variable does not exist, + * returns nil. + * + * a = Thread.new { Thread.current["name"] = "A"; Thread.stop } + * b = Thread.new { Thread.current[:name] = "B"; Thread.stop } + * c = Thread.new { Thread.current["name"] = "C"; Thread.stop } + * Thread.list.each {|x| puts "#{x.inspect}: #{x[:name]}" } + * + * produces: + * + * #: C + * #: B + * #: A + * #: + */ + static VALUE rb_thread_aref(thread, id) VALUE thread, id; @@ -10070,6 +10543,15 @@ rb_thread_local_aset(thread, id, val) return val; } + +/* + * call-seq: + * thr[sym] = obj => obj + * + * Attribute Assignment---Sets or creates the value of a thread-local variable, + * using either a symbol or a string. See also Thread#[]. + */ + static VALUE rb_thread_aset(thread, id, val) VALUE thread, id, val; @@ -10077,6 +10559,20 @@ rb_thread_aset(thread, id, val) return rb_thread_local_aset(thread, rb_to_id(id), val); } + +/* + * call-seq: + * thr.key?(sym) => true or false + * + * Returns true if the given string (or symbol) exists as a + * thread-local variable. + * + * me = Thread.current + * me[:oliver] = "a" + * me.key?(:oliver) #=> true + * me.key?(:stanley) #=> false + */ + static VALUE rb_thread_key_p(thread, id) VALUE thread, id; @@ -10098,6 +10594,21 @@ thread_keys_i(key, value, ary) return ST_CONTINUE; } + +/* + * call-seq: + * thr.keys => array + * + * Returns an an array of the names of the thread-local variables (as Symbols). + * + * thr = Thread.new do + * Thread.current[:cat] = 'meow' + * Thread.current["dog"] = 'woof' + * end + * thr.join #=> # + * thr.keys #=> [:dog, :cat] + */ + static VALUE rb_thread_keys(thread) VALUE thread; @@ -10291,6 +10802,19 @@ struct thgroup { VALUE group; }; + +/* + * Document-class: ThreadGroup + * + * ThreadGroup provides a means of keeping track of a number of + * threads as a group. A Thread can belong to only one + * ThreadGroup at a time; adding a thread to a new group will + * remove it from any previous group. + * + * Newly created threads belong to the same group as the thread from which they + * were created. + */ + static VALUE thgroup_s_alloc _((VALUE)); static VALUE thgroup_s_alloc(klass) @@ -10306,6 +10830,17 @@ thgroup_s_alloc(klass) return group; } + +/* + * call-seq: + * thgrp.list => array + * + * Returns an array of all existing Thread objects that belong to + * this group. + * + * ThreadGroup::Default.list #=> [#] + */ + static VALUE thgroup_list(group) VALUE group; @@ -10327,6 +10862,25 @@ thgroup_list(group) return ary; } + +/* + * call-seq: + * thgrp.enclose => thgrp + * + * Prevents threads from being added to or removed from the receiving + * ThreadGroup. New threads can still be started in an enclosed + * ThreadGroup. + * + * ThreadGroup::Default.enclose #=> # + * thr = Thread::new { Thread.stop } #=> # + * tg = ThreadGroup::new #=> # + * tg.add thr + * + * produces: + * + * ThreadError: can't move from the enclosed thread group + */ + VALUE thgroup_enclose(group) VALUE group; @@ -10339,6 +10893,15 @@ thgroup_enclose(group) return group; } + +/* + * call-seq: + * thgrp.enclosed? => true or false + * + * Returns true if thgrp is enclosed. See also + * ThreadGroup#enclose. + */ + static VALUE thgroup_enclosed_p(group) VALUE group; @@ -10350,6 +10913,33 @@ thgroup_enclosed_p(group) return Qfalse; } + +/* + * call-seq: + * thgrp.add(thread) => thgrp + * + * Adds the given thread to this group, removing it from any other + * group to which it may have previously belonged. + * + * puts "Initial group is #{ThreadGroup::Default.list}" + * tg = ThreadGroup.new + * t1 = Thread.new { sleep } + * t2 = Thread.new { sleep } + * puts "t1 is #{t1}" + * puts "t2 is #{t2}" + * tg.add(t1) + * puts "Initial group now #{ThreadGroup::Default.list}" + * puts "tg group now #{tg.list}" + * + * produces: + * + * Initial group is # + * t1 is # + * t2 is # + * Initial group now ## + * tg group now # + */ + static VALUE thgroup_add(group, thread) VALUE group, thread; @@ -10383,6 +10973,17 @@ thgroup_add(group, thread) return group; } + +/* + * Thread encapsulates the behavior of a thread of + * execution, including the main thread of the Ruby script. + * + * In the descriptions of the methods in this class, the parameter sym + * refers to a symbol, which is either a quoted string or a Symbol + * (such as :name). + * + */ + void Init_Thread() { -- cgit v1.2.3