How to protect some code from SIG terminating with threads/mutex? - ruby

I make some important calculations in endless loop and don't want this calculation interrupts with SIGINT signal (e.g. ctrl-c). So I place loop in thread with protecting important calculation with mutex:
mutex = Mutex.new
trap('INT') do
Thread.new do
puts 'Terminating..'
exit(0)
end.join
end
Thread.new do
loop do
mutex.synchronize do
puts 'Some important computation is started.'
sleep(5)
puts 'Some important computation is done.'
end
sleep(30)
end
end.join
I add another thread inside trap block, so I expect this thread will be executed only when mutex will be unlocked.
But in fact, this second thread starts immediately after receiving SIGINT signal:
Some important computation is started.
^CTerminating..
What am I missed/doing wrong?

You must synchronize the trap thread with the computation:
trap('INT') do
Thread.new do
mutex.synchronize do
puts 'Terminating..'
exit(0)
end
end.join
end
But perhaps it is easier if you set a boolean var in your trap function, and you use it to break the loop.
mustexit = false
trap('INT') do
mustexit= true
end
Thread.new do
loop do
puts 'Some important computation is started.'
sleep(5)
puts 'Some important computation is done.'
if mustexit then
break
end
end
sleep(30)
end.join

Related

How exactly exception unlocks mutex?

This simple test mostly results as
(1) rescue break, m.locked?: false
but sometimes I can see
(1) rescue break, m.locked?: true
m = Mutex.new
6.times do
Thread.new do
begin
m.synchronize do
puts 't1 action'
3.times do
puts '.'
sleep 0.5
end
raise 'Break'
end
rescue
puts "(1) rescue break, m.locked?: #{m.locked?}"
m.synchronize do
sleep 0.1
end
puts '(2) after m {sleep}'
sleep 0.1
puts 'rescue break 2'
end
end
sleep 0.1
t2 = Thread.new do
puts 't2 waiting for mutex'
m.synchronize do
puts '(3) t2 action'
end
end
t2.join
sleep 0.2
puts;puts;
end
I expected that inside the rescue block mutex will be always unlocked.
Environment:
Ruby v2.6.3.62 (2019-04-16) [x64-mingw32]
Nobody promised that the processor would stop the world, waiting for your action :) That said, between
raise 'Break'
and
puts "(1) rescue break, m.locked?: #{m.locked?}"
there is another thread that might get the execution time and in turn lock the mutex.
Please also note, that
raise 'Break'
end
rescue
puts "(1) rescue break, m.locked?: #{m.locked?}"
is effectively the same as
end
puts "(1) rescue break, m.locked?: #{m.locked?}"
In the latter snippet, it should be clear that m might be either locked by another thread, or not; we have it just released, so no promise.

Ruby: how to handler thread exception?

my code here...
require 'thread'
$temp = Thread.new do
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
sleep(2)
end
how to keep runner and loop thread running? and how to fix it like this code?
I tried like Thread.abort_on_exception , but it will kill the process...
Catch the exception inside the thread, and set the error in a variable accessible by the main thread (for testing you could use a global variable like so: $thread_error).
If the error-variable exists, then raise it from the main thread.
You could also use a queue to communicate between the threads, but then it wouldn't be able to utilize multiple threads.
require 'thread'
$temp = Thread.new do
begin
loop do
puts 'loop me'
begin
puts "try thread"
raise Exception.new('QwQ') if rand > 0.5
puts "skip try"
rescue
puts "QwQ"
end
sleep(0.5)
end
puts '...WTF'
rescue Exception => e
$thread_error = e
raise e
end
end
loop do
puts "runner #{Thread.list.length} #{$temp.status}"
raise $thread_error if $thread_error
sleep(2)
end

Alternating routines sharing a mutex

I have method a that is invoked repeatedly at some random time, which triggers method b, which is completely executed after some random time and is in it own thread. I want to ensure that a subsequent execution of a waits until b is completed, which is triggered by the current execution of a. In other words, a and b are to be executed alternatively. I tried to do this using mutex and condition variable as follows:
def a
Thread.new do
$mutex.synchronize do
puts "a"
b
$cv.wait($mutex)
end
end
end
def b
Thread.new do
sleep(rand)
$mutex.synchronize do
puts "b"
$cv.signal
end
end
end
$mutex, $cv = Mutex.new, ConditionVariable.new
loop{a; sleep(rand)}
In this code, $mutex.synchronize do ... end in method a ensures that $cv.signal (also within another $mutex.synchronize do ... end) in method b is not invoked until $cv.wait($mutex) sets $cv into listening mode for signals. This much is given in the document.
Another function I intended to assign to $mutex.synchronize do ... end in method a is to avoid consecutive execution of method a. My reasoning is that $cv.wait($mutex) in method a should avoid $mutex from being completed and released until $cv.signal in method b is invoked, by which time b should be finished.
I expected that a and b are executed alternatively, thereby printing "a" and "b" alternatively. But in reality, they are not; each of "a" or "b" can be printed consecutively.
After that, I thought that my reasoning above may be wrong in the sense that $mutex is rather completed and released even if $cv (or $mutex) is in waiting mode, once $cv.wait($mutex) has been called. So I added some dummy process to a, changing it to:
def a
Thread.new do
$mutex.synchronize do
puts "a"
b
$cv.wait($mutex)
nil # Dummy process intended to keep `$mutex` locked until `$cv` is released
end
end
end
but that did not have effect.
How can this be fixed? Or, what am I wrong about this?
I don't have a solution for you, but isn't the reason a is being called more than you expect is wait releases the lock on the mutex? Otherwise signal could never be called. This seems to happen "as expected" the first time, but after that, you end up having several a threads queued up, itching to enter the synchronize block, and they sneak in before a b thread wakes up and locks the mutex again.
If you poor-man's instrument your code at every turn, you can see it happen:
def a
puts("a before thread #{Thread.current}")
Thread.new do
puts(" a synch0 #{Thread.current}")
$mutex.synchronize do
puts(" a before b #{Thread.current}")
b
puts(" a after b, before wait #{Thread.current}")
$cv.wait($mutex)
puts(" a after wait #{Thread.current}")
end
puts(" a synch1 #{Thread.current}")
end
puts("a after thread #{Thread.current}")
end
def b
puts("b before thread #{Thread.current}")
Thread.new do
puts(" b before sleep #{Thread.current}")
sleep(rand)
puts(" b after sleep, synch0 #{Thread.current}")
$mutex.synchronize do
puts(" b before signal #{Thread.current}")
$cv.signal
puts(" b after signal #{Thread.current}")
end
puts(" b synch1 #{Thread.current}")
end
puts("b after thread #{Thread.current}")
end
$mutex, $cv = Mutex.new, ConditionVariable.new
loop{a; sleep(rand)}
I know it sounds strange, but it will be easier to use a queue to block those threads:
def a
Thread.new do
$queue.pop
puts "a"
b
end
end
def b
Thread.new do
sleep(rand)
puts "b"
$queue << true
end
end
$queue = Queue.new
$queue << true
loop{a; sleep(rand)}

ruby - start a thread

I am new to ruby and trying to work around threads
Let's say I have a method which I want to run every x seconds as follows
def say_hello
puts 'hello world'
end
I am trying to run it as follows
Thread.new do
while true do
say_hello
sleep(5)
end
end
But when I run the script, nothing is displayed on the console. What am I missing? Thanks!
The main thread is exiting before your thread can run. Use the join method to make the current thread wait for the say_hello thread to finish executing (though it never will).
t = Thread.new do
while true do
say_hello
sleep(5)
end
end
t.join
You are creating the Thread object, but you are not waiting for it to finish its execution, try with:
Thread.new do
while true do
say_hello
sleep(5)
end
end.join
Try
t1 = Thread.new do
while true do
say_hello
sleep(5)
end
end
t1.join

How do I interrupt a loop in a command-line script, but only after the method finishes?

I have a Ruby script that looks like:
def big_function
puts "starting..."
#does stuff
puts "done"
end
loop do
big_function
end
It runs indefinitely, executing big_function.
I need a way for a user to interrupt the running script but never in the middle of big_function. If it is interrupted while big_function is running, it should exit when big_function is done.
Trap does that:
interrupted = false
trap("INT") { interrupted = true } # traps Ctrl-C
puts 'Press Ctrl-C to exit'
def big_function
puts "starting..."
sleep 1
puts "done"
end
until interrupted do
big_function
end
You could use flags. You can have a separate process polling in the key triggering of the user, and if he clicks or press a key that flag changes state. You will have to check the flag before exiting.
flag = false
t = Thread.new() {
loop do
flag = gets
break if flag
end
}
def big_function
puts "starting..."
#does stuff
puts "done"
end
loop do
break if flag
big_function
end

Resources