require "benchmark/ips"
require "bcrypt"

module Enumerable
  def first_to_finish
    threads = collect { |args| Thread.new { yield(args) } }
    loop until done = threads.detect { |t| !t.alive? }
    threads.each(&:kill)
    done.value
  end

  def first_to_finish_with_queue
    queue = Queue.new
    threads = collect { |args| Thread.new { queue << yield(args) } }
    result = queue.pop
    threads.each(&:kill)
    result
  end

  def get_first_result_async
    result = nil
    threads = map do |args|
      Thread.new do
        if current_result = yield(args)
          result = current_result
          (threads - [Thread.current]).each(&:kill) # kill siblings
        end
      end
    end
    threads.each(&:join)
    result
  end
end

COSTS = (10..15).to_a.reverse

def sferik
  COSTS.first_to_finish { |cost| BCrypt::Password.create("secret", :cost => cost) }
end

def choonkeat
  COSTS.first_to_finish_with_queue { |cost| BCrypt::Password.create("secret", :cost => cost) }
end

def juanito
  COSTS.get_first_result_async { |cost| BCrypt::Password.create("secret", :cost => cost) }
end

Benchmark.ips do |x|
  x.report("@sferik") { sferik }
  x.report("@choonkeat") { choonkeat }
  x.report("@JuanitoFatas") { juanito }
  x.compare!
end