How to isolate methods in Ruby - multiprocessing question - ruby

I have 3 methods that do something on the file system but each method changes current directory and I can't use Threading for those since it uses same ruby process and I have racing condition. I found Process.spawn (can't use fork on windows) might be solution but I don't know how to spawn 3 process and call method in each of those.
I tried following as a test, but didn't work
#!/usr/bin/env ruby
def something
puts 'printaj'
end
def nextsomething
puts 'vici'
end
pid1 = Process.spawn(something)
pid2 = Process.spawn(nextsomething)
Process.wait pid1
Process.wait pid2
puts 'both finished'

How about threads and mutex?
semaphore = Mutex.new
a = Thread.new {
semaphore.synchronize {
# do something on the file system
}
}
b = Thread.new {
semaphore.synchronize {
# do something on the file system
}
}
With this code you will run only one operation on file system in the same time.
https://ruby-doc.org/core-2.5.0/Mutex.html

Related

What's the correct Ruby pattern for waiting for a background thread to start?

I need to write a Ruby program that executes a background program, and performs some functionality on it.
Before doing any operation, the main thread needs to make sure that the background thread is started. What the correct pattern?
This is not exact:
condition = ConditionVariable.new
mutex = Mutex.new
thread = Thread.new do
mutex.synchronize { condition.signal }
# background work
end
mutex.synchronize { condition.wait(mutex) }
# other work
because :signal could execute before :wait, blocking the main thread.
An exact solution is:
thread = Thread.new do
Thread.current[:started] = true
# background work
end
sleep 0.01 while thread[:started].nil?
# other work
however, it uses sleep, which I'd like to avoid.
Another exact, but more complex, solution is:
mutex = Mutex.new
condition = ConditionVariable.new
thread = Thread.new do
mutex.synchronize do
Thread.current[:started] = true
condition.signal
end
# background work
end
mutex.synchronize do
condition.wait(mutex) if !thread[:started]
end
# other work
Is there any exact, simple and idiomatic way to structure this functionality?
You could use a Queue:
queue = Queue.new
thread = Thread.new do
queue.push :ready
# background work
end
queue.pop
# other work
Queue#pop will wait until an item is available and return it.

Ruby EventMachine, kill running processes?

How can I kill running processes in EventMachine?
Below is an example, I'm starting 10 processes and then I'm trying to erase them all (but it doesn't work). My goal is to not have the "Finished" output.
require "rubygems"
require "eventmachine"
class Event
def start
sleep(5)
puts Time.now.to_s + ": Finished!"
end
end
EventMachine.run do
events = []
10.times {
handle = Event.new
events << handle
EventMachine.defer(proc {
handle.start
})
}
# Terminate all events!
events.each do |handle|
handle = nil
ObjectSpace.garbage_collect
end
end
I'm aware that I could set a variable and check whether it's set when doing the output, but I feel like this isn't the "real" thing, or is this really the only solution there is?
Try EventMachine.stop_event_loop, it will “cause all open connections and accepting servers to be run down and closed”.

How to make a ruby thread execute a function of my choosing?

Is it possible to create a "worker thread" so to speak that is on standby until it receives a function to execute asynchronously?
Is there a way to send a function like
def some_function
puts "hi"
# write something
db.exec()
end
to an existing thread that's just sitting there waiting?
The idea is I'd like to pawn off some database writes to a thread which runs asynchronously.
I thought about creating a Queue instance, then have a thread do something like this:
$command = Queue.new
Thread.new do
while trigger = $command.pop
some_method
end
end
$command.push("go!")
However this does not seem like a particularly good way to go about it. What is a better alternative?
The thread gem looks like it would suit your needs:
require 'thread/channel'
def some_method
puts "hi"
end
channel = Thread.channel
Thread.new do
while data = channel.receive
some_method
end
end
channel.send("go!")
channel.send("ruby!") # Any truthy message will do
channel.send(nil) # Non-truthy message to terminate other thread
sleep(1) # Give other thread time to do I/O
The channel uses ConditionVariable, which you could use yourself if you prefer.

Testing a REPL in Ruby with RSpec and threads

I'm using RSpec to test the behavior of a simple REPL. The REPL just echoes back whatever the input was, unless the input was "exit", in which case it terminates the loop.
To avoid hanging the test runner, I'm running the REPL method inside a separate thread. To make sure that the code in the thread has executed before I write expectations about it, I've found it necessary to include a brief sleep call. If I remove it, the tests fail intermittently because the expectations are sometimes made before the code in the thread has run.
What is a good way to structure the code and spec such that I can make expectations about the REPL's behavior deterministically, without the need for the sleep hack?
Here is the REPL class and the spec:
class REPL
def initialize(stdin = $stdin, stdout = $stdout)
#stdin = stdin
#stdout = stdout
end
def run
#stdout.puts "Type exit to end the session."
loop do
#stdout.print "$ "
input = #stdin.gets.to_s.chomp.strip
break if input == "exit"
#stdout.puts(input)
end
end
end
describe REPL do
let(:stdin) { StringIO.new }
let(:stdout) { StringIO.new }
let!(:thread) { Thread.new { subject.run } }
subject { described_class.new(stdin, stdout) }
# Removing this before hook causes the examples to fail intermittently
before { sleep 0.01 }
after { thread.kill if thread.alive? }
it "prints a message on how to end the session" do
expect(stdout.string).to match(/end the session/)
end
it "prints a prompt for user input" do
expect(stdout.string).to match(/\$ /)
end
it "echoes input" do
stdin.puts("foo")
stdin.rewind
expect(stdout.string).to match(/foo/)
end
end
Instead of letting :stdout be a StringIO, you could back it by a Queue. Then when you try to read from the queue, your tests will just wait until the REPL pushes something into the queue (aka. writes to stdout).
require 'thread'
class QueueIO
def initialize
#queue = Queue.new
end
def write(str)
#queue.push(str)
end
def puts(str)
write(str + "\n")
end
def read
#queue.pop
end
end
let(:stdout) { QueueIO.new }
I just wrote this up without trying it out, and it may not be robust enough for your needs, but it gets the point across. If you use a data structure to synchronize the two threads like this, then you don't need to sleep at all. Since this removes the non-determinism, you shouldn't see the intermittent failures.
I've used a running? guard for situations like this. You probably can't avoid the sleep entirely, but you can avoid unnecessary sleeps.
First, add a running? method to your REPL class.
class REPL
...
def running?
!!#running
end
def run
#running=true
loop do
...
if input == 'exit
#running = false
break
end
...
end
end
end
Then, in your specs, sleep until the REPL is running:
describe REPL do
...
before { sleep 0.01 until REPL.running? }
...
end

Ruby multiple background threads

I need to run multiple background threads in a thread pool with timeout.
The scheme is something like:
#!/usr/bin/env ruby
require 'thread'
def foo(&block)
bar(block)
end
def bar(block)
Thread.abort_on_exception=true
#main = Thread.new { block.call }
end
foo {
sleep 1
puts 'test'
}
Why if i run that i get no output? (and no sleep wait?)
The program ends when the main thread ends. You have to wait on the thread created by bar using join:
foo {
sleep 1
puts 'test'
}.join
Try the work_queue gem http://rubygems.org/gems/work_queue/

Resources