thread_tools 0.27 → 0.28

Sign up to get free protection for your applications and to get access to all the features.
@@ -31,6 +31,11 @@ require 'thread'
31
31
  module ThreadTools
32
32
 
33
33
  class EMutexOrder < Exception
34
+ attr_reader :mutex
35
+ def initialize(mutex, str)
36
+ @mutex = mutex
37
+ super(str)
38
+ end
34
39
  end
35
40
 
36
41
  class DebugMutex < Mutex
@@ -80,7 +85,7 @@ module ThreadTools
80
85
  else
81
86
  if @owner[:locks].delete(self)
82
87
  @out_of_order_locks += 1
83
- raise EMutexOrder.new("Expected #{@owner[:locks].last}")
88
+ raise EMutexOrder.new(self, "Expected #{@owner[:locks].last}")
84
89
  end
85
90
  # if called again let it pass
86
91
  end
@@ -33,6 +33,7 @@ module ThreadTools
33
33
  # if set to true a new worker is created if the pool is empty
34
34
  attr_accessor :create_on_spawn
35
35
 
36
+
36
37
  # *_size* should be at least 1, *_thr_group* (optional) thread group
37
38
  def initialize(_size, _thr_group = nil)
38
39
  @kill_worker_on_exception = false
@@ -40,7 +41,8 @@ module ThreadTools
40
41
  @pool_mtx = Mutex.new
41
42
  @pool_cv = ConditionVariable.new
42
43
  @pool = []
43
- @thr_grp = _thr_group
44
+ @dummy_grp = ThreadGroup.new
45
+ @busy_grp = _thr_group.nil? ? ThreadGroup.new : _thr_group
44
46
  @create_on_spawn = false
45
47
  _size = 1 if _size < 1
46
48
  _size.times { create_worker }
@@ -48,35 +50,31 @@ module ThreadTools
48
50
 
49
51
  def create_worker
50
52
  Thread.new do
51
- thr = Thread.current
52
- @pool_mtx.synchronize do
53
- @size += 1
54
- if (!@thr_grp.nil?)
55
- @thr_grp.add(thr)
56
- end
57
- end
58
- thr[:jobs] = [] # XXX array not really necessary
59
- thr[:sem] = Semaphore.new(0)
60
- loop do
61
- @pool_mtx.synchronize do
62
- @pool << thr # puts this thread in the pool
63
- @pool_cv.signal
64
- end
65
- thr[:sem].acquire # wait here for jobs to become available
66
- job = thr[:jobs].shift # pop out a job
67
- if (job.nil? || job[:block].nil?)
68
- break # exit thread if job or block is nil
69
- end
70
- begin
71
- job[:block].call(*job[:args]) # call block
72
- rescue
73
- if (@kill_worker_on_exception)
74
- break # exit thread on exception
53
+ @pool_mtx.synchronize do @size += 1 end
54
+ begin
55
+ thr = Thread.current
56
+ thr[:jobs] = [] # XXX array not really necessary
57
+ thr[:sem] = Semaphore.new(0)
58
+ loop do
59
+ @pool_mtx.synchronize do
60
+ @pool << thr # puts this thread in the pool
61
+ @pool_cv.signal
62
+ end
63
+ thr[:sem].acquire # wait here for jobs to become available
64
+ job = thr[:jobs].shift # pop out a job
65
+ if (job.nil? || job[:block].nil?)
66
+ break # exit thread if job or block is nil
67
+ end
68
+ begin
69
+ @busy_grp.add(thr) # adds the thread
70
+ job[:block].call(*job[:args]) # call block
71
+ @dummy_grp.add(thr) # removes from the previous group
72
+ rescue
73
+ break if @kill_worker_on_exception # exit thread on exception
75
74
  end
76
75
  end
77
- end
78
- @pool_mtx.synchronize do
79
- @size -= 1
76
+ ensure
77
+ @pool_mtx.synchronize do @size -= 1 end
80
78
  end
81
79
  end
82
80
  end
@@ -85,9 +83,7 @@ module ThreadTools
85
83
  thr = nil
86
84
  @pool_mtx.synchronize do
87
85
  # creates a new worker thread if pool is empty and flag is set
88
- if (@create_on_spawn && @pool.empty?)
89
- create_worker
90
- end
86
+ create_worker if @create_on_spawn && @pool.empty?
91
87
  # wait here until a worker is available
92
88
  @pool_cv.wait(@pool_mtx) until !(thr = @pool.shift).nil?
93
89
  thr[:jobs] << { :args => args, :block => block }
@@ -105,10 +101,18 @@ module ThreadTools
105
101
  thr[:jobs].clear # clear any pending job
106
102
  thr[:jobs] << nil # queue a nil job
107
103
  thr[:sem].release
108
- if (_sync)
109
- thr.join # wait here for the thread to die
110
- end
104
+ thr.join if _sync # wait here for the thread to die
111
105
  end
106
+ busy_list = @busy_grp.list
107
+ busy_list.each do |athr|
108
+ athr.raise('shutdown') # XXX we could .kill instead
109
+ athr.join if _sync # wait here for the thread to die
110
+ end
111
+ end
112
+
113
+ # returns the number of busy workers
114
+ def busy_size
115
+ @busy_grp.list.size
112
116
  end
113
117
  end
114
118
 
@@ -6,36 +6,53 @@ class ThreadPoolTest < Test::Unit::TestCase
6
6
  def setup
7
7
  super
8
8
  @tpool = ThreadTools::ThreadPool.new(2)
9
- @tpool.kill_worker_on_exception = true
10
9
  end
11
10
 
12
- def test_thread_spawn
11
+ def test_spawn
13
12
  uniq_threads = {}
14
13
  sum_orig = 0
15
- sum_lock = Mutex.new
16
14
  sum_thr = 0
17
- 4.times {|i|
15
+ sum_lock = Mutex.new
16
+ 4.times do |i|
18
17
  sum_orig += i
19
- @tpool.spawn(i) {|ti|
20
- sum_lock.synchronize do
21
- sum_thr += ti
22
- end
18
+ @tpool.spawn(i) do |j|
19
+ sum_lock.synchronize { sum_thr += j }
23
20
  uniq_threads[Thread.current] = true
24
- if (ti == 3)
25
- # this exception should kill this worker
26
- raise "oups"
27
- end
28
- }
29
- }
21
+ end
22
+ end
30
23
  sleep 0.1
31
- # 2 workers - 1 from raised exceptions = 1
32
- assert_equal(@tpool.size, 1)
33
- @tpool.shutdown
34
24
  # same sum
35
25
  assert_equal(sum_orig, sum_thr)
36
26
  # number of different threads
37
27
  assert_equal(uniq_threads.size, 2)
28
+ end
29
+
30
+ def test_raise
31
+ @tpool.kill_worker_on_exception = true
32
+ 2.times do |i|
33
+ @tpool.spawn(i+1) do |j|
34
+ raise 'oups' if j == 2
35
+ end
36
+ end
37
+ sleep 0.1
38
+ # 2 workers - 1 from raised exceptions = 1
39
+ assert_equal(@tpool.size, 1)
40
+ end
41
+
42
+ def test_shutdown
43
+ # initial number of threads
44
+ assert_equal(@tpool.size, 2)
45
+ @tpool.shutdown
38
46
  # after shutdown all threads are dead
39
47
  assert_equal(@tpool.size, 0)
40
48
  end
49
+
50
+ def test_spawn_create
51
+ # kill all workers first
52
+ @tpool.shutdown
53
+ @tpool.create_on_spawn = true
54
+ @tpool.spawn {}
55
+ # a new worker was created
56
+ assert_equal(@tpool.size, 1)
57
+ end
41
58
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{thread_tools}
3
- s.version = '0.27'
3
+ s.version = '0.28'
4
4
  s.summary = %q{Utilities for threaded apps}
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.email = %q{daniel@tralamazza.com}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thread_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.27"
4
+ version: "0.28"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Tralamazza
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-06 00:00:00 +02:00
12
+ date: 2009-10-14 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies: []
15
15