@@ -5,7 +5,7 @@ Although, `Promises.future { 1 + 1 }` is better suited for that purpose.
55
66``` ruby
77actor = Concurrent ::ErlangActor .spawn (:on_thread , name: ' addition' ) { 1 + 1 }
8- # => #<Concurrent::ErlangActor::Pid:0x000002 addition>
8+ # => #<Concurrent::ErlangActor::Pid:0x000002 addition terminated normally with 2 >
99actor.terminated.value! # => 2
1010```
1111
@@ -25,7 +25,7 @@ actor = Concurrent::ErlangActor.spawn(:on_thread, name: 'sum') do
2525 end
2626 sum
2727end
28- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
28+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
2929```
3030
3131The actor can be either told a message asynchronously,
@@ -34,12 +34,12 @@ or asked. The ask method will block until actor replies.
3434``` ruby
3535# tell returns immediately returning the actor
3636actor.tell(1 ).tell(1 )
37- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
37+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
3838# blocks, waiting for the answer
3939actor.ask 10 # => 12
4040# stop the actor
4141actor.tell :done
42- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
42+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
4343actor.terminated.value! # => 12
4444```
4545
@@ -49,9 +49,9 @@ Simplest message receive.
4949
5050``` ruby
5151actor = Concurrent ::ErlangActor .spawn (:on_thread ) { receive }
52- # => #<Concurrent::ErlangActor::Pid:0x000004>
52+ # => #<Concurrent::ErlangActor::Pid:0x000004 running >
5353actor.tell :m
54- # => #<Concurrent::ErlangActor::Pid:0x000004>
54+ # => #<Concurrent::ErlangActor::Pid:0x000004 running >
5555actor.terminated.value! # => :m
5656```
5757
@@ -60,16 +60,133 @@ because if no block is given it will use a default block `{ |v| v }`
6060
6161``` ruby
6262actor = Concurrent ::ErlangActor .spawn (:on_pool ) { receive { |v | v } }
63- # => #<Concurrent::ErlangActor::Pid:0x000005>
63+ # => #<Concurrent::ErlangActor::Pid:0x000005 running >
6464# can simply be following
6565actor = Concurrent ::ErlangActor .spawn (:on_pool ) { receive }
66- # => #<Concurrent::ErlangActor::Pid:0x000006>
66+ # => #<Concurrent::ErlangActor::Pid:0x000006 running >
6767actor.tell :m
68- # => #<Concurrent::ErlangActor::Pid:0x000006>
68+ # => #<Concurrent::ErlangActor::Pid:0x000006 running >
6969actor.terminated.value! # => :m
7070```
7171
72- TBA
72+ The received message type can be limited.
73+
74+ ``` ruby
75+ Concurrent ::ErlangActor .
76+ spawn (:on_thread ) { receive(Numeric ).succ }.
77+ tell(' junk' ). # ignored message
78+ tell(42 ).
79+ terminated.value! # => 43
80+ ```
81+
82+ On pool it requires a block.
83+
84+ ``` ruby
85+ Concurrent ::ErlangActor .
86+ spawn (:on_pool ) { receive(Numeric ) { |v | v.succ } }.
87+ tell(' junk' ). # ignored message
88+ tell(42 ).
89+ terminated.value! # => 43
90+ ```
91+
92+ By the way, the body written for on pool actor will work for on thread actor
93+ as well.
94+
95+ ``` ruby
96+ Concurrent ::ErlangActor .
97+ spawn (:on_thread ) { receive(Numeric ) { |v | v.succ } }.
98+ tell(' junk' ). # ignored message
99+ tell(42 ).
100+ terminated.value! # => 43
101+ ```
102+
103+ The ` receive ` method can be also used to dispatch based on the received message.
104+
105+ ``` ruby
106+ actor = Concurrent ::ErlangActor .spawn (:on_thread ) do
107+ while true
108+ receive(on(Symbol ) { |s | reply s.to_s },
109+ on(And [Numeric , -> v { v >= 0 }]) { |v | reply v.succ },
110+ # put last works as else
111+ on(ANY ) do |v |
112+ reply :bad_message
113+ terminate [:bad_message , v]
114+ end )
115+ end
116+ end
117+ # => #<Concurrent::ErlangActor::Pid:0x000007 running>
118+ actor.ask 1 # => 2
119+ actor.ask 2 # => 3
120+ actor.ask :value # => "value"
121+ # this malformed message will terminate the actor
122+ actor.ask - 1 # => :bad_message
123+ # the actor is no longer alive, so ask fails
124+ actor.ask " junk" rescue $!
125+ # => #<Concurrent::ErlangActor::NoActor: #<Concurrent::ErlangActor::Pid:0x000007 terminated because of [:bad_message, -1]>>
126+ actor.terminated.result # => [false, nil, [:bad_message, -1]]
127+ ```
128+
129+ And a same thing for the actor on pool.
130+ Since it cannot loop it will call the body method repeatedly.
131+
132+ ``` ruby
133+ module Behaviour
134+ def body
135+ receive(on(Symbol ) do |s |
136+ reply s.to_s
137+ body # call again
138+ end ,
139+ on(And [Numeric , -> v { v >= 0 }]) do |v |
140+ reply v.succ
141+ body # call again
142+ end ,
143+ # put last works as else
144+ on(ANY ) do |v |
145+ reply :bad_message
146+ terminate [:bad_message , v]
147+ end )
148+ end
149+ end # => :body
150+
151+ actor = Concurrent ::ErlangActor .spawn (:on_pool , environment: Behaviour ) { body }
152+ # => #<Concurrent::ErlangActor::Pid:0x000008 running>
153+ actor.ask 1 # => 2
154+ actor.ask 2 # => 3
155+ actor.ask :value # => "value"
156+ # this malformed message will terminate the actor
157+ actor.ask - 1 # => :bad_message
158+ # the actor is no longer alive, so ask fails
159+ actor.ask " junk" rescue $!
160+ # => #<Concurrent::ErlangActor::NoActor: #<Concurrent::ErlangActor::Pid:0x000008 terminated because of [:bad_message, -1]>>
161+ actor.terminated.result # => [false, nil, [:bad_message, -1]]
162+ ```
163+
164+ Since the behavior is stable in this case we can simplify with the ` :keep ` option
165+ that will keep the receive rules until another receive is called
166+ replacing the kept rules.
167+
168+ ``` ruby
169+ actor = Concurrent ::ErlangActor .spawn (:on_pool ) do
170+ receive(on(Symbol ) { |s | reply s.to_s },
171+ on(And [Numeric , -> v { v >= 0 }]) { |v | reply v.succ },
172+ # put last works as else
173+ on(ANY ) do |v |
174+ reply :bad_message
175+ terminate [:bad_message , v]
176+ end ,
177+ keep: true )
178+ end
179+ # => #<Concurrent::ErlangActor::Pid:0x000009 running>
180+ actor.ask 1 # => 2
181+ actor.ask 2 # => 3
182+ actor.ask :value # => "value"
183+ # this malformed message will terminate the actor
184+ actor.ask - 1 # => :bad_message
185+ # the actor is no longer alive, so ask fails
186+ actor.ask " junk" rescue $!
187+ # => #<Concurrent::ErlangActor::NoActor: #<Concurrent::ErlangActor::Pid:0x000009 terminated because of [:bad_message, -1]>>
188+ actor.terminated.result # => [false, nil, [:bad_message, -1]]
189+ ```
73190
74191### Actor types
75192
@@ -93,7 +210,7 @@ Let's have a look at how the bodies of actors differ between the types:
93210
94211``` ruby
95212ping = Concurrent ::ErlangActor .spawn (:on_thread ) { reply receive }
96- # => #<Concurrent::ErlangActor::Pid:0x000007 >
213+ # => #<Concurrent::ErlangActor::Pid:0x00000a running >
97214ping.ask 42 # => 42
98215```
99216
@@ -107,7 +224,7 @@ after the message is received has to be provided.
107224
108225``` ruby
109226ping = Concurrent ::ErlangActor .spawn (:on_pool ) { receive { |m | reply m } }
110- # => #<Concurrent::ErlangActor::Pid:0x000008 >
227+ # => #<Concurrent::ErlangActor::Pid:0x00000b running >
111228ping.ask 42 # => 42
112229```
113230
@@ -144,10 +261,11 @@ actor = Concurrent::ErlangActor.spawn(:on_thread) do
144261 trap # equivalent of process_flag(trap_exit, true)
145262 receive
146263end
147- # => #<Concurrent::ErlangActor::Pid:0x000009 >
264+ # => #<Concurrent::ErlangActor::Pid:0x00000c running >
148265actor.terminated.value!
149- # => #<Concurrent::ErlangActor::Exit:0x00000a
150- # @from=#<Concurrent::ErlangActor::Pid:0x00000b>,
266+ # => #<Concurrent::ErlangActor::Exit:0x00000d
267+ # @from=
268+ # #<Concurrent::ErlangActor::Pid:0x00000e terminated because of err>,
151269# @link_terminated=true,
152270# @reason=:err>
153271```
@@ -159,3 +277,6 @@ actor.terminated.value!
159277* Back pressure with bounded mailbox
160278* _ op methods
161279* types of actors
280+ * always use timeout
281+ * drop and log unrecognized messages, or just terminate
282+ * Functions module
0 commit comments