Skip to content

Instantly share code, notes, and snippets.

@kopylovvlad
Last active October 12, 2018 21:42
Show Gist options
  • Save kopylovvlad/22700acbc79a33ba6c84bf102a455671 to your computer and use it in GitHub Desktop.
Save kopylovvlad/22700acbc79a33ba6c84bf102a455671 to your computer and use it in GitHub Desktop.
require 'time'
# thread pool class
# it takes pool size
class ThreadPool
attr_reader :result, :number, :size
def initialize(size)
@finish = false
@result = nil
@number = nil
@size = size
@jobs = Queue.new
@pool = init_pool
end
def finish?
@finish
end
def finish!(result, number)
@finish = true
@result = result
@number = number
@jobs = [[ Proc.new {}, nil]]
end
def jobs_size
@jobs.size
end
# add a job to queue
def schedule(*args, &block)
@jobs << [block, args]
end
def clear!
@jobs = []
end
# run threads and perform jobs from queue
def run!
@size.times do
schedule { throw :exit }
end
@pool.map(&:join)
end
private
def init_pool
Array.new(size) do
Thread.new do
catch(:exit) do
loop do
job, args = @jobs.pop
job.call(*args)
break if @jobs.size == 0
end
end
end
end
end
end
module HashGenerator
# calculating hash for string "#{string}#{number}"
def self.call(string, number)
# for macOS
return `echo '#{string}#{number}' | sha2 -256`
.gsub('SHA-256 ((null)) = ', '')
.gsub(/\n/, '')
# for Linux
# return `echo '#{string}#{number}' | sha256sum`.gsub(/\s\s\-$/, '')
end
end
module HashChecker
# check does hash start with corrent numbers
def self.call(hash_str, zero_count)
hash_start = hash_str[0..(zero_count - 1)]
hash_start == ('0' * zero_count)
end
end
module TimeSplitter
# prepare time format for output
def self.call(time_start, time_end)
seconds = ((time_end - time_start)).round
minutes = ((time_end - time_start) / 60).round
[minutes, seconds]
end
end
class MainMachine
attr_reader :main_string, :zero_count, :thread_count, :query_limit
def initialize(main_string, zero_count, thread_count)
@main_string = main_string
@zero_count = zero_count
@thread_count = thread_count
@query_limit = 1_000_000
@pool = ThreadPool.new(thread_count)
end
def perform
time_start = Time.now
# step 1: fill query from 1 to 1_000_000
fill_queue
# step 2: run all jobs
run_jobs
time_end = Time.now
# output result
if @pool.finish?
minutes, seconds = TimeSplitter.call(time_start, time_end)
puts "\n\rstring: '#{main_string}'"
puts "number: '#{@pool.number}'"
puts "hash: '#{@pool.result}'"
puts "time: '#{minutes}'(minutes), '#{seconds}'(seconds)"
else
puts "\n\r we did not calculate it"
puts "please, run again with new limit"
end
end
private
def fill_queue
number = 0
loop do
dublicate_number = number.to_s.dup.to_i
@pool.schedule do
# generate hash
sha_hash = HashGenerator.call(main_string, dublicate_number)
# check is hash valid
if HashChecker.call(sha_hash, zero_count) == true
@pool.finish!(sha_hash, dublicate_number)
end
end
break if number == query_limit
number += 1
end
end
def run_jobs
@pool.run!
loop do
break if @pool.finish? || @pool.jobs_size == 0
end
@pool.clear!
end
end
main_string = 'hello world!'
number_of_leading_zeros = 3
thread_count = 10
MainMachine.new(main_string, number_of_leading_zeros, thread_count).perform
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment