dispatch_queue_rb 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.
- 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
|