@@ -5,7 +5,7 @@ Although, `Promises.future { 1 + 1 }` is better suited for that purpose.
5
5
6
6
``` ruby
7
7
actor = 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 >
9
9
actor.terminated.value! # => 2
10
10
```
11
11
@@ -25,7 +25,7 @@ actor = Concurrent::ErlangActor.spawn(:on_thread, name: 'sum') do
25
25
end
26
26
sum
27
27
end
28
- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
28
+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
29
29
```
30
30
31
31
The actor can be either told a message asynchronously,
@@ -34,12 +34,12 @@ or asked. The ask method will block until actor replies.
34
34
``` ruby
35
35
# tell returns immediately returning the actor
36
36
actor.tell(1 ).tell(1 )
37
- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
37
+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
38
38
# blocks, waiting for the answer
39
39
actor.ask 10 # => 12
40
40
# stop the actor
41
41
actor.tell :done
42
- # => #<Concurrent::ErlangActor::Pid:0x000003 sum>
42
+ # => #<Concurrent::ErlangActor::Pid:0x000003 sum running >
43
43
actor.terminated.value! # => 12
44
44
```
45
45
@@ -49,9 +49,9 @@ Simplest message receive.
49
49
50
50
``` ruby
51
51
actor = Concurrent ::ErlangActor .spawn (:on_thread ) { receive }
52
- # => #<Concurrent::ErlangActor::Pid:0x000004>
52
+ # => #<Concurrent::ErlangActor::Pid:0x000004 running >
53
53
actor.tell :m
54
- # => #<Concurrent::ErlangActor::Pid:0x000004>
54
+ # => #<Concurrent::ErlangActor::Pid:0x000004 running >
55
55
actor.terminated.value! # => :m
56
56
```
57
57
@@ -60,16 +60,133 @@ because if no block is given it will use a default block `{ |v| v }`
60
60
61
61
``` ruby
62
62
actor = Concurrent ::ErlangActor .spawn (:on_pool ) { receive { |v | v } }
63
- # => #<Concurrent::ErlangActor::Pid:0x000005>
63
+ # => #<Concurrent::ErlangActor::Pid:0x000005 running >
64
64
# can simply be following
65
65
actor = Concurrent ::ErlangActor .spawn (:on_pool ) { receive }
66
- # => #<Concurrent::ErlangActor::Pid:0x000006>
66
+ # => #<Concurrent::ErlangActor::Pid:0x000006 running >
67
67
actor.tell :m
68
- # => #<Concurrent::ErlangActor::Pid:0x000006>
68
+ # => #<Concurrent::ErlangActor::Pid:0x000006 running >
69
69
actor.terminated.value! # => :m
70
70
```
71
71
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
+ ```
73
190
74
191
### Actor types
75
192
@@ -93,7 +210,7 @@ Let's have a look at how the bodies of actors differ between the types:
93
210
94
211
``` ruby
95
212
ping = Concurrent ::ErlangActor .spawn (:on_thread ) { reply receive }
96
- # => #<Concurrent::ErlangActor::Pid:0x000007 >
213
+ # => #<Concurrent::ErlangActor::Pid:0x00000a running >
97
214
ping.ask 42 # => 42
98
215
```
99
216
@@ -107,7 +224,7 @@ after the message is received has to be provided.
107
224
108
225
``` ruby
109
226
ping = Concurrent ::ErlangActor .spawn (:on_pool ) { receive { |m | reply m } }
110
- # => #<Concurrent::ErlangActor::Pid:0x000008 >
227
+ # => #<Concurrent::ErlangActor::Pid:0x00000b running >
111
228
ping.ask 42 # => 42
112
229
```
113
230
@@ -144,10 +261,11 @@ actor = Concurrent::ErlangActor.spawn(:on_thread) do
144
261
trap # equivalent of process_flag(trap_exit, true)
145
262
receive
146
263
end
147
- # => #<Concurrent::ErlangActor::Pid:0x000009 >
264
+ # => #<Concurrent::ErlangActor::Pid:0x00000c running >
148
265
actor.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>,
151
269
# @link_terminated=true,
152
270
# @reason=:err>
153
271
```
@@ -159,3 +277,6 @@ actor.terminated.value!
159
277
* Back pressure with bounded mailbox
160
278
* _ op methods
161
279
* types of actors
280
+ * always use timeout
281
+ * drop and log unrecognized messages, or just terminate
282
+ * Functions module
0 commit comments