e-threadpool 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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