|
1 | 1 | require 'spec_helper'
|
2 | 2 | require 'concurrent/actress'
|
3 | 3 |
|
4 |
| -EXECUTOR = Concurrent::ThreadPoolExecutor.new( |
5 |
| - min_threads: [2, Concurrent.processor_count].max, |
6 |
| - max_threads: [20, Concurrent.processor_count * 15].max, |
7 |
| - idletime: 2 * 60, # 2 minutes |
8 |
| - max_queue: 0, # unlimited |
9 |
| - overflow_policy: :abort # raise an exception |
10 |
| -) |
| 4 | +module Concurrent |
| 5 | + module Actress |
| 6 | + describe 'Concurrent::Actress' do |
11 | 7 |
|
12 |
| -describe Concurrent::Actress do |
| 8 | + class Ping |
| 9 | + include Context |
13 | 10 |
|
14 |
| - class Ping |
15 |
| - include Concurrent::Actress::Context |
| 11 | + def initialize(queue) |
| 12 | + @queue = queue |
| 13 | + end |
16 | 14 |
|
17 |
| - def initialize(queue) |
18 |
| - @queue = queue |
19 |
| - end |
| 15 | + def on_message(message) |
| 16 | + case message |
| 17 | + when :terminate |
| 18 | + terminate! |
| 19 | + when :child |
| 20 | + AdHoc.spawn(:pong, @queue) { |queue| -> m { queue << m } } |
| 21 | + else |
| 22 | + @queue << message |
| 23 | + message |
| 24 | + end |
| 25 | + end |
| 26 | + end |
20 | 27 |
|
21 |
| - def on_message(message) |
22 |
| - case message |
23 |
| - when :terminate |
24 |
| - terminate! |
25 |
| - when :child |
26 |
| - Concurrent::Actress::AdHoc.spawn(name: :pong, executor: EXECUTOR) { -> m { @queue << m } } |
27 |
| - else |
28 |
| - @queue << message |
29 |
| - message |
| 28 | + # def trace! |
| 29 | + # set_trace_func proc { |event, file, line, id, binding, classname| |
| 30 | + # # thread = eval('Thread.current', binding).object_id.to_s(16) |
| 31 | + # printf "%8s %20s %20s %s %s:%-2d\n", event, id, classname, nil, file, line |
| 32 | + # } |
| 33 | + # yield |
| 34 | + # ensure |
| 35 | + # set_trace_func nil |
| 36 | + # end |
| 37 | + |
| 38 | + def assert condition |
| 39 | + unless condition |
| 40 | + # require 'pry' |
| 41 | + # binding.pry |
| 42 | + raise |
| 43 | + puts "--- \n#{caller.join("\n")}" |
| 44 | + end |
30 | 45 | end
|
31 |
| - end |
32 |
| - end |
33 | 46 |
|
34 |
| - # def trace! |
35 |
| - # set_trace_func proc { |event, file, line, id, binding, classname| |
36 |
| - # # thread = eval('Thread.current', binding).object_id.to_s(16) |
37 |
| - # printf "%8s %20s %20s %s %s:%-2d\n", event, id, classname, nil, file, line |
38 |
| - # } |
39 |
| - # yield |
40 |
| - # ensure |
41 |
| - # set_trace_func nil |
42 |
| - # end |
43 |
| - |
44 |
| - def assert condition |
45 |
| - unless condition |
46 |
| - # require 'pry' |
47 |
| - # binding.pry |
48 |
| - raise |
49 |
| - puts "--- \n#{caller.join("\n")}" |
50 |
| - end |
51 |
| - end |
| 47 | + describe 'stress test' do |
| 48 | + 1.times do |i| |
| 49 | + it format('run %3d', i) do |
| 50 | + # puts format('run %3d', i) |
| 51 | + Array.new(10).map do |
| 52 | + Thread.new do |
| 53 | + 10.times do |
| 54 | + # trace! do |
| 55 | + queue = Queue.new |
| 56 | + actor = Ping.spawn :ping, queue |
| 57 | + |
| 58 | + # when spawn returns children are set |
| 59 | + assert Concurrent::Actress::ROOT.send(:core).instance_variable_get(:@children).include?(actor) |
| 60 | + |
| 61 | + actor << 'a' << 1 |
| 62 | + assert queue.pop == 'a' |
| 63 | + assert actor.ask(2).value == 2 |
52 | 64 |
|
| 65 | + assert actor.parent == Concurrent::Actress::ROOT |
| 66 | + assert Concurrent::Actress::ROOT.path == '/' |
| 67 | + assert actor.path == '/ping' |
| 68 | + child = actor.ask(:child).value |
| 69 | + assert child.path == '/ping/pong' |
| 70 | + queue.clear |
| 71 | + child.ask(3) |
| 72 | + assert queue.pop == 3 |
53 | 73 |
|
54 |
| - # FIXME increasing this does not work in Rspec environment |
55 |
| - 1.times do |i| |
56 |
| - it format('run %3d', i) do |
57 |
| - # puts format('run %3d', i) |
58 |
| - Array.new(10).map do |
59 |
| - Thread.new do |
60 |
| - 10.times do |
61 |
| - # trace! do |
62 |
| - queue = Queue.new |
63 |
| - actor = Ping.spawn name: :ping, executor: EXECUTOR, args: [queue] |
64 |
| - |
65 |
| - # when spawn returns children are set |
66 |
| - assert Concurrent::Actress::ROOT.send(:core).instance_variable_get(:@children).include?(actor) |
67 |
| - |
68 |
| - actor << 'a' << 1 |
69 |
| - assert queue.pop == 'a' |
70 |
| - assert actor.ask(2).value == 2 |
71 |
| - |
72 |
| - assert actor.parent == Concurrent::Actress::ROOT |
73 |
| - assert Concurrent::Actress::ROOT.path == '/' |
74 |
| - assert actor.path == '/ping' |
75 |
| - child = actor.ask(:child).value |
76 |
| - assert child.path == '/ping/pong' |
77 |
| - queue.clear |
78 |
| - child.ask(3) |
79 |
| - assert queue.pop == 3 |
80 |
| - |
81 |
| - actor << :terminate |
82 |
| - assert actor.ask(:blow_up).wait.rejected? |
| 74 | + actor << :terminate |
| 75 | + assert actor.ask(:blow_up).wait.rejected? |
| 76 | + end |
| 77 | + end |
| 78 | + end.each(&:join) |
83 | 79 | end
|
84 | 80 | end
|
85 |
| - end.each(&:join) |
| 81 | + end |
| 82 | + |
| 83 | + describe 'spawning' do |
| 84 | + describe 'Actress#spawn' do |
| 85 | + behaviour = -> v { -> _ { v } } |
| 86 | + subjects = { spawn: -> { Actress.spawn(AdHoc, :ping, 'arg', &behaviour) }, |
| 87 | + context_spawn: -> { AdHoc.spawn(:ping, 'arg', &behaviour) }, |
| 88 | + spawn_by_hash: -> { Actress.spawn(class: AdHoc, name: :ping, args: ['arg'], &behaviour) }, |
| 89 | + context_spawn_by_hash: -> { AdHoc.spawn(name: :ping, args: ['arg'], &behaviour) } } |
| 90 | + |
| 91 | + subjects.each do |desc, subject_definition| |
| 92 | + describe desc do |
| 93 | + subject &subject_definition |
| 94 | + its(:path) { should eq '/ping' } |
| 95 | + its(:parent) { should eq ROOT } |
| 96 | + its(:name) { should eq 'ping' } |
| 97 | + its(:executor) { should eq Concurrent.configuration.global_task_pool } |
| 98 | + its(:reference) { should eq subject } |
| 99 | + it 'returns ars' do |
| 100 | + subject.ask!(:anything).should eq 'arg' |
| 101 | + end |
| 102 | + end |
| 103 | + end |
| 104 | + end |
| 105 | + |
| 106 | + it 'terminates on failed initialization' do |
| 107 | + a = AdHoc.spawn(name: :fail, logger_level: 4) { raise } |
| 108 | + a.ask(nil).wait.rejected?.should be_true |
| 109 | + a.terminated?.should be_true |
| 110 | + end |
| 111 | + |
| 112 | + it 'terminates on failed message processing' do |
| 113 | + a = AdHoc.spawn(name: :fail, logger_level: 4) { -> _ { raise } } |
| 114 | + a.ask(nil).wait.rejected?.should be_true |
| 115 | + a.terminated?.should be_true |
| 116 | + end |
| 117 | + end |
| 118 | + |
| 119 | + describe 'messaging' do |
| 120 | + subject { AdHoc.spawn(:add) { c = 0; -> v { c = c + v } } } |
| 121 | + specify do |
| 122 | + subject.tell(1).tell(1) |
| 123 | + subject << 1 << 1 |
| 124 | + subject.ask(0).value!.should eq 4 |
| 125 | + end |
| 126 | + end |
| 127 | + |
| 128 | + describe 'children' do |
| 129 | + let(:parent) do |
| 130 | + AdHoc.spawn(:parent) do |
| 131 | + -> message do |
| 132 | + if message == :child |
| 133 | + AdHoc.spawn(:child) { -> _ { parent } } |
| 134 | + else |
| 135 | + children |
| 136 | + end |
| 137 | + end |
| 138 | + end |
| 139 | + end |
| 140 | + |
| 141 | + it 'has children set after a child is created' do |
| 142 | + child = parent.ask!(:child) |
| 143 | + parent.ask!(nil).should include(child) |
| 144 | + child.ask!(nil).should eq parent |
| 145 | + end |
| 146 | + end |
| 147 | + |
| 148 | + describe 'envelope' do |
| 149 | + subject { AdHoc.spawn(:subject) { -> _ { envelope } } } |
| 150 | + specify do |
| 151 | + envelope = subject.ask!('a') |
| 152 | + envelope.should be_a_kind_of Envelope |
| 153 | + envelope.message.should eq 'a' |
| 154 | + envelope.ivar.should be_completed |
| 155 | + envelope.ivar.value.should eq envelope |
| 156 | + envelope.sender.should eq Thread.current |
| 157 | + end |
| 158 | + end |
86 | 159 | end
|
| 160 | + |
87 | 161 | end
|
88 | 162 | end
|
| 163 | + |
0 commit comments