Search code examples
rubymultithreadingwatir

Controlling the same browser instance with 2 threads WATIR RUBY


I want my program to be constantly checking if an element is presenting (always checking for it), however when I try to check the HTML in another different thread I get read_nonblock error and/or a stream closed error ( couldn't find explanation in the internet) so I created this program to verify wtf is happening.

require 'watir'
require 'thread'

profile = Selenium::WebDriver::Firefox::Profile.new
profile['permissions.default.image'] = 2
profile['media.autoplay.enabled'] = false


browser = Watir::Browser.new :firefox, profile: profile
threads = Array.new
threads[0] = Thread.new do
  loop do
    begin
      sleep(3)
      puts browser.url
      browser.goto("https://monip.org")
    rescue
      puts "thread 1 encountered an error"
    end
  end
end
threads[1] = Thread.new do
  loop do
    begin
      if browser.url == "https://monip.org"
        puts "hi"
      end
    rescue
      puts "thread 2 encountered an error"
    end
  end
end

threads.each do |e|
  e.join
end

I'd get

thread 1 encountered an error
thread 2 encountered an error
thread 2 encountered an error
thread 1 encountered an error
thread 1 encountered an error
about:blank
thread 2 encountered an error
thread 1 encountered an error
thread 1 encountered an error
thread 1 encountered an error
thread 2 encountered an error
thread 1 encountered an error
thread 2 encountered an error

of course any other solution would be nice, this is what I though of but couldn't write it with code .. Is it possible to make the program wait/check if it can control/communicate with the browser for a certain set of seconds. or reserve the browser for itself for a period of time I'd use "sleep". or allow the 2 threads to control the browser at once.


EDIT: I was able to get the desired behaviour by using "mutex"

this is how the code would look

require 'watir'
require 'thread'

profile = Selenium::WebDriver::Firefox::Profile.new
profile['permissions.default.image'] = 2
profile['media.autoplay.enabled'] = false


mutex = Mutex.new
browser = Watir::Browser.new :firefox, profile: profile
threads = Array.new
threads[0] = Thread.new do
  loop do
  sleep(0.1)
    begin
      mutex.synchronize do
        puts "#{Time.now.ctime}  #{browser.url}"
        browser.goto("https://monip.org")
      end
    rescue
      puts "#{Time.now.ctime}  thread 1 encountered an error"
    end
  end
end

threads[1] = Thread.new do
  loop do
  sleep(0.1)
    begin
      mutex.synchronize do
        if browser.url == "https://monip.org/" || browser.url == "https://monip.org"
          puts "#{Time.now.ctime}  hi"
        end
      end
    rescue
      puts "#{Time.now.ctime}  thread 2 encountered an error"
    end
  end
end
threads[0].join
threads[1].join

the output is

Fri Jan  5 13:49:36 2018  about:blank
Fri Jan  5 13:49:37 2018  hi
Fri Jan  5 13:49:37 2018  https://monip.org/
Fri Jan  5 13:49:37 2018  hi
Fri Jan  5 13:49:37 2018  https://monip.org/
Fri Jan  5 13:49:37 2018  hi
Fri Jan  5 13:49:37 2018  https://monip.org/
Fri Jan  5 13:49:38 2018  hi
Fri Jan  5 13:49:38 2018  https://monip.org/
Fri Jan  5 13:49:38 2018  hi
Fri Jan  5 13:49:38 2018  https://monip.org/
Fri Jan  5 13:49:38 2018  hi
Fri Jan  5 13:49:38 2018  https://monip.org/
Fri Jan  5 13:49:38 2018  hi
Fri Jan  5 13:49:38 2018  https://monip.org/
Fri Jan  5 13:49:39 2018  hi
Fri Jan  5 13:49:39 2018  https://monip.org/
Fri Jan  5 13:49:39 2018  hi
Fri Jan  5 13:49:39 2018  https://monip.org/
Fri Jan  5 13:49:39 2018  hi
Fri Jan  5 13:49:39 2018  https://monip.org/
Fri Jan  5 13:49:40 2018  hi
Fri Jan  5 13:49:40 2018  https://monip.org/
Fri Jan  5 13:49:40 2018  hi
Fri Jan  5 13:49:40 2018  https://monip.org/

as you can see in the timestamps, we are checking constantly and respectively without raising any error


Solution

  • You simply can't let them both use the browser at the same time.

    Problem 1: Your script will never output "hi" because the url is actually "https://monip.org/", not "https://monip.org".

    Problem 2: You may want to be "constantly" checking for this element (I assume some sort of dialog or something), because once it appears, you'll need to do something. But by checking constantly, you are essentially ensuring that the thread that is actually doing things is going to be interrupted. Most likely checking once every second (or even 2-10 seconds) will be more than enough.

    Problem 3: As I stated at the beginning, you can't let them use the browser at the same time. You'll need to implement some sort of locking procedure to prevent this behavior.