thread_tools 0.25 → 0.26
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.
@@ -3,27 +3,40 @@
|
|
3
3
|
#
|
4
4
|
# Mutex
|
5
5
|
#
|
6
|
-
# This class will make things slower, expect more contentions than normal
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# This class will make things slower, expect more contentions than normal, so
|
7
|
+
# don't use to protect small blocks or tight loops
|
8
|
+
#
|
9
|
+
# I derived this mutex because I needed to check contention levels, trace
|
10
|
+
# ownership changes and catch simple lock inversions
|
11
|
+
#
|
12
|
+
# Use .inspect method to get a small report
|
9
13
|
|
10
14
|
require 'thread'
|
11
15
|
|
12
16
|
|
13
17
|
module ThreadTools
|
14
18
|
|
19
|
+
class EMutexOrder < Exception
|
20
|
+
end
|
21
|
+
|
15
22
|
class DebugMutex < Mutex
|
23
|
+
attr_reader :out_of_order_locks
|
16
24
|
attr_reader :contentions
|
17
25
|
attr_reader :owner
|
18
26
|
|
19
27
|
def initialize
|
20
28
|
@contentions = 0
|
29
|
+
@out_of_order_locks = 0
|
21
30
|
end
|
22
31
|
|
23
32
|
def lock
|
24
33
|
unless try_lock
|
25
34
|
super
|
26
35
|
@owner = Thread.current
|
36
|
+
if @owner[:locks].nil?
|
37
|
+
@owner[:locks] = []
|
38
|
+
end
|
39
|
+
@owner[:locks] << self
|
27
40
|
end
|
28
41
|
self
|
29
42
|
end
|
@@ -34,12 +47,28 @@ module ThreadTools
|
|
34
47
|
@contentions += 1
|
35
48
|
else
|
36
49
|
@owner = Thread.current
|
50
|
+
if @owner[:locks].nil?
|
51
|
+
@owner[:locks] = []
|
52
|
+
end
|
53
|
+
@owner[:locks] << self
|
37
54
|
end
|
38
55
|
ret
|
39
56
|
end
|
40
57
|
|
58
|
+
# throws EMutexOrder, mutex remains locked in this case
|
41
59
|
def unlock
|
42
|
-
|
60
|
+
if (!@owner.nil?)
|
61
|
+
if (@owner[:locks].last == self)
|
62
|
+
@owner[:locks].pop
|
63
|
+
else
|
64
|
+
if @owner[:locks].delete(self)
|
65
|
+
@out_of_order_locks += 1
|
66
|
+
raise EMutexOrder.new("Expected #{@owner[:locks].last}")
|
67
|
+
end
|
68
|
+
# if called again let it pass
|
69
|
+
end
|
70
|
+
@owner = nil
|
71
|
+
end
|
43
72
|
super
|
44
73
|
end
|
45
74
|
|
@@ -51,6 +80,17 @@ module ThreadTools
|
|
51
80
|
unlock
|
52
81
|
end
|
53
82
|
end
|
83
|
+
|
84
|
+
def inspect
|
85
|
+
"Owner #{@owner}, Contentions #{@contentions}, Out of order acquisitions #{@out_of_order_locks}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# just for fun
|
89
|
+
def self.unlock_all(thread)
|
90
|
+
thread[:locks].reverse_each do |l|
|
91
|
+
l.unlock
|
92
|
+
end
|
93
|
+
end
|
54
94
|
end
|
55
95
|
|
56
96
|
end
|
@@ -13,7 +13,7 @@ require File.expand_path(File.dirname(__FILE__)+'/threadpool')
|
|
13
13
|
module Mongrel
|
14
14
|
class HttpServer
|
15
15
|
|
16
|
-
def run(_pool_size =
|
16
|
+
def run(_pool_size = 50)
|
17
17
|
trap("TERM") { stop } # trap "kill"
|
18
18
|
|
19
19
|
@thread_pool = ThreadTools::ThreadPool.new(_pool_size, @workers)
|
@@ -20,19 +20,23 @@ module ThreadTools
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def acquire
|
23
|
+
ret = 0
|
23
24
|
@mtx.synchronize do
|
24
25
|
@cv.wait(@mtx) until @count > 0
|
25
26
|
@count -= 1
|
27
|
+
ret = @count
|
26
28
|
end
|
27
|
-
|
29
|
+
ret
|
28
30
|
end
|
29
31
|
|
30
32
|
def release
|
33
|
+
ret = 0
|
31
34
|
@mtx.synchronize do
|
32
35
|
@count += 1
|
36
|
+
ret = @count
|
33
37
|
@cv.signal
|
34
38
|
end
|
35
|
-
|
39
|
+
ret
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
@@ -25,6 +25,8 @@ module ThreadTools
|
|
25
25
|
attr_accessor :kill_worker_on_exception
|
26
26
|
# number of worker threads
|
27
27
|
attr_reader :size
|
28
|
+
# if set to true a new worker is created if the pool is empty
|
29
|
+
attr_accessor :create_on_spawn
|
28
30
|
|
29
31
|
# _size should be at least 1
|
30
32
|
def initialize(_size, _thr_group = nil)
|
@@ -34,6 +36,7 @@ module ThreadTools
|
|
34
36
|
@pool_cv = ConditionVariable.new
|
35
37
|
@pool = []
|
36
38
|
@thr_grp = _thr_group
|
39
|
+
@create_on_spawn = false
|
37
40
|
_size = 1 if _size < 1
|
38
41
|
_size.times { create_worker }
|
39
42
|
end
|
@@ -76,6 +79,10 @@ module ThreadTools
|
|
76
79
|
def spawn(*args, &block)
|
77
80
|
thr = nil
|
78
81
|
@pool_mtx.synchronize do
|
82
|
+
# creates a new worker thread if pool is empty and flag is set
|
83
|
+
if (@create_on_spawn && @pool.empty?)
|
84
|
+
create_worker
|
85
|
+
end
|
79
86
|
# wait here until a worker is available
|
80
87
|
@pool_cv.wait(@pool_mtx) until !(thr = @pool.shift).nil?
|
81
88
|
thr[:jobs] << { :args => args, :block => block }
|
@@ -98,4 +105,4 @@ module ThreadTools
|
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
|
-
end
|
108
|
+
end
|
data/test/test_debugmutex.rb
CHANGED
@@ -44,4 +44,17 @@ class DebugMutexTest < Test::Unit::TestCase
|
|
44
44
|
# owner is nil
|
45
45
|
assert_nil(mtx.owner)
|
46
46
|
end
|
47
|
+
|
48
|
+
def test_lock_ordering
|
49
|
+
mtxA = ThreadTools::DebugMutex.new
|
50
|
+
mtxB = ThreadTools::DebugMutex.new
|
51
|
+
|
52
|
+
mtxA.lock
|
53
|
+
mtxB.lock
|
54
|
+
assert_raise ThreadTools::EMutexOrder do
|
55
|
+
mtxA.unlock # wrong unlock order
|
56
|
+
end
|
57
|
+
# ensure correct ordering
|
58
|
+
ThreadTools::DebugMutex.unlock_all(Thread.current)
|
59
|
+
end
|
47
60
|
end
|
data/thread_tools.gemspec
CHANGED
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.
|
4
|
+
version: "0.26"
|
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-
|
12
|
+
date: 2009-10-06 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|