dispatch_queue_rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +13 -0
- data/LICENSE +22 -0
- data/README.md +98 -0
- data/dispatch_queue_rb.gemspec +43 -0
- data/lib/dispatch_queue_rb.rb +32 -0
- data/lib/dispatch_queue_rb/concurrent_queue.rb +116 -0
- data/lib/dispatch_queue_rb/dispatch.rb +66 -0
- data/lib/dispatch_queue_rb/dispatch_group.rb +72 -0
- data/lib/dispatch_queue_rb/internal/condition_variable_pool.rb +33 -0
- data/lib/dispatch_queue_rb/internal/continuation.rb +41 -0
- data/lib/dispatch_queue_rb/internal/heap.rb +88 -0
- data/lib/dispatch_queue_rb/internal/thread_pool_queue.rb +127 -0
- data/lib/dispatch_queue_rb/internal/thread_queue.rb +62 -0
- data/lib/dispatch_queue_rb/internal/timer_pool.rb +71 -0
- data/lib/dispatch_queue_rb/mixins/dispatch_after_impl.rb +18 -0
- data/lib/dispatch_queue_rb/mixins/dispatch_sync_impl.rb +42 -0
- data/lib/dispatch_queue_rb/serial_queue.rb +77 -0
- data/lib/dispatch_queue_rb/version.rb +12 -0
- data/rakefile.rb +110 -0
- data/test/_test_env.rb +52 -0
- data/test/test_concurrent_queue.rb +90 -0
- data/test/test_condition_variable_pool.rb +41 -0
- data/test/test_continuation.rb +23 -0
- data/test/test_dispatch.rb +91 -0
- data/test/test_dispatch_group.rb +59 -0
- data/test/test_group_concurrent_queue.rb +75 -0
- data/test/test_group_serial_queue.rb +33 -0
- data/test/test_group_thread_pool_queue.rb +34 -0
- data/test/test_heap.rb +58 -0
- data/test/test_serial_queue.rb +77 -0
- data/test/test_thread_pool_queue.rb +63 -0
- data/test/test_thread_queue.rb +77 -0
- data/test/test_timer_pool.rb +124 -0
- data/test/test_version.rb +155 -0
- metadata +181 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_dispatch.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
|
13
|
+
module DispatchQueue
|
14
|
+
describe Dispatch do
|
15
|
+
describe "default_queue" do
|
16
|
+
it "is a ThreadPoolQueue" do
|
17
|
+
Dispatch.default_queue.must_be_instance_of ThreadPoolQueue
|
18
|
+
end
|
19
|
+
|
20
|
+
it "must remain the same" do
|
21
|
+
q1 = Dispatch.default_queue
|
22
|
+
q2 = Dispatch.default_queue
|
23
|
+
|
24
|
+
q1.wont_be_nil
|
25
|
+
q2.wont_be_nil
|
26
|
+
q2.must_be_same_as q1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "main_queue" do
|
31
|
+
it "is a ThreadQueue" do
|
32
|
+
Dispatch.main_queue.must_be_instance_of ThreadQueue
|
33
|
+
end
|
34
|
+
|
35
|
+
it "must remain the same" do
|
36
|
+
q1 = Dispatch.main_queue
|
37
|
+
q2 = Dispatch.main_queue
|
38
|
+
|
39
|
+
q1.wont_be_nil
|
40
|
+
q2.wont_be_nil
|
41
|
+
q2.must_be_same_as q1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "ncpu" do
|
46
|
+
it "returns the number of cpu cores available" do
|
47
|
+
Dispatch.ncpu.must_be_kind_of Integer
|
48
|
+
Dispatch.ncpu.must_be :>, 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "synchronize" do
|
53
|
+
it "synchronizes an asynchronous operation with the calling thread" do
|
54
|
+
done = false
|
55
|
+
assert_wont_timeout do
|
56
|
+
Dispatch.synchronize do |completion_handler|
|
57
|
+
Dispatch.default_queue.dispatch_async do
|
58
|
+
done = true
|
59
|
+
completion_handler.call()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
done.must_equal true
|
64
|
+
end
|
65
|
+
|
66
|
+
it "return its result to the calling thread" do
|
67
|
+
assert_wont_timeout do
|
68
|
+
Dispatch.synchronize do |completion_handler|
|
69
|
+
Dispatch.default_queue.dispatch_async do
|
70
|
+
completion_handler.call( :result )
|
71
|
+
end
|
72
|
+
end.must_equal :result
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "concurrent_map" do
|
78
|
+
it "returns reslt of concurrently executed block" do
|
79
|
+
start_time = Time.now()
|
80
|
+
result = Dispatch.concurrent_map( 1..10 ) do |i|
|
81
|
+
sleep( 0.001 )
|
82
|
+
i
|
83
|
+
end.must_equal (1..10).to_a
|
84
|
+
|
85
|
+
elapsed_time = Time.now - start_time
|
86
|
+
elapsed_time.must_be :<, 0.005
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_dispatch_group.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
require 'timeout'
|
13
|
+
|
14
|
+
module DispatchQueue
|
15
|
+
describe DispatchGroup do
|
16
|
+
|
17
|
+
subject { DispatchGroup.new }
|
18
|
+
|
19
|
+
it "can create a new group" do
|
20
|
+
subject.must_be_instance_of DispatchGroup
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe "wait()" do
|
25
|
+
it "returns true immediatly when no work is pending in the group" do
|
26
|
+
subject.wait().must_equal true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "waits forever if group enters and never leaves" do
|
30
|
+
subject.enter()
|
31
|
+
|
32
|
+
assert_must_timeout do
|
33
|
+
subject.wait()
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it "waits until timeout expires and returns false if enter never leaves " do
|
38
|
+
subject.enter()
|
39
|
+
|
40
|
+
assert_wont_timeout(0.002) do
|
41
|
+
subject.wait( timeout:0.001 ).must_equal false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it "returns true when leave() is called" do
|
46
|
+
subject.enter()
|
47
|
+
Dispatch.default_queue.dispatch_async do
|
48
|
+
sleep( 0.001 )
|
49
|
+
subject.leave()
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_wont_timeout(0.002) do
|
53
|
+
subject.wait().must_equal true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end # describe "wait()"
|
57
|
+
|
58
|
+
end # describe DispatchGroup do
|
59
|
+
end # module DispatchQueue
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_group_concurrent_queue.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
require 'timeout'
|
13
|
+
|
14
|
+
module DispatchQueue
|
15
|
+
describe "DispatchGroup with ConcurrentQueue" do
|
16
|
+
let( :target_queue ) { ConcurrentQueue.new }
|
17
|
+
let( :group ) { DispatchGroup.new }
|
18
|
+
|
19
|
+
it "leaves group when task completes, with single task on idle queue " do
|
20
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
21
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
22
|
+
assert_wont_timeout( 0.002 ) { group.wait() }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "leaves group when task completes, with enquened tasks" do
|
26
|
+
(1..10).each do
|
27
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
31
|
+
assert_wont_timeout( 0.020 ) { group.wait() }
|
32
|
+
end
|
33
|
+
|
34
|
+
it "leaves group when task completes, with barrier tasks only" do
|
35
|
+
(1..4).each do
|
36
|
+
target_queue.dispatch_barrier_async( group:group ) { sleep( 0.002 ) }
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
40
|
+
assert_wont_timeout( 0.010 ) { group.wait() }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "leaves group when task completes, with mix of barrier and non-barrier tasks" do
|
44
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
45
|
+
(1..10).each do
|
46
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.001 ) }
|
47
|
+
end
|
48
|
+
|
49
|
+
target_queue.dispatch_barrier_async( group:group ) { sleep( 0.001 ) }
|
50
|
+
(1..10).each do
|
51
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
52
|
+
end
|
53
|
+
target_queue.dispatch_barrier_async( group:group ) { sleep( 0.001 ) }
|
54
|
+
|
55
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
56
|
+
assert_wont_timeout( 0.030 ) { group.wait() }
|
57
|
+
end
|
58
|
+
|
59
|
+
it "completes immediatly after a last synchronous barrier" do
|
60
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
61
|
+
(1..10).each do
|
62
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.001 ) }
|
63
|
+
end
|
64
|
+
|
65
|
+
target_queue.dispatch_barrier_async( group:group ) { sleep( 0.001 ) }
|
66
|
+
(1..10).each do
|
67
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
68
|
+
end
|
69
|
+
|
70
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
71
|
+
target_queue.dispatch_barrier_sync( group:group ) { sleep( 0.001 ) }
|
72
|
+
assert_wont_timeout( 0.001 ) { group.wait() }
|
73
|
+
end
|
74
|
+
end # DispatchGroup with ConcurrentQueue
|
75
|
+
end # module DispatchQueue
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_group_serial_queue.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
require 'timeout'
|
13
|
+
|
14
|
+
module DispatchQueue
|
15
|
+
describe "DispatchGroup with SerialQueue" do
|
16
|
+
|
17
|
+
let( :target_queue ) { SerialQueue.new }
|
18
|
+
let( :group ) { DispatchGroup.new }
|
19
|
+
|
20
|
+
it "leaves group when task completes, with single task on idle queue " do
|
21
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
22
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
23
|
+
assert_wont_timeout( 0.002 ) { group.wait() }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "leaves group when task completes, with enquened tasks" do
|
27
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
28
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
29
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
30
|
+
assert_wont_timeout( 0.005 ) { group.wait() }
|
31
|
+
end
|
32
|
+
end # DispatchGroup with SerialQueue
|
33
|
+
end # module DispatchQueue
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_group_thread_pool_queue.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
require 'timeout'
|
13
|
+
|
14
|
+
module DispatchQueue
|
15
|
+
describe "DispatchGroup with ThreadPoolQueue" do
|
16
|
+
let( :target_queue ) { Dispatch.default_queue }
|
17
|
+
let( :group ) { DispatchGroup.new }
|
18
|
+
|
19
|
+
it "leaves group when task completes, with single task on idle queue " do
|
20
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
21
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
22
|
+
assert_wont_timeout( 0.002 ) { group.wait() }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "leaves group when task completes, with enquened tasks" do
|
26
|
+
(1..10).each do
|
27
|
+
target_queue.dispatch_async( group:group ) { sleep( 0.002 ) }
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_must_timeout( 0.001 ) { group.wait() }
|
31
|
+
assert_wont_timeout( 0.020 ) { group.wait() }
|
32
|
+
end
|
33
|
+
end # DispatchGroup with ThreadPoolQueue
|
34
|
+
end # module DispatchQueue
|
data/test/test_heap.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_heap.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
|
13
|
+
module DispatchQueue
|
14
|
+
describe Heap do
|
15
|
+
|
16
|
+
subject { Heap.new }
|
17
|
+
|
18
|
+
def push( heap, *elements )
|
19
|
+
elements.each { |e| heap.push( e ) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def drain( heap )
|
23
|
+
elements = []
|
24
|
+
elements.push( heap.pop() ) while !heap.empty?
|
25
|
+
elements
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can be initialized with no argument" do
|
29
|
+
subject.must_be_instance_of Heap
|
30
|
+
end
|
31
|
+
|
32
|
+
it "is empty when newly created" do
|
33
|
+
subject.size.must_equal 0
|
34
|
+
subject.count.must_equal 0
|
35
|
+
subject.length.must_equal 0
|
36
|
+
subject.empty?.must_equal true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "stores pushed elements in an order that preserves heap property" do
|
40
|
+
push( subject, 7, 1, 6, 2, 5, 3, 4 )
|
41
|
+
subject.size.must_equal 7
|
42
|
+
subject.empty?.must_equal false
|
43
|
+
subject.elements.must_equal( [1, 2, 3, 7, 5, 6, 4] )
|
44
|
+
end
|
45
|
+
|
46
|
+
it "pops elements in sorted order" do
|
47
|
+
push( subject, 7, 1, 6, 2, 5, 3, 4 )
|
48
|
+
drain( subject ).must_equal [1, 2, 3, 4, 5, 6, 7]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "pops elements in sorted order" do
|
52
|
+
count = 100
|
53
|
+
push( subject, *(1..count).to_a.shuffle )
|
54
|
+
drain( subject ).must_equal (1..count).to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_serial_queue.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
|
13
|
+
module DispatchQueue
|
14
|
+
describe SerialQueue do
|
15
|
+
|
16
|
+
let( :task_count ) { 4 }
|
17
|
+
let( :log ) { [] }
|
18
|
+
let( :lock ) { Mutex.new }
|
19
|
+
let( :logger ) { Proc.new { |e, d| lock.synchronize { log << [e, d] } } }
|
20
|
+
subject { SerialQueue.new }
|
21
|
+
|
22
|
+
it "can initialize a serial queue" do
|
23
|
+
subject.must_be_instance_of SerialQueue
|
24
|
+
end
|
25
|
+
|
26
|
+
def task( task_id )
|
27
|
+
logger.call( :begin_task, { task_id:task_id, thread:Thread.current } )
|
28
|
+
sleep(0.001)
|
29
|
+
logger.call( :end_task, { task_id:task_id, thread:Thread.current } )
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "dispatch_async" do
|
33
|
+
it "executes tasks serially" do
|
34
|
+
(1..task_count).each { |i| subject.dispatch_async { task( i ) } }
|
35
|
+
subject.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
36
|
+
|
37
|
+
log.map { |e,d| e }.must_equal [:begin_task, :end_task] * task_count
|
38
|
+
end
|
39
|
+
|
40
|
+
it "executes tasks in enqueue order" do
|
41
|
+
(1..task_count).each { |i| subject.dispatch_async { task( i ) } }
|
42
|
+
subject.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
43
|
+
|
44
|
+
task_id_list = log.select { |e,d| e == :begin_task }.map { |e,d| d[:task_id] }
|
45
|
+
task_id_list.must_equal (1..task_count).to_a
|
46
|
+
end
|
47
|
+
|
48
|
+
it "resumes execution when new tasks are enqueues" do
|
49
|
+
(1..task_count).each { |i| subject.dispatch_async { task( "a#{i}".to_sym ) } }
|
50
|
+
subject.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
51
|
+
sleep( 0.01 )
|
52
|
+
|
53
|
+
(1..task_count).each { |i| subject.dispatch_async { task( "b#{i}".to_sym ) } }
|
54
|
+
subject.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
55
|
+
|
56
|
+
task_id_list = log.select { |e,d| e == :begin_task }.map { |e,d| d[:task_id] }
|
57
|
+
task_id_list.count.must_equal task_count*2, task_id_list
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "with multiple serial queues" do
|
62
|
+
let( :subject2 ) { SerialQueue.new }
|
63
|
+
|
64
|
+
it "interleaves tasks from different queues" do
|
65
|
+
(1..task_count).each { |i| subject.dispatch_async { task( "a#{i}".to_sym ) } }
|
66
|
+
(1..task_count).each { |i| subject2.dispatch_async { task( "b#{i}".to_sym ) } }
|
67
|
+
subject.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
68
|
+
subject2.dispatch_barrier_sync {} # wait for all previous tasks to complete
|
69
|
+
|
70
|
+
task_id_list = log.select { |e,d| e == :begin_task }.map { |e,d| d[:task_id] }
|
71
|
+
task_chunks = task_id_list.chunk { |e| e.to_s[0] }.map { |c, l| l }
|
72
|
+
task_chunks.count.must_be :>, 3, task_chunks
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
#
|
3
|
+
# MODULE : test/test_thread_pool_queue.rb
|
4
|
+
# PROJECT : DispatchQueue
|
5
|
+
# DESCRIPTION :
|
6
|
+
#
|
7
|
+
# Copyright (c) 2016, Marc-Antoine Argenton. All rights reserved.
|
8
|
+
# =============================================================================
|
9
|
+
|
10
|
+
|
11
|
+
require '_test_env.rb'
|
12
|
+
|
13
|
+
module DispatchQueue
|
14
|
+
describe ThreadPoolQueue do
|
15
|
+
|
16
|
+
let( :log ) { [] }
|
17
|
+
let( :lock ) { Mutex.new }
|
18
|
+
let( :logger ) { Proc.new { |e, d| lock.synchronize { log << [e, d] } } }
|
19
|
+
let( :max_threads ) { 2 }
|
20
|
+
subject { ThreadPoolQueue.new( max_threads: max_threads,
|
21
|
+
debug_trace: logger,
|
22
|
+
thread_termination_delay: 0.001 ) }
|
23
|
+
|
24
|
+
it "can initialize a thread ppol" do
|
25
|
+
subject.must_be_instance_of ThreadPoolQueue
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can execute work in the thread pool" do
|
29
|
+
(1..10).each do |i|
|
30
|
+
subject.dispatch_async do
|
31
|
+
sleep(0.001)
|
32
|
+
logger.call( :task, { task_id:i, thread:Thread.current } )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
subject.wait_for_all_threads_termination()
|
36
|
+
|
37
|
+
completed_tasks = Set.new( log.select { |e, d| e == :task }.map { |e, d| d[:task_id] } )
|
38
|
+
completed_tasks.must_equal( Set.new( 1..10 ) )
|
39
|
+
end
|
40
|
+
|
41
|
+
it "spawns and terminates threads as needed" do
|
42
|
+
(1..200).each do |i|
|
43
|
+
subject.dispatch_async do
|
44
|
+
sleep(0.001)
|
45
|
+
logger.call( :task, { task_id:i, thread:Thread.current } )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sleep(0.02)
|
50
|
+
subject.max_threads = 8
|
51
|
+
sleep(0.01)
|
52
|
+
subject.max_threads = 2
|
53
|
+
|
54
|
+
subject.wait_for_all_threads_termination()
|
55
|
+
|
56
|
+
thread_count = 0
|
57
|
+
thread_count_list = log.map { |e, d| thread_count = d[:thread_count] || thread_count }
|
58
|
+
major_thread_counts = thread_count_list.chunk { |n| n }.map { |cnt, e| [cnt, e.count] }.select { |t,e| e > 10 }.map { |t,e| t }
|
59
|
+
major_thread_counts.must_equal( [2, 8, 2] )
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|