scbi_mapreduce 0.0.40 → 0.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/{README.rdoc → README.md} +0 -0
- data/Rakefile +8 -28
- data/lib/scbi_mapreduce.rb +2 -10
- data/lib/scbi_mapreduce/main_worker.rb +20 -6
- data/lib/scbi_mapreduce/manager.rb +4 -0
- data/lib/scbi_mapreduce/version.rb +3 -0
- data/lib/scbi_mapreduce/work_manager.rb +22 -0
- data/lib/scbi_mapreduce/worker_launcher.rb +34 -0
- data/scbi_mapreduce.gemspec +27 -0
- data/skeleton/.DS_Store +0 -0
- data/skeleton/dummy_calcs/.DS_Store +0 -0
- data/skeleton/old/dummy_calculations/README.txt +25 -0
- data/skeleton/old/dummy_calculations/lib/calculations.rb +37 -0
- data/skeleton/old/dummy_calculations/lib/thread_pool.rb +107 -0
- data/skeleton/old/dummy_calculations/main.rb +59 -0
- data/skeleton/old/dummy_calculations/my_worker.rb +56 -0
- data/skeleton/old/dummy_calculations/my_worker_manager.rb +52 -0
- data/skeleton/old/dummy_calculations/threads_implementation.rb +29 -0
- data/skeleton/old/sequences_blast/README.txt +31 -0
- data/{test/drb_test/main.rb → skeleton/old/sequences_blast/launch_only_workers.rb} +6 -10
- data/skeleton/old/sequences_blast/lib/db/mids.fasta +64 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nhr +0 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nin +0 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nog +0 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nsd +48 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nsi +0 -0
- data/skeleton/old/sequences_blast/lib/db/mids.fasta.nsq +0 -0
- data/skeleton/old/sequences_blast/lib/find_mids.rb +134 -0
- data/skeleton/old/sequences_blast/lib/thread_pool.rb +107 -0
- data/skeleton/old/sequences_blast/linear_implementation.rb +86 -0
- data/skeleton/old/sequences_blast/logs/worker0_osiris-2.local_log.txt +13 -0
- data/skeleton/old/sequences_blast/logs/worker1_osiris-2.local_log.txt +13 -0
- data/skeleton/old/sequences_blast/main.rb +63 -0
- data/skeleton/old/sequences_blast/my_worker.rb +58 -0
- data/skeleton/old/sequences_blast/my_worker_manager.rb +60 -0
- data/skeleton/old/sequences_blast/results.fastq +3996 -0
- data/skeleton/old/sequences_blast/test_threads.rb +32 -0
- data/skeleton/old/sequences_blast/threads_implementation.rb +108 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nhr +0 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nin +0 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nog +0 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nsd +120 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nsi +0 -0
- data/skeleton/remove_mids/lib/db/mids.fasta.nsq +0 -0
- data/{.gemtest → skeleton/remove_mids/results.fastq558292} +0 -0
- data/skeleton/remove_mids/results.fastq662870 +3996 -0
- data/skeleton/simple/launch_only_workers.rb +29 -0
- metadata +102 -110
- data/History.txt +0 -93
- data/Manifest.txt +0 -47
- data/PostInstall.txt +0 -7
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/test/drb_test/my_worker.rb +0 -36
- data/test/drb_test/my_worker_manager.rb +0 -41
- data/test/drb_test/scbi_drb_checkpoint +0 -1
- data/test/drb_test/scbi_mapreduce_checkpoint +0 -1
- data/test/test_helper.rb +0 -3
- data/test/test_scbi_drb.rb +0 -11
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4e90686d178555e506b72d838c6124daa917ce3f
|
4
|
+
data.tar.gz: e67e2f68ea90141d054662425c9be5fba4001ca8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4ab49d1a862ca5432744b2c1b6039dfd6de3c6b391e163cf92713f2b49ea647b2aecc6561b76ca27523fa234f515d37f4ad3aa764db7ef219e15667ce2790ab
|
7
|
+
data.tar.gz: 414bd290d851360a7b2d8e4cfd6439d4aa7b46e3eb06798f856f6265992dd94186f69ddbed6dc3faa4b758d821aa35752b1b86fad45b32ea29fd28830ac74fbf
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 dariogf
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/{README.rdoc → README.md}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -1,28 +1,8 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# Hoe.plugin :cucumberfeatures
|
10
|
-
|
11
|
-
# Generate all the Rake tasks
|
12
|
-
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
-
$hoe = Hoe.spec 'scbi_mapreduce' do
|
14
|
-
self.developer 'Dario Guerrero', 'dariogf@gmail.com'
|
15
|
-
self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
|
16
|
-
self.rubyforge_name = self.name # TODO this is default value
|
17
|
-
# self.extra_deps = [['activesupport','>= 2.0.2']]
|
18
|
-
self.extra_deps = [['eventmachine','>= 0.12.0'],['json','>= 0']]
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
require 'newgem/tasks'
|
24
|
-
Dir['tasks/**/*.rake'].each { |t| load t }
|
25
|
-
|
26
|
-
# TODO - want other tests/tasks run by default? Add them to the list
|
27
|
-
# remove_task :default
|
28
|
-
# task :default => [:spec, :features]
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << 'test'
|
7
|
+
t.pattern = "test/*_test.rb"
|
8
|
+
end
|
data/lib/scbi_mapreduce.rb
CHANGED
@@ -1,15 +1,7 @@
|
|
1
|
-
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
-
|
4
|
-
# $: << File.join(File.dirname(__FILE__),File.basename(__FILE__,File.extname(__FILE__)))
|
5
|
-
|
6
|
-
$: << File.expand_path('scbi_mapreduce')
|
7
|
-
# puts $:
|
1
|
+
require "scbi_mapreduce/version"
|
8
2
|
|
9
3
|
module ScbiMapreduce
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
# Your code goes here...
|
13
5
|
end
|
14
6
|
|
15
7
|
class Time
|
@@ -35,17 +35,31 @@ ip = ARGV[1]
|
|
35
35
|
port = ARGV[2].to_i
|
36
36
|
custom_worker_file = ARGV[3]
|
37
37
|
|
38
|
-
|
38
|
+
using_slurm=false
|
39
39
|
|
40
|
-
|
40
|
+
if worker_id.upcase == 'AUTO'
|
41
|
+
worker_id = ENV['SLURM_PROCID']
|
42
|
+
using_slurm=true
|
43
|
+
end
|
44
|
+
|
45
|
+
if worker_id.to_i == 0 && using_slurm
|
46
|
+
puts "Launching worker with: worker_id:#{worker_id}, ip:#{ip}, port:#{port}, worker_file:#{custom_worker_file}"
|
47
|
+
puts "Ignoring first worker in manager node worker_id:#{worker_id}"
|
48
|
+
else
|
49
|
+
|
50
|
+
puts "Launching worker with: worker_id:#{worker_id}, ip:#{ip}, port:#{port}, worker_file:#{custom_worker_file}"
|
41
51
|
|
42
|
-
|
52
|
+
#$: << File.expand_path(File.dirname(custom_worker_file))
|
43
53
|
|
44
|
-
|
54
|
+
require custom_worker_file
|
45
55
|
|
46
|
-
|
56
|
+
klass_name = File.basename(custom_worker_file,File.extname(custom_worker_file)).camelize
|
47
57
|
|
48
|
-
worker_class.
|
58
|
+
worker_class = Object.const_get(klass_name)
|
59
|
+
|
60
|
+
worker_class.start_worker(worker_id,ip,port)
|
61
|
+
|
62
|
+
end
|
49
63
|
|
50
64
|
puts "FINISH WORKER"
|
51
65
|
|
@@ -96,12 +96,16 @@ module ScbiMapreduce
|
|
96
96
|
|
97
97
|
end
|
98
98
|
|
99
|
+
|
100
|
+
|
101
|
+
|
99
102
|
# Start a EventMachine loop acting as a server for incoming workers connections
|
100
103
|
def start_server
|
101
104
|
|
102
105
|
# set a custom error handler, otherwise errors are silently ignored when they occurs inside a callback.
|
103
106
|
EM.error_handler{ |e|
|
104
107
|
$SERVER_LOG.error(e.message + ' => ' + e.backtrace.join("\n"))
|
108
|
+
@work_manager_class.global_error_received(e)
|
105
109
|
}
|
106
110
|
|
107
111
|
# $SERVER_LOG.info("Installing INT and TERM traps in #{@work_manager_class}")
|
@@ -148,6 +148,14 @@ module ScbiMapreduce
|
|
148
148
|
|
149
149
|
end
|
150
150
|
|
151
|
+
def self.work_manager_finished
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.global_error_received(error_exception)
|
156
|
+
|
157
|
+
end
|
158
|
+
|
151
159
|
def next_work
|
152
160
|
|
153
161
|
end
|
@@ -385,6 +393,7 @@ module ScbiMapreduce
|
|
385
393
|
|
386
394
|
t=Time.now_us
|
387
395
|
|
396
|
+
begin
|
388
397
|
# prepare new data
|
389
398
|
@@chunk_size.times do
|
390
399
|
obj=next_work
|
@@ -395,6 +404,13 @@ module ScbiMapreduce
|
|
395
404
|
objs << obj
|
396
405
|
end
|
397
406
|
end
|
407
|
+
rescue Exception => e
|
408
|
+
$SERVER_LOG.error("Exception creating next_work. Worker, quit!")
|
409
|
+
send_object(:sleep)
|
410
|
+
self.class.global_error_received(e)
|
411
|
+
|
412
|
+
#raise e
|
413
|
+
end
|
398
414
|
|
399
415
|
@@total_read_time+=(Time.now_us - t)
|
400
416
|
|
@@ -648,8 +664,10 @@ module ScbiMapreduce
|
|
648
664
|
EM.stop
|
649
665
|
$SERVER_LOG.info "Exiting server"
|
650
666
|
|
667
|
+
|
651
668
|
self.class.end_work_manager
|
652
669
|
|
670
|
+
|
653
671
|
@@total_seconds = (Time.now_us-@@total_seconds)
|
654
672
|
@@total_manager_time= @@total_manager_time
|
655
673
|
|
@@ -679,6 +697,8 @@ module ScbiMapreduce
|
|
679
697
|
@@stats[:connected_workers]=@@max_workers
|
680
698
|
@@stats[:each_transmission_time]=@@each_transmission_time
|
681
699
|
@@stats[:each_worker_time]=@@each_worker_time
|
700
|
+
|
701
|
+
|
682
702
|
|
683
703
|
|
684
704
|
$SERVER_LOG.info "Total processed: #{@@count} objects in #{@@total_seconds} seconds"
|
@@ -703,6 +723,8 @@ module ScbiMapreduce
|
|
703
723
|
$SERVER_LOG.info "Chunk size: #{@@chunk_size}"
|
704
724
|
$SERVER_LOG.info "Total connected workers: #{@@max_workers}"
|
705
725
|
|
726
|
+
self.class.work_manager_finished
|
727
|
+
|
706
728
|
end
|
707
729
|
|
708
730
|
end
|
@@ -40,6 +40,34 @@ module ScbiMapreduce
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def launch_workers
|
43
|
+
|
44
|
+
if system("which srun > /dev/null 2>&1") && (!ENV['SLURM_PROCID'].to_s.empty?)
|
45
|
+
$LAUNCHER_LOG.info "SLURM DETECTED"
|
46
|
+
$LAUNCHER_LOG.info "Launching #{@workers} workers via srun"
|
47
|
+
launch_workers_srun
|
48
|
+
else
|
49
|
+
$LAUNCHER_LOG.info "Launching #{@workers} workers via SSH"
|
50
|
+
launch_workers_ssh
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def launch_workers_srun
|
56
|
+
# TODO - si aqui falla algo, no peta, se bloquea
|
57
|
+
$LAUNCHER_LOG.info "Launching #{@workers} srun workers"
|
58
|
+
|
59
|
+
pid=fork{
|
60
|
+
$LAUNCHER_LOG.info "Connecting #{@workers} srun workers to #{@server_ip}:#{@server_port}"
|
61
|
+
cmd = "srun #{File.join(File.dirname(__FILE__),'main_worker.rb')} auto #{server_ip} #{server_port} #{@worker_file}"
|
62
|
+
$LAUNCHER_LOG.info cmd
|
63
|
+
exec(cmd)
|
64
|
+
}
|
65
|
+
|
66
|
+
$LAUNCHER_LOG.info "All workers launched"
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def launch_workers_ssh
|
43
71
|
# TODO - si aqui falla algo, no peta, se bloquea
|
44
72
|
$LAUNCHER_LOG.info "Launching #{@workers} local workers"
|
45
73
|
if @workers > 0
|
@@ -111,6 +139,12 @@ module ScbiMapreduce
|
|
111
139
|
|
112
140
|
|
113
141
|
def launch_external_workers(workers)
|
142
|
+
|
143
|
+
#skip if slurm detected
|
144
|
+
if system("which srun > /dev/null 2>&1")
|
145
|
+
return
|
146
|
+
end
|
147
|
+
|
114
148
|
puts "Launching #{workers.count} external workers: #{workers}"
|
115
149
|
puts "INIT_ENV_FILE: #{@init_env_file}"
|
116
150
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'scbi_mapreduce/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "scbi_mapreduce"
|
8
|
+
spec.version = ScbiMapreduce::VERSION
|
9
|
+
spec.authors = ["dariogf"]
|
10
|
+
spec.email = ["dariogf@gmail.com"]
|
11
|
+
spec.summary = %q{scbi_mapreduce brings parallel and distributed computing capabilities to your code.}
|
12
|
+
spec.description = %q{scbi_mapreduce brings parallel and distributed computing capabilities to your code, with a very easy to use framework that allows you to exploit your clustered or cloud computational resources.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'eventmachine','>=0.12.0'
|
25
|
+
spec.add_runtime_dependency 'json','>=0'
|
26
|
+
|
27
|
+
end
|
data/skeleton/.DS_Store
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,25 @@
|
|
1
|
+
Comparison of workers with scbi_mapreduce vs ruby-threads
|
2
|
+
=========================================================
|
3
|
+
|
4
|
+
This application is only useful for testing. You can modify the files
|
5
|
+
to perform other tasks. There are other templates available, you
|
6
|
+
can list them by issuing this command:
|
7
|
+
|
8
|
+
scbi_mapreduce
|
9
|
+
|
10
|
+
You can launch the tests application right now with the following command:
|
11
|
+
|
12
|
+
time ruby main.rb
|
13
|
+
|
14
|
+
|
15
|
+
This launches 4 workers that do some simple calculations (only to keep busy
|
16
|
+
the processor), to demonstrate the gain speed agains threads. 4 workers are
|
17
|
+
used for a quad-core processor. Adjust it accordingly to your processor cores.
|
18
|
+
|
19
|
+
|
20
|
+
To launch the threaded version of the application, you can do:
|
21
|
+
|
22
|
+
time ruby threads_implementation.rb
|
23
|
+
|
24
|
+
You can compare the two times obtained. Threaded version will last the same with 1 thread or with 100.
|
25
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Calculations
|
2
|
+
|
3
|
+
|
4
|
+
def do_dummy_calculations
|
5
|
+
t=Time.now
|
6
|
+
x=0
|
7
|
+
20000000.times do |i|
|
8
|
+
x+=1
|
9
|
+
end
|
10
|
+
puts Time.now-t
|
11
|
+
end
|
12
|
+
|
13
|
+
def do_dummy_calculations2
|
14
|
+
numer_of_calcs=250000
|
15
|
+
|
16
|
+
# t=Time.now
|
17
|
+
|
18
|
+
x1=1
|
19
|
+
x2=1
|
20
|
+
|
21
|
+
# do a loop with calculations
|
22
|
+
numer_of_calcs.times do |i|
|
23
|
+
x=x1+x2
|
24
|
+
|
25
|
+
x1=x2
|
26
|
+
x2=x
|
27
|
+
|
28
|
+
# puts some info at regular intervals
|
29
|
+
# if (i % 100000)==0
|
30
|
+
# puts "Calculated #{i}"
|
31
|
+
# end
|
32
|
+
end
|
33
|
+
# puts Time.now-t
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "thread.rb"
|
2
|
+
|
3
|
+
######################################
|
4
|
+
# This class creates a thread's pool
|
5
|
+
######################################
|
6
|
+
|
7
|
+
class ThreadPool
|
8
|
+
class Worker
|
9
|
+
@@count=0
|
10
|
+
def initialize
|
11
|
+
|
12
|
+
@identifier = @@count
|
13
|
+
@@count+=1
|
14
|
+
|
15
|
+
Thread.abort_on_exception = true
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@thread = Thread.new do
|
18
|
+
while true
|
19
|
+
sleep 0.001
|
20
|
+
block = get_block
|
21
|
+
if block
|
22
|
+
begin
|
23
|
+
block.call
|
24
|
+
rescue Exception => e
|
25
|
+
puts "In thread: " + e.message
|
26
|
+
raise e
|
27
|
+
end
|
28
|
+
|
29
|
+
reset_block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_block
|
36
|
+
@mutex.synchronize {@block}
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_block(block)
|
40
|
+
# puts "set block #{@identifier}"
|
41
|
+
@mutex.synchronize do
|
42
|
+
raise RuntimeError, "Thread already busy." if @block
|
43
|
+
@block = block
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def reset_block
|
48
|
+
@mutex.synchronize {@block = nil}
|
49
|
+
end
|
50
|
+
|
51
|
+
def busy?
|
52
|
+
@mutex.synchronize {!@block.nil?}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
attr_accessor :max_size
|
57
|
+
attr_reader :workers
|
58
|
+
|
59
|
+
# Defines the max number of threads that will be able to exist
|
60
|
+
def initialize(max_size = 10)
|
61
|
+
@max_size = max_size
|
62
|
+
@workers = []
|
63
|
+
@mutex = Mutex.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def size
|
67
|
+
@mutex.synchronize {@workers.size}
|
68
|
+
end
|
69
|
+
|
70
|
+
def busy?
|
71
|
+
@mutex.synchronize {@workers.any? {|w| w.busy?}}
|
72
|
+
end
|
73
|
+
|
74
|
+
#Allows that main program doesn't finish until the thread have been executed
|
75
|
+
def join
|
76
|
+
sleep 0.01 while busy?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Begin the block's processing. After using this method, will call to "join"
|
80
|
+
def process(&block)
|
81
|
+
wait_for_worker.set_block(block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def wait_for_worker
|
85
|
+
while true
|
86
|
+
worker = find_available_worker
|
87
|
+
return worker if worker
|
88
|
+
sleep 0.01
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_available_worker
|
93
|
+
@mutex.synchronize {free_worker || create_worker}
|
94
|
+
end
|
95
|
+
|
96
|
+
def free_worker
|
97
|
+
@workers.each {|w| return w unless w.busy?}; nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_worker
|
101
|
+
return nil if @workers.size >= @max_size
|
102
|
+
worker = Worker.new
|
103
|
+
@workers << worker
|
104
|
+
worker
|
105
|
+
end
|
106
|
+
private :wait_for_worker , :find_available_worker , :free_worker , :create_worker
|
107
|
+
end
|