e-threadpool 1.0.0

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Dale Ma
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Threadpool
2
+ Threadpool is the "threadpool pattern" implemented in ruby. It's a compact but robust component.
3
+
4
+ ## Features
5
+ - Timeout jobs, it means the stuck threads will be recycled.
6
+ - Job based tasks, job is an object not a block, could be more flexible.
7
+ - MRI and JRuby are supported.
8
+
9
+ ## Install
10
+ gem install e-threadpool
11
+ or edit Gemfile
12
+ ...
13
+ gem 'e-threadpool'
14
+ ...
15
+
16
+
17
+ ## Example
18
+ require 'threadpool'
19
+
20
+ class TestJob < Job
21
+ def run
22
+ puts "hello"
23
+ end
24
+ end
25
+
26
+ threadpool = Threadpool::Threadpool.new
27
+ 100.times.each do
28
+ # threadpool auto-executes the job after loaded
29
+ threadpool.load(TestJob.new)
30
+ end
31
+
32
+ # shutdown will wait until all jobs are finished
33
+ threadpool.shutdown
34
+
35
+ ## LICENSE
36
+ MIT LICENSE, please refer to the LICENSE file.
data/lib/threadpool.rb ADDED
@@ -0,0 +1,5 @@
1
+ $: << File.dirname(__FILE__)
2
+
3
+ require 'threadpool/worker'
4
+ require 'threadpool/core'
5
+ require 'threadpool/job'
@@ -0,0 +1,94 @@
1
+ require "monitor"
2
+
3
+ module Threadpool
4
+
5
+ class Threadpool
6
+ INIT_WORKERS = 10
7
+ MAX_WORKERS = 20
8
+ TIMEOUT_SECS = 30
9
+
10
+ def initialize(*args)
11
+ extend MonitorMixin
12
+ @init_workers = args.shift || INIT_WORKERS
13
+ @max_workers = args.shift || MAX_WORKERS
14
+ @timeout_secs = args.shift || TIMEOUT_SECS
15
+ if @max_workers < @init_workers
16
+ raise "max_workers cannot smaller than init_workers"
17
+ end
18
+
19
+ @workers = (0...@init_workers).map { Threadpool::Worker.new }
20
+ Thread.new { process }
21
+ end
22
+
23
+ def load(job)
24
+ if @teminate
25
+ puts "job: #{job.jobid} cannot be loaded, the pool is terminating."
26
+ return
27
+ end
28
+
29
+ worker = nil
30
+ loop do
31
+ worker = idle_worker
32
+ worker.nil? ? create_worker : break
33
+ sleep(0.001)
34
+ end
35
+
36
+ worker.job = job
37
+ end
38
+
39
+ def shutdown
40
+ @teminate = true
41
+
42
+ loop do
43
+ return if busy_workers.count == 0
44
+ sleep(0.001)
45
+ end
46
+ end
47
+
48
+ private
49
+ def process
50
+ loop do
51
+ synchronize do
52
+ @workers.each do |w|
53
+
54
+ if w.loaded?
55
+ w.process
56
+
57
+ if Time.now - w.start_time > @timeout_secs
58
+ puts "job is timeout."
59
+ w.cancel
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ sleep(0.001)
66
+
67
+ if @teminate && busy_workers.count == 0
68
+ return
69
+ end
70
+ end
71
+ end
72
+
73
+ def idle_worker
74
+ synchronize do
75
+ @workers.each do |worker|
76
+ return worker if !worker.loaded?
77
+ end
78
+ end
79
+
80
+ nil
81
+ end
82
+
83
+ def busy_workers
84
+ @workers.select { |w| w.loaded? }
85
+ end
86
+
87
+ def create_worker
88
+ synchronize do
89
+ @workers << Worker.new if @workers.count < @max_workers
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,20 @@
1
+ module Threadpool
2
+
3
+ class Job
4
+ attr_reader :jobid
5
+
6
+ def initialize(*args)
7
+ @args = args
8
+ @jobid = (0...16).map { ('0'..'9').to_a[rand(9)] }.join
9
+ end
10
+
11
+ def run
12
+ p "Running job: #{@jobid}"
13
+ end
14
+
15
+ def cancel
16
+ Thread.current.exit
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ module Threadpool
2
+
3
+ class Worker
4
+ attr_reader :start_time
5
+
6
+ def loaded?
7
+ !@job.nil?
8
+ end
9
+
10
+ def job=(job)
11
+ @job = job
12
+ end
13
+
14
+ def process
15
+ return if @start_time
16
+ @start_time = Time.now
17
+ @thread = Thread.new {
18
+ @job.run
19
+ reset
20
+ }
21
+ end
22
+
23
+ def cancel
24
+ @thread.exit
25
+ reset
26
+ end
27
+
28
+ def jobid
29
+ @job.jobid if @job
30
+ end
31
+
32
+ private
33
+ def reset
34
+ @job = nil
35
+ @start_time = nil
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../lib/threadpool'
2
+ require 'test/unit'
3
+
4
+ include Threadpool
5
+
6
+ class TestJob < Job
7
+ # NOTE: Mutext synchronize is a must, or the result might be unpredictable.
8
+ @@m = Mutex.new
9
+
10
+ def run
11
+ @@m.synchronize do
12
+ @args[0][0] += 1
13
+ end
14
+ end
15
+ end
16
+
17
+ class TestThreadpool < Test::Unit::TestCase
18
+
19
+ def test_sync
20
+ # arguments are init_workers, max_workers, timeout_secs. [OPTIONAL]
21
+ threadpool = Threadpool::Threadpool.new(8, 22, 5)
22
+
23
+ sum = [0]
24
+ 5000.times.each do
25
+ threadpool.load(TestJob.new(sum))
26
+ end
27
+
28
+ threadpool.shutdown
29
+ assert_equal(5000, sum[0])
30
+
31
+ # after shutdown, threadpool should not accept any jobs
32
+ threadpool.load(TestJob.new(sum))
33
+ assert_equal(5000, sum[0])
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: e-threadpool
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dale Ma
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-21 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: dalema22@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/threadpool/core.rb
21
+ - lib/threadpool/job.rb
22
+ - lib/threadpool/worker.rb
23
+ - lib/threadpool.rb
24
+ - LICENSE
25
+ - README.md
26
+ - test/threadpool_test.rb
27
+ homepage: http://github.com/eguitarz/threadpool
28
+ licenses: []
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubyforge_project:
47
+ rubygems_version: 1.8.19
48
+ signing_key:
49
+ specification_version: 3
50
+ summary: Ruby based thread pool
51
+ test_files:
52
+ - test/threadpool_test.rb