Re: Thread#stop

The following probably contains world-destroying race-conditions, but it
roughly works. Making it work properly is left as an exercise for the
reader.

$stoppable = []
$stop_all = false

def stop_all
$stop_all = true
end

def start_all
$stop_all = false
$stoppable.each {|t| p “waking #{t}”; t.wakeup}
end

set_trace_func proc { |event, file, line, id, binding, classname|
if $stop_all && $stoppable.include?(Thread.current)
p “#{Thread.current} stopping”
Thread.stop
p “#{Thread.current} restarted”
end
}

$stoppable << Thread.new {
while sleep 1
p “child is running”
end
}

p “started”
sleep 5
p “stopping all”
stop_all
sleep 5
p “starting all”
start_all
sleep 5
p “exiting”

Daniel S. wrote:

The following probably contains world-destroying race-conditions, but it
roughly works. Making it work properly is left as an exercise for the
reader.

Nice, but I think set_trace_func is going to kill my app. Thanks anyway,
though.

Daniel S. wrote:

end
p “#{Thread.current} restarted”
sleep 5
p “stopping all”
stop_all
sleep 5
p “starting all”
start_all
sleep 5
p “exiting”

On second thought, this is actually just what I need, with a small
modification: only call set_trace_func when the request to stop comes
in, and then call set_trace_func(nil) when threads are restarted.

For example:

th = Thread.new do
loop do
sleep 0.1
puts “alive!”
end
end

sleep 0.3

puts “stopping”
stopper = proc {|event, file, line, id, binding, classname|
Thread.stop if Thread.current == th
}
set_trace_func stopper

sleep 1

puts “starting”
set_trace_func nil
th.wakeup

sleep 0.3

The output is:

alive!
alive!
stopping
starting
alive!
alive!
alive!

(There’s about a 1 sec delay between the “stopping” and “starting”
lines, as desired.)

So, thanks for the suggestion, Daniel. I just didn’t consider it closely
enough before, because I was scared away by set_trace_func.

And with a little extra work, one can use this to approximate
Thread#backtrace as well as Thread#stop (neither of which exist):

th = Thread.new do
loop do
sleep 0.1
puts “alive!”
end
end

sleep 0.3

puts “stopping”
stopper = proc {|event, file, line, id, binding, classname|
if Thread.current == th
puts caller.join("\n from ")
Thread.stop
end
}
set_trace_func stopper

sleep 1

puts “starting”
set_trace_func nil
th.wakeup

sleep 0.3

alive!
alive!
stopping
thread-stop.rb:6
from thread-stop.rb:6
from thread-stop.rb:4
from thread-stop.rb:3
starting
alive!
alive!
alive!

(This is only approximate because the thread isn’t stopped until after
the interpreter finishes whatever current ruby primitive is evaluating,
but it’s good enough for my purposes.)