threadify_procs 0.0.1
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 +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +28 -0
- data/Rakefile +1 -0
- data/lib/threadify_procs/version.rb +3 -0
- data/lib/threadify_procs.rb +89 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/threadify_procs_spec.rb +72 -0
- data/threadify_procs.gemspec +22 -0
- metadata +100 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 68ea99f978e760abe63d79a225b5a62ba609d58a
|
4
|
+
data.tar.gz: 65b85ee7bbe065f5ef827c76fd44c93886237a6e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f4780d7d718692e686c93e508dec9d0bdad3c7b775f5d912b82b4a38e068b07187f9d819d05f29dbf88b2d0d554968de0df4775db6ed5889140893efd8f64eb5
|
7
|
+
data.tar.gz: 84b1e44be3f747f6ddb22676d77456481878d93f68b33a1f8d3e8f907a713e61c7ae1c4a36881275c01765bb0db9a699e31f76d1679476953ecd10197e3e900f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Antoine Qu'hen
|
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.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# ThreadifyProcs
|
2
|
+
|
3
|
+
Create an array of Procs, launch them within threads. It adresses the common
|
4
|
+
problem of writting files concurrently in threads with a ruby proces or
|
5
|
+
downloading simultaneously multiple files. It avoids 'Too many open files'
|
6
|
+
errors.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
require 'threadify_procs'
|
11
|
+
procs = [
|
12
|
+
Proc.new { puts 1 },
|
13
|
+
Proc.new { puts 2 }
|
14
|
+
]
|
15
|
+
call_with_threads procs, number_of_threads: 50
|
16
|
+
|
17
|
+
An other option is available :with_writer (boolean). Its goal is to launch
|
18
|
+
another thread which responsibility is to create files on disc.
|
19
|
+
|
20
|
+
require 'threadify_procs'
|
21
|
+
procs = []
|
22
|
+
10_000.times do |n|
|
23
|
+
Proc.new do
|
24
|
+
@files_to_write << [
|
25
|
+
"#{Rails.root}/tmp/#{n}.txt", SecureRandom.uuid]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
launch_in_threads procs, number_of_threads: 50, with_writer: true
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "threadify_procs/version"
|
2
|
+
|
3
|
+
module ThreadifyProcs
|
4
|
+
|
5
|
+
def call_with_threads procs, options={}
|
6
|
+
set_procs procs
|
7
|
+
set_options options
|
8
|
+
with_writer_thread do
|
9
|
+
launch_procs
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def launch_writer_thread
|
16
|
+
@writer_thread = Thread.new do
|
17
|
+
begin
|
18
|
+
while true do
|
19
|
+
unless @files_to_write.empty?
|
20
|
+
data = @files_to_write.shift
|
21
|
+
filename, string = data
|
22
|
+
File.open(filename, 'w') do |file|
|
23
|
+
file.binmode
|
24
|
+
file.write string
|
25
|
+
end
|
26
|
+
else
|
27
|
+
break if ready_to_kill_writer_thread?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue => exception
|
31
|
+
Thread.main.raise exception
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def kill_writer_thread
|
37
|
+
@kill_writer_thread = true
|
38
|
+
@writer_thread.join
|
39
|
+
end
|
40
|
+
|
41
|
+
def launch_procs
|
42
|
+
@threads = [].tap do |threads|
|
43
|
+
groups_of_procs.each do |procs|
|
44
|
+
threads << Thread.new do
|
45
|
+
procs.each(&:call)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@threads.each(&:join)
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_procs procs
|
53
|
+
@procs = procs
|
54
|
+
unless procs.kind_of?(Array) && procs.all?{|_proc|_proc.kind_of?(Proc)}
|
55
|
+
raise 'ThreadifyProcs expects an array of procs.'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def set_options options
|
60
|
+
@number_of_threads = options[:number_of_threads].to_i || 25
|
61
|
+
if @number_of_threads < 1
|
62
|
+
raise 'ThreadifyProcs expects a positive number of Threads.'
|
63
|
+
end
|
64
|
+
@procs_per_thread = (procs.size / @number_of_threads).ceil
|
65
|
+
@with_writer = !!options[:with_writer]
|
66
|
+
@files_to_write = [] if @with_writer
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_writer_thread
|
70
|
+
begin
|
71
|
+
launch_writer_thread if @with_writer
|
72
|
+
yield
|
73
|
+
ensure
|
74
|
+
kill_writer_thread if @with_writer
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def groups_of_procs
|
79
|
+
[].tap do |groups|
|
80
|
+
while !@procs.empty? do
|
81
|
+
groups << @procs.shift(@procs_per_thread)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def ready_to_kill_writer_thread?
|
87
|
+
@kill_writer_thread && @files_to_write.empty? && @threads.all?(&:stop?)
|
88
|
+
end
|
89
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class ThreadifiedJob
|
5
|
+
include ThreadifyProcs
|
6
|
+
attr_reader :total
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@total = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def procs
|
13
|
+
[].tap do |_procs|
|
14
|
+
3.times do |n|
|
15
|
+
_procs << Proc.new do
|
16
|
+
@total += n+1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def launch
|
23
|
+
call_with_threads procs, number_of_threads: 2
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ThreadifiedJobWithWriter < ThreadifiedJob
|
28
|
+
|
29
|
+
def procs
|
30
|
+
[].tap do |_procs|
|
31
|
+
3.times do |n|
|
32
|
+
_procs << Proc.new do
|
33
|
+
@files_to_write << ["#{File.dirname(__FILE__)}/../tmp/#{n}.txt", n]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def launch
|
40
|
+
@files_to_write = []
|
41
|
+
call_with_threads procs, number_of_threads: 3, with_writer: true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ThreadifyProcs do
|
46
|
+
let(:tmp_dir) { "#{File.dirname(__FILE__)}/../tmp" }
|
47
|
+
|
48
|
+
describe 'call_with_threads' do
|
49
|
+
subject{ ThreadifiedJob.new }
|
50
|
+
|
51
|
+
it 'should create threads from procs' do
|
52
|
+
subject.launch
|
53
|
+
expect(subject.total).to eq 6
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'with_writer' do
|
57
|
+
subject{ ThreadifiedJobWithWriter.new }
|
58
|
+
before { FileUtils.mkdir tmp_dir}
|
59
|
+
after { FileUtils.rm_r tmp_dir}
|
60
|
+
|
61
|
+
it 'should create threads from procs' do
|
62
|
+
subject.launch
|
63
|
+
|
64
|
+
3.times do |n|
|
65
|
+
path = "#{tmp_dir}/#{n}.txt"
|
66
|
+
expect File.exists?(path)
|
67
|
+
expect(File.read(path)).to eq n.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'threadify_procs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'threadify_procs'
|
8
|
+
spec.version = ThreadifyProcs::VERSION
|
9
|
+
spec.authors = ["Antoine Qu'hen"]
|
10
|
+
spec.email = ["antoinequhen@gmail.com"]
|
11
|
+
spec.summary = %q{Launch an array of Procs within threads.}
|
12
|
+
spec.license = 'MIT'
|
13
|
+
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
20
|
+
spec.add_development_dependency "rake"
|
21
|
+
spec.add_development_dependency "rspec"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: threadify_procs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Antoine Qu'hen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- antoinequhen@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- .gitignore
|
63
|
+
- .travis.yml
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- lib/threadify_procs.rb
|
69
|
+
- lib/threadify_procs/version.rb
|
70
|
+
- spec/spec_helper.rb
|
71
|
+
- spec/threadify_procs_spec.rb
|
72
|
+
- threadify_procs.gemspec
|
73
|
+
homepage:
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.4.1
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Launch an array of Procs within threads.
|
97
|
+
test_files:
|
98
|
+
- spec/spec_helper.rb
|
99
|
+
- spec/threadify_procs_spec.rb
|
100
|
+
has_rdoc:
|