mutant 0.6.7 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +10 -0
- data/README.md +1 -1
- data/config/flay.yml +1 -1
- data/config/reek.yml +11 -40
- data/config/rubocop.yml +1 -1
- data/lib/mutant.rb +10 -2
- data/lib/mutant/actor.rb +64 -0
- data/lib/mutant/actor/actor.rb +50 -0
- data/lib/mutant/actor/env.rb +35 -0
- data/lib/mutant/actor/mailbox.rb +53 -0
- data/lib/mutant/actor/receiver.rb +48 -0
- data/lib/mutant/actor/sender.rb +27 -0
- data/lib/mutant/ast/types.rb +1 -1
- data/lib/mutant/cli.rb +2 -0
- data/lib/mutant/config.rb +2 -1
- data/lib/mutant/env.rb +6 -2
- data/lib/mutant/expression/methods.rb +1 -1
- data/lib/mutant/integration.rb +11 -1
- data/lib/mutant/isolation.rb +1 -2
- data/lib/mutant/meta/example.rb +1 -1
- data/lib/mutant/mutation.rb +47 -21
- data/lib/mutant/mutator/node.rb +1 -1
- data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
- data/lib/mutant/mutator/node/send.rb +4 -3
- data/lib/mutant/reporter/cli.rb +2 -0
- data/lib/mutant/reporter/cli/format.rb +23 -36
- data/lib/mutant/reporter/cli/printer.rb +66 -27
- data/lib/mutant/result.rb +45 -58
- data/lib/mutant/runner.rb +47 -154
- data/lib/mutant/runner/master.rb +174 -0
- data/lib/mutant/runner/scheduler.rb +141 -0
- data/lib/mutant/runner/worker.rb +93 -0
- data/lib/mutant/subject/method/instance.rb +1 -15
- data/lib/mutant/test.rb +2 -15
- data/lib/mutant/version.rb +1 -1
- data/meta/send.rb +16 -0
- data/mutant-rspec.gemspec +1 -1
- data/mutant.gemspec +1 -1
- data/spec/integration/mutant/rspec_spec.rb +0 -6
- data/spec/spec_helper.rb +9 -1
- data/spec/support/fake_actor.rb +93 -0
- data/spec/support/shared_context.rb +135 -0
- data/spec/unit/mutant/actor/actor_spec.rb +35 -0
- data/spec/unit/mutant/actor/binding_spec.rb +32 -0
- data/spec/unit/mutant/actor/env_spec.rb +49 -0
- data/spec/unit/mutant/actor/message_spec.rb +23 -0
- data/spec/unit/mutant/actor/receiver_spec.rb +60 -0
- data/spec/unit/mutant/actor/sender_spec.rb +22 -0
- data/spec/unit/mutant/cli_spec.rb +17 -4
- data/spec/unit/mutant/env_spec.rb +2 -2
- data/spec/unit/mutant/mailbox_spec.rb +33 -0
- data/spec/unit/mutant/mutation_spec.rb +52 -18
- data/spec/unit/mutant/mutator/registry_spec.rb +4 -4
- data/spec/unit/mutant/reporter/cli_spec.rb +131 -249
- data/spec/unit/mutant/result/env_spec.rb +55 -0
- data/spec/unit/mutant/result/subject_spec.rb +43 -0
- data/spec/unit/mutant/runner/master_spec.rb +199 -0
- data/spec/unit/mutant/runner/scheduler_spec.rb +161 -0
- data/spec/unit/mutant/runner/worker_spec.rb +73 -0
- data/spec/unit/mutant/runner_spec.rb +60 -118
- data/spec/unit/mutant/subject/method/instance_spec.rb +18 -31
- data/spec/unit/mutant/warning_filter_spec.rb +1 -1
- metadata +39 -14
- data/lib/mutant/runner/collector.rb +0 -133
- data/lib/mutant/warning_expectation.rb +0 -47
- data/spec/unit/mutant/runner/collector_spec.rb +0 -198
- data/spec/unit/mutant/test_spec.rb +0 -23
- data/spec/unit/mutant/warning_expectation_spec.rb +0 -80
- data/test_app/Gemfile.rspec2 +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 607569850361ac4088176c89f75ac7f7d732d8f0
|
4
|
+
data.tar.gz: 3247e38171198bf00c53341447881cb76fa44a3f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 888a9f29aa202635ece333e17c514ca52c7a28660e0ab8cd7f4397f4f108be3a8d91477812ad48ae970f2808b1516de084f44ff02f743b21174ad2504fb4cdae
|
7
|
+
data.tar.gz: 5e3faa4096046383aa51274b6f201fb3f1eb416d7f8ca8049505fcf823fd994f051c34dd159256a6926e56401f5270d859b0bad0f13b645101442bfff5e80ef6
|
data/Changelog.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# v0.7.1 2014-12-04
|
2
|
+
|
3
|
+
* Fix invalid dependencies on rspec for mutant-rspec
|
4
|
+
|
5
|
+
# v0.7.0 2014-12-04
|
6
|
+
|
7
|
+
* Use homegrown actor based parallelization
|
8
|
+
* Fix redundant spec execution in rspec integration
|
9
|
+
* Add mutation from #send to #__send__ (the canonical form).
|
10
|
+
|
1
11
|
# v0.6.7 2014-11-17
|
2
12
|
|
3
13
|
* Fix duplicate neutral emit for memoized instance method subjects
|
data/README.md
CHANGED
@@ -97,7 +97,7 @@ This can have the following unintended effects:
|
|
97
97
|
counts the mutation as killed.
|
98
98
|
|
99
99
|
* MRI crashes with a segfault and gets stuck when handling the segfault.
|
100
|
-
Depending on the number of active kill jobs mutant might appear to continue normally
|
100
|
+
Depending on the number of active kill jobs mutant might appear to continue normally until
|
101
101
|
all workers are stuck into this state when it begins to hang.
|
102
102
|
Currently mutant must assume that your test suite simply not terminated yet as from the outside
|
103
103
|
(parent process) the difference between a long running test and a stuck MRI is not observable.
|
data/config/flay.yml
CHANGED
data/config/reek.yml
CHANGED
@@ -12,7 +12,6 @@ ControlParameter:
|
|
12
12
|
enabled: true
|
13
13
|
exclude:
|
14
14
|
- Mutant::Expression#match_length
|
15
|
-
- Mutant::Reporter::CLI::Printer::SubjectProgress#print_mutation_result
|
16
15
|
DataClump:
|
17
16
|
enabled: true
|
18
17
|
exclude: []
|
@@ -26,24 +25,18 @@ DuplicateMethodCall:
|
|
26
25
|
FeatureEnvy:
|
27
26
|
enabled: true
|
28
27
|
exclude:
|
29
|
-
# Nature of OptionParser :(
|
30
|
-
- Mutant::CLI#add_environment_options
|
31
28
|
- Mutant::Env#scope_name
|
32
29
|
- Mutant::Diff#minimized_hunks
|
33
|
-
- Mutant::Integration::Rspec#
|
34
|
-
- Mutant::Integration::Rspec::Rspec2#full_description
|
35
|
-
- Mutant::Integration::Rspec::Rspec3#full_description
|
30
|
+
- Mutant::Integration::Rspec#parse_example
|
36
31
|
- Mutant::Matcher::Method::Instance#match?
|
37
32
|
- Mutant::Matcher::Method::Singleton#receiver?
|
38
|
-
- Mutant::Mutation::Evil#success?
|
39
|
-
- Mutant::Mutation::Neutral#success?
|
40
33
|
- Mutant::Mutator::Node#children_indices
|
41
|
-
# False positive, its a utility
|
42
|
-
- Mutant::Meta::Example::Verification#format_mutation
|
34
|
+
- Mutant::Meta::Example::Verification#format_mutation # False positive, its a utility
|
43
35
|
- Mutant::Reporter::CLI#subject_results
|
44
|
-
- Mutant::Runner#run_mutation_test
|
45
|
-
- Mutant::Runner#kill_mutation
|
46
36
|
- Mutant::Runner#finish
|
37
|
+
- Mutant::Runner::Master#stop_worker
|
38
|
+
- Mutant::Runner::Worker#run_mutation
|
39
|
+
- Mutant::Runner::Worker#handle
|
47
40
|
IrresponsibleModule:
|
48
41
|
enabled: true
|
49
42
|
exclude: []
|
@@ -51,8 +44,6 @@ LongParameterList:
|
|
51
44
|
enabled: true
|
52
45
|
exclude:
|
53
46
|
- Mutant::Matcher::Method::Instance#self.build?
|
54
|
-
- Mutant::Runner#finish # API client of parallel, one gets _ignored.
|
55
|
-
- Mutant::Runner#self.run
|
56
47
|
max_params: 2
|
57
48
|
LongYieldList:
|
58
49
|
enabled: true
|
@@ -63,7 +54,6 @@ NestedIterators:
|
|
63
54
|
exclude:
|
64
55
|
- Mutant#self.singleton_subclass_instance
|
65
56
|
- Mutant::CLI#parse
|
66
|
-
- Mutant::Integration::Rspec#run
|
67
57
|
- Mutant::Isolation::Fork#self.call
|
68
58
|
- Mutant::Mutator::Util::Array::Element#dispatch
|
69
59
|
- Mutant::Mutator::Node::Resbody#mutate_captures
|
@@ -80,41 +70,31 @@ RepeatedConditional:
|
|
80
70
|
enabled: true
|
81
71
|
exclude:
|
82
72
|
- Mutant::Mutator
|
83
|
-
- Mutant::Reporter::CLI
|
84
73
|
- Mutant::Meta::Example::DSL
|
74
|
+
- Mutant::Runner::Master
|
85
75
|
max_ifs: 1
|
86
76
|
TooManyInstanceVariables:
|
87
77
|
enabled: true
|
88
78
|
exclude:
|
89
79
|
- Mutant::Mutator # 4 vars
|
90
|
-
- Mutant::Runner
|
80
|
+
- Mutant::Runner::Master # 4 vars
|
81
|
+
- Mutant::Runner::Scheduler # 4 vars
|
91
82
|
max_instance_variables: 3
|
92
83
|
TooManyMethods:
|
93
84
|
enabled: true
|
94
85
|
exclude:
|
95
86
|
- Mutant::CLI
|
96
|
-
- Mutant::Subject
|
97
87
|
- Mutant::Mutator::Node
|
98
|
-
- Mutant::Reporter::CLI
|
99
|
-
- Mutant::Runner
|
100
88
|
- Mutant::Meta::Example::Verification
|
101
89
|
max_methods: 10
|
102
90
|
TooManyStatements:
|
103
91
|
enabled: true
|
104
92
|
exclude:
|
105
|
-
- Mutant#self.singleton_subclass_instance
|
106
|
-
- Mutant::Integration::Rspec#run
|
107
93
|
- Mutant::Isolation::Fork#self.call
|
108
|
-
- Mutant::Reporter::CLI#colorized_diff
|
109
94
|
- Mutant::Reporter::CLI::Printer::EnvProgress#run
|
110
95
|
- Mutant::Reporter::CLI::Printer::Config#run
|
111
|
-
- Mutant::RequireHighjack#infect
|
112
|
-
- Mutant::Rspec::Killer#run
|
113
|
-
- Mutant::Runner#visit_collection
|
114
96
|
- Mutant::Runner#initialize
|
115
|
-
- Mutant::Runner::Mutation#run
|
116
97
|
- Mutant::Zombifier::File#self.find
|
117
|
-
- Mutant::CLI#add_debug_options
|
118
98
|
- Mutant::CLI#add_environment_options
|
119
99
|
max_statements: 7
|
120
100
|
UncommunicativeMethodName:
|
@@ -129,9 +109,7 @@ UncommunicativeMethodName:
|
|
129
109
|
accept: []
|
130
110
|
UncommunicativeModuleName:
|
131
111
|
enabled: true
|
132
|
-
exclude:
|
133
|
-
- Rspec2
|
134
|
-
- Rspec3
|
112
|
+
exclude: []
|
135
113
|
reject:
|
136
114
|
- !ruby/regexp /^.$/
|
137
115
|
- !ruby/regexp /[0-9]$/
|
@@ -160,14 +138,7 @@ UtilityFunction:
|
|
160
138
|
exclude:
|
161
139
|
- Mutant::AST::Sexp#s
|
162
140
|
- Mutant::CLI#reporter
|
163
|
-
- Mutant::Integration::Rspec#
|
164
|
-
- Mutant::
|
165
|
-
- Mutant::Integration::Rspec::Rspec2#full_description
|
166
|
-
- Mutant::Integration::Rspec::Rspec2#new_reporter
|
167
|
-
- Mutant::Integration::Rspec::Rspec3#full_description
|
168
|
-
- Mutant::Meta::Example::Verification#format_mutation
|
169
|
-
- Mutant::Mutation::Evil#success?
|
170
|
-
- Mutant::Mutation::Neutral#success?
|
141
|
+
- Mutant::Integration::Rspec#parse_example
|
142
|
+
- Mutant::Meta::Example::Verification#format_mutation # False positive, its a utility
|
171
143
|
- Mutant::Reporter::CLI::Format::Progressive#new_buffer
|
172
|
-
- Mutant::Runner#run_mutation_test
|
173
144
|
max_helper_calls: 0
|
data/config/rubocop.yml
CHANGED
@@ -3,7 +3,7 @@ inherit_from: ../.rubocop.yml
|
|
3
3
|
# General note about rubocop.
|
4
4
|
# It does NOT allow to silence a specific rule violation.
|
5
5
|
# For that reason I sometimes have to disable a whole cop where
|
6
|
-
# I just tried to whitelist a specific
|
6
|
+
# I just tried to whitelist a specific occurrence.
|
7
7
|
|
8
8
|
|
9
9
|
AllCops:
|
data/lib/mutant.rb
CHANGED
@@ -100,10 +100,15 @@ require 'mutant/ast/nodes'
|
|
100
100
|
require 'mutant/ast/named_children'
|
101
101
|
require 'mutant/ast/node_predicates'
|
102
102
|
require 'mutant/ast/meta'
|
103
|
+
require 'mutant/actor'
|
104
|
+
require 'mutant/actor/receiver'
|
105
|
+
require 'mutant/actor/sender'
|
106
|
+
require 'mutant/actor/mailbox'
|
107
|
+
require 'mutant/actor/actor'
|
108
|
+
require 'mutant/actor/env'
|
103
109
|
require 'mutant/cache'
|
104
110
|
require 'mutant/delegator'
|
105
111
|
require 'mutant/warning_filter'
|
106
|
-
require 'mutant/warning_expectation'
|
107
112
|
require 'mutant/require_highjack'
|
108
113
|
require 'mutant/isolation'
|
109
114
|
require 'mutant/mutator'
|
@@ -196,7 +201,9 @@ require 'mutant/cli'
|
|
196
201
|
require 'mutant/color'
|
197
202
|
require 'mutant/diff'
|
198
203
|
require 'mutant/runner'
|
199
|
-
require 'mutant/runner/
|
204
|
+
require 'mutant/runner/scheduler'
|
205
|
+
require 'mutant/runner/master'
|
206
|
+
require 'mutant/runner/worker'
|
200
207
|
require 'mutant/result'
|
201
208
|
require 'mutant/reporter'
|
202
209
|
require 'mutant/reporter/null'
|
@@ -224,6 +231,7 @@ module Mutant
|
|
224
231
|
reporter: Reporter::CLI.build($stdout),
|
225
232
|
zombie: false,
|
226
233
|
jobs: Mutant.ci? ? CI_DEFAULT_PROCESSOR_COUNT : Parallel.processor_count,
|
234
|
+
actor_env: Mutant::Actor::Env.new(Thread),
|
227
235
|
expected_coverage: 100.0
|
228
236
|
)
|
229
237
|
end # Config
|
data/lib/mutant/actor.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
module Mutant
|
2
|
+
# A minimal actor implementation
|
3
|
+
module Actor
|
4
|
+
|
5
|
+
# Error raised when actor signalling protocol is violated
|
6
|
+
class ProtocolError < RuntimeError
|
7
|
+
end # ProtocolError
|
8
|
+
|
9
|
+
# Undefined message payload
|
10
|
+
Undefined = Class.new do
|
11
|
+
INSPECT = 'Mutant::Actor::Undefined'.freeze
|
12
|
+
|
13
|
+
# Return object inspection
|
14
|
+
#
|
15
|
+
# @return [String]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def inspect
|
20
|
+
INSPECT
|
21
|
+
end
|
22
|
+
end.new
|
23
|
+
|
24
|
+
# Message being exchanged between actors
|
25
|
+
class Message
|
26
|
+
include Concord::Public.new(:type, :payload)
|
27
|
+
|
28
|
+
# Return new message
|
29
|
+
#
|
30
|
+
# @param [Symbol] type
|
31
|
+
# @param [Object] payload
|
32
|
+
#
|
33
|
+
# @return [Message]
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
#
|
37
|
+
def self.new(_type, _payload = Undefined)
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
end # Message
|
42
|
+
|
43
|
+
# Bindin to others actors sender for simple RPC
|
44
|
+
class Binding
|
45
|
+
include Concord.new(:actor, :other)
|
46
|
+
|
47
|
+
# Send message and wait for reply
|
48
|
+
#
|
49
|
+
# @param [Symbol] type
|
50
|
+
#
|
51
|
+
# @return [Object]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
#
|
55
|
+
def call(type)
|
56
|
+
other.call(Message.new(type, actor.sender))
|
57
|
+
message = actor.receiver.call
|
58
|
+
fail ProtocolError, "Expected #{type} but got #{message.type}" unless type.equal?(message.type)
|
59
|
+
message.payload
|
60
|
+
end
|
61
|
+
|
62
|
+
end # Binding
|
63
|
+
end # Actor
|
64
|
+
end # Mutant
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Actor
|
3
|
+
# Actor object available to acting threads
|
4
|
+
class Actor
|
5
|
+
include Concord.new(:thread, :mailbox)
|
6
|
+
|
7
|
+
# Initialize object
|
8
|
+
#
|
9
|
+
# @return [undefined]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def initialize(*)
|
14
|
+
super
|
15
|
+
@sender = mailbox.sender(thread)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return sender to this actor
|
19
|
+
#
|
20
|
+
# @return [Sender]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
attr_reader :sender
|
25
|
+
|
26
|
+
# Return receiver for messages to this actor
|
27
|
+
#
|
28
|
+
# @return [Receiver]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
def receiver
|
33
|
+
mailbox.receiver
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return binding for RPC to other actors
|
37
|
+
#
|
38
|
+
# @param [Actor::Sender] other
|
39
|
+
#
|
40
|
+
# @return [Binding]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
def bind(other)
|
45
|
+
Binding.new(self, other)
|
46
|
+
end
|
47
|
+
|
48
|
+
end # Actor
|
49
|
+
end # Actor
|
50
|
+
end # Mutant
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Actor
|
3
|
+
# Actor root environment
|
4
|
+
class Env
|
5
|
+
include Concord.new(:thread_root)
|
6
|
+
|
7
|
+
# Spawn a new actor executing block
|
8
|
+
#
|
9
|
+
# @return [Actor::Sender]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def spawn
|
14
|
+
mailbox = Mailbox.new
|
15
|
+
|
16
|
+
thread = thread_root.new do
|
17
|
+
yield mailbox.actor(thread_root.current)
|
18
|
+
end
|
19
|
+
|
20
|
+
mailbox.sender(thread)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return an private actor for current thread
|
24
|
+
#
|
25
|
+
# @return [Actor::Private]
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
#
|
29
|
+
def current
|
30
|
+
Mailbox.new.actor(thread_root.current)
|
31
|
+
end
|
32
|
+
|
33
|
+
end # Env
|
34
|
+
end # Actor
|
35
|
+
end # Mutant
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Actor
|
3
|
+
# Unbound mailbox
|
4
|
+
class Mailbox
|
5
|
+
|
6
|
+
# Initialize new unbound mailbox
|
7
|
+
#
|
8
|
+
# @return [undefined]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
def initialize
|
13
|
+
@mutex = Mutex.new
|
14
|
+
@messages = []
|
15
|
+
@receiver = Receiver.new(@mutex, @messages)
|
16
|
+
freeze
|
17
|
+
end
|
18
|
+
|
19
|
+
# Return receiver
|
20
|
+
#
|
21
|
+
# @return [Receiver]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
#
|
25
|
+
attr_reader :receiver
|
26
|
+
|
27
|
+
# Return actor that is able to read mailbox
|
28
|
+
#
|
29
|
+
# @param [Thread] thread
|
30
|
+
#
|
31
|
+
# @return [Actor]
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
def actor(thread)
|
36
|
+
Actor.new(thread, self)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return sender to mailbox
|
40
|
+
#
|
41
|
+
# @param [Thread] thread
|
42
|
+
#
|
43
|
+
# @return [Sender]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
#
|
47
|
+
def sender(thread)
|
48
|
+
Sender.new(thread, @mutex, @messages)
|
49
|
+
end
|
50
|
+
|
51
|
+
end # Mailbox
|
52
|
+
end # Actor
|
53
|
+
end # Mutant
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Actor
|
3
|
+
# Receiver side of an actor
|
4
|
+
class Receiver
|
5
|
+
include Concord.new(:mutex, :mailbox)
|
6
|
+
|
7
|
+
# Receives a message, blocking
|
8
|
+
#
|
9
|
+
# @return [Object]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def call
|
14
|
+
2.times do
|
15
|
+
message = try_blocking_receive
|
16
|
+
return message unless message.equal?(Undefined)
|
17
|
+
end
|
18
|
+
fail ProtocolError
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Try a blocking receive
|
24
|
+
#
|
25
|
+
# @return [Undefined]
|
26
|
+
# if there is no message yet
|
27
|
+
#
|
28
|
+
# @return [Object]
|
29
|
+
# if there is a message
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
#
|
33
|
+
def try_blocking_receive
|
34
|
+
mutex.lock
|
35
|
+
if mailbox.empty?
|
36
|
+
mutex.unlock
|
37
|
+
Thread.stop
|
38
|
+
Undefined
|
39
|
+
else
|
40
|
+
mailbox.shift.tap do
|
41
|
+
mutex.unlock
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end # Receiver
|
47
|
+
end # Actor
|
48
|
+
end # Mutant
|