Ruby thread synchronization - ruby

My process has two threads like the following
#semaphore = Mutex.new
.
.
.
.
thread_a = Thread.new {
loop do
#some work
if(some_condition)
#semaphore.synchronize {
#thread_b_running = false
}
end
end
}
thread_b = Thread.new {
while(#semaphore.synchronize { #thread_b_running }) do
#thread_b's work
end
}
thread_a.join
thread_b.join
Basically, thread_a and thread_b do some work in parallel, however when thread_a sees an event happen it needs to shut down thread_b. As you can see right now I am doing it by using a boolean protected by a mutex. I think this approach is not too bad performance wise since thread_b will almost always get the lock without waiting for it. However since I have not written a lot of multithreaded code I was wondering if there is a better way of doing what I'm doing?

If only one of the threads is writing the variable, there is no need for a mutex. So a better way in your example is just removing the mutex.

Related

Creating an async method which throws an exception after a specified amount of time unless a certain condition is met outside of that function

I am currently working on a Ruby script which is supposed to perform different tasks on a pretty long list of hosts. I am using the net-ssh gem for connectivity with those hosts. The thing is, there seem to exist some conditions under which net-ssh times out without throwing an exception. As of know, the script was only once able to finish a run. Most of the time, the scripts just hangs at some point without ever throwing an exception or doing anything.
I thought about running all tasks that may timeout in different threads, passing them a pointer to some variable they can change when the tasks finished successfully, and then check that variable for a given amount of time. If the task has not finished by then, throw an exception in the main thread that I can catch somewhere.
This is the first time I am writing something in Ruby. To give a clear demonstration of what I want to accomplish, this is what I'd do in C++:
void perform_long_running_task(bool* finished);
void start_task_and_throw_on_timeout(int secs, std::function<void(bool*)> func);
int seconds_to_wait {5};
int seconds_task_takes{6};
int main() {
start_task_and_throw_on_timeout(seconds_to_wait, &perform_long_running_task);
// do other stuff
return 0;
}
void perform_long_running_task(bool* finished){
// Do something that may possible timeout..
std::this_thread::sleep_for(std::chrono::seconds(seconds_task_takes));
// Finished..
*finished = true;
}
void start_task_and_throw_on_timeout(int secs, std::function<void(bool*)> func){
bool finished {false};
std::thread task(func, &finished);
while (secs > 0){
std::this_thread::sleep_for(std::chrono::seconds(1));
secs--;
if (finished){
task.join();
return;
}
}
throw std::exception();
}
Here, when 'seconds_task_takes' is bigger than 'seconds_to_wait', an exception is thrown in the main thread. If the task finishes in time, everything goes on smoothly.
However, I need to write my piece of software in a dynamic scripting language that can run anywhere and needs not to be compiled. I would be super glad for any advice about how I could write something like the code above in Ruby.
Thanks alot in advance :)
edit: in the example ,I added a std::function parameter to start_task_and_throw_timeout so it's reusable for all similar functions
I think module timeout has everything you need to do. It allows you to run the block for a while and raise an exception if it was not fast enough.
Here is a code example:
require "timeout"
def run(name)
puts "Running the job #{name}"
sleep(10)
end
begin
Timeout::timeout(5) { run("hard") }
rescue Timeout::Error
puts "Failed!"
end
You can play with it here: https://repl.it/repls/CraftyUnluckyCore. The documentation for the module lives here: https://ruby-doc.org/stdlib-2.5.1/libdoc/timeout/rdoc/Timeout.html. Notice that you can customize not only the timeout, but also error class and message, so different jobs may have different kinds of errors.

How to return a value to a function then starts a thread in the same function?

Is there a way for return a value to a function, then invoking a thread in that function? For example:
def foo
return fast_function
Thread.new do
slow_function
end
end
The reason behind this is that both fast_function and slow_function write to the same resource. But I want to ensure that fast_function runs and complete first, and return its value to foo before slow_function writes to the shared resource. There are some cases where slow_function completes before fast_function and I am hit with a race condition.
EDIT:
More context on the problem. This is related to server-side events I am trying to implement. I am trying to get fast_function to compute an event id and return and html. While slow_function is responsible for notifying the client via event id that the process is done. However, in some cases, slow_function notifies the client before the client event know where to listen, because fast_function did not return the event id yet.
No, a return will exit the function, it would also exit the function in a yield block. In my opinions there are multiple solutions to this problem.
Actually it would be a perfect fit for a Promise of Concurrent Ruby (https://github.com/ruby-concurrency/concurrent-ruby)
you could use it somewhat like this:
def foo
fast = Concurrent::Promise.execute{ fast_function }
slow = promises[:fast].then{ slow_function }
.on_fullfill{ notify_client }
return fast.value
end
As you can guess it will return the value of your fast function.
But it will also call the on_fullfill function (Or a proc) if the slow function has finished. And the most important, it will guarante order.
NOTE: I am not sure if I understood you correctly, if you want to start booth threads at the same time, but ensure that the fast one has finished first. you can do something like this:
fast = Concurrent::Promise.execute{ fast_function }
slow = Concurrent::Promise.execute{ slow_function }
render fast.value # Or what you ever do with the html.
#.value will wait for the Promise to finish.
result slow = slow.value
This way you would start booth functions parallel, but be sure you would get the answer first for the fast one.
Edit 1: I thougth about this, and I am not really sure if you want to have an asynchronous task at all. It is hard to tell since you posted a minimal example (what is correct of coruse).
If you just want to have a function which returns botth function returns in the right order, you could just do a yield:
def foo
yield fast_function
yield slow_function
end

Stopping a for loop until worker is done

I have made a for loop who call a web worker to highlight code inside code tag
I have made this loop, this call my worker and do the job.
highlightBase: ->
codes = document.getElementsByClassName('hljs')
if (codes)
for code in codes
HighlightWorker = new Worker('/js/highlight_worker.js')
HighlightWorker.onmessage = (event) ->
code.innerHTML = event.data
HighlightWorker.postMessage(code.textContent)
This is working well but I need to stop this loop until the worker is done.
Is this possible to stop this loop and continue it after or I need to add some timeOut ?
I am not very familiar very coffeeScript but this might give you an idea.
Stoping a for loop is not possible. There are generator functions for that lazy evaluation purposes. However although you could use, i don't think you need generators here. Instead i guess promises could be very handy. You might use Promise.all(). But first you have to create a utility function to return a promise when you instantiate a worker.
function postMessageToWorker(worker,message){
var p = new Promise((resolve,reject) => worker.onmessage = resolve);
worker.postMessage(message);
return p;
}
var proms = [];
if (codes)
for (var code of codes)
proms.push(postMessageToWorker(new Worker('/js/highlight_worker.js'), code.textContent));
Promise.all(proms).then(a => a.forEach((e,i) => codes[i].innerHTML = e.data));
Here is a plunker code for you to play.
Why do you need to stop the loop?
It looks like you are trying to do synchronous tasks, meaning the workers do their job one after the other.
But the whole thing about workers is that they are asynchronous...
I guess if you want your code to work synchronously, you need to do something like that:
highlightBase: ->
codes = document.getElementsByClassName('hljs')
if (codes)
for code in codes
code.innerHTML = function_that_was_before_made_by_worker()
And that it is...

EventMachine and looping

Here is my code:
EventMachine.run {
conn = EM::Protocols::HttpClient2.connect request.host, 80
req = conn.get(request.query)
req.callback { |response|
p(response.status)
p(response.headers)
p(response.content)
}
}
The callbacks fire, that is, I get the string outputs of the status, etc.
But what I want it to do is fire the callbacks, then repeat. There is more logic I plan to implement, such as tweaking the URL each time, but for now, I just want it to:
Retrieve the URL
Fire the callbacks
Repeat...
My understanding about this pattern was that everything in that loop fires, then returns, then goes on forever until I do an EM.stop.
Right now, it retrieves the URL data, and just seems to hang.
Do I need to do a return of some sort to continue here? Why is it hanging, and not looping over and over?
If I surround the entire above code block with a loop do ... end it works as expected.. is that the correct way to implement this? I suppose I am confused as I thought everything within EM.run repeats when it completes.
The run block you give runs only once. The event loop is not exposed directly to you but is something that's intended to be invisible. Don't confuse the run block with a while loop. It's run once and once only, but it is run while the event loop is executing.
If you want to repeat an operation you need to create some kind of a stack and work through that, with each callback checking the stack if there's more work to do and then issuing another call. EventMachine applications are built using this callback-chaining method.
You will need to implement something like:
def do_stuff(queue, request = nil)
request ||= queue.pop
return unless (request)
conn = EM::Protocols::HttpClient2.connect request.host, 80
req = conn.get(request.query)
req.callback { |response|
p(response.status)
p(response.headers)
p(response.content)
EventMachine.next_tick do
# This schedules an operation to be performed the next time through
# the event-loop. Usually this is almost immediate.
do_stuff(queue)
end
}
end
Inside your event loop you kick of this chain:
EventMachine.run do
queue = [ ... ] # List of things to do
do_stuff(queue)
end
You can probably find a more elegant way to implement this once you get a better sense of how EventMachine works.

MonitorMixin condition variable -> deadlock

I have a synchronized queue that provides a condition variable.
That condition variable signals when data is added to the queue.
I have 5 threads:
Thread.new do
loop do
#queue.synchronize {
cond.wait_until { #queue.has_data? || #queue.finished? }
}
# some processing code that can also call #queue.enqueue
end
end
Then I do:
#queue.enqueue some_data
#threads.each(&:join)
MyQueue#enqueue looks like this:
def enqueue(data)
synchronize do
#pending << v unless queued?(data) || processed?(data) || processing?(data)
data_cond.signal
end
end
def finished?
#started && #processing.empty? && #pending.empty?
end
def has_data?
!#pending.empty?
end
And I get on #join
deadlock detected
How exactly does this cause a deadlock and how would one fix it?
I wonder if this is a problem that all of the threads are blocked on the same condition variable, and there isnt a thread available to enqueue data, which would release the other threads.
Based on the comment in this code:
Thread.new do
loop do
#queue.synchronize {
cond.wait_until { #queue.has_data? || #queue.finished? }
}
# some processing code that can also call #queue.enqueue
end
end
Your comment that mentions "some processing code that can also call #queue.enqueue", is this the only place where #queue.enqueue is called? If so, then all of the threads will be blocked on the condition variable and none will be able to get to the point to be able to call enqueue. Im sure Ruby can detect that all threads are locked on the same entity and none are available to release it, thus deadlock.
If you do indeed have a separate thread that only enqueues (which would be a typical producer/consumer situation) make sure that it doesnt also wait on the condition variable, which could also cause deadlock.
It's a little hard to help you because you are only posting code fragments...
You should try the work_queue gem, or at least take a look at the source code.
There is no need to wait for has_data? || finished? in synchronize block. The code should look like:
Thread.new do
loop do
cond.wait_until { #queue.has_data? || #queue.finished? }
enq = nil
#queue.synchronize {
enq = #queue.pop
}
# some processing code that can also call #queue.enqueue
end
end
In that case you lock other threads only when operating with queue content. What you need to do is to synchronize on queue state change, like finished
A better solution is to wrap all thread critical variables with mutex, like here in rails. It'll make code a little bit slower since it eliminate simultaneous variable access.

Resources