/
Actor.io
199 lines (174 loc) · 5.63 KB
/
Actor.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
Notifier := Object clone do(
addListener := method(listener,
if(self getSlot("listeners") == nil, self listeners := List clone)
listeners append(listener);
self
)
removeListener := method(obj,
if(?listeners, listeners remove(obj))
)
notifyListeners := method(
if(?listeners,
listeners foreach(l,
if(l hasSlot(call argAt(0) name),
stopStatus(l doMessage(call argAt(0), call sender))
)
)
)
)
)
FutureProxy := Object clone do(
with := method(future,
p := self clone
p _future := future
p forward := self getSlot("_forward")
p _become := self getSlot("become")
//p xyz := method(_future writeln("XYZ!"))
p type := "FutureProxy"
p removeAllProtos
p
)
_forward := method(
//_future writeln("FutureProxy forward ", call message)
_future waitOnResult
self doMessage(call message, call sender)
)
)
Future := Object clone do(
newSlot("runTarget")
newSlot("runMessage")
newSlot("waitingCoros")
futureProxy := method(
self waitingCoros := List clone
self proxy := FutureProxy with(self)
proxy
)
setResult := method(r,
if(self hasSlot("proxy"),
proxy _become(getSlot("r"))
if(waitingCoros, waitingCoros foreach(resumeLater))
//notifyListeners(futureReady(self))
)
)
waitOnResult := method(
waitingCoros append(Scheduler currentCoroutine)
Scheduler currentCoroutine pause
)
)
/*
Object do(
actorIsRunning := method(
//writeln("self hasLocalSlot(actorCoroutine) = ", self hasLocalSlot("actorCoroutine"))
if(self hasLocalSlot("actorCoroutine"),
writeln("actorQueue size = ", actorQueue size)
writeln("Coroutine yieldingCoros contains(actorCoroutine) = ", Coroutine yieldingCoros contains(actorCoroutine))
writeln("Coroutine currentCoroutine == actorCoroutine = ", Coroutine currentCoroutine == actorCoroutine)
)
self hasLocalSlot("actorCoroutine") and(actorQueue size > 0) and(
Coroutine yieldingCoros contains(actorCoroutine) or(Coroutine currentCoroutine == actorCoroutine)
)
)
actorIsPaused := method(self hasLocalSlot("actorCoroutine") and(actorQueue size > 0))
actorPause := method(if(actorIsPaused, actorCoroutine pause))
actorResume := method(if(actorIsPaused, actorCoroutine resumeLater))
)
*/
Object do(
/*doc Object yield
Yields to another coroutine. Does nothing if yieldingCoros queue is empty.
<br/>
See Coroutine documentation for more details.
*/
yield := method(Coroutine currentCoroutine yield)
/*doc Object pause
Removes current coroutine from the yieldingCoros queue and
yields to another coro. Exits if no coros left.
<br/>
See Coroutine documentation for more details.
*/
pause := method(Coroutine currentCoroutine pause)
//doc Object actorRun Starts actor mode if not started already. Basically, sets actorProcessQueue for later execution.
actorRun := method(
if(self hasLocalSlot("actorCoroutine"),
if(actorQueue size == 0, self actorCoroutine resumeLater)
,
self actorQueue := List clone
// need to yield in coroDo to allow future to be added to queue
//self actorCoroutine := self coroDo(yield; actorProcessQueue) // coroDo refs stack!
self actorCoroutine := Coroutine clone //setStackSize(20000)
actorCoroutine setRunTarget(self)
actorCoroutine setRunLocals(self)
actorCoroutine setRunMessage(message(actorProcessQueue))
Coroutine yieldingCoros atInsert(0, actorCoroutine)
//Coroutine yieldingCoros append(actorCoroutine)
)
)
//doc Object actorProcessQueue Processes each message in a queue, yielding between each message.
actorProcessQueue := method(
//writeln(self type, "_", self uniqueId, " actorProcessQueue")
/*
if(Coroutine currentCoroutine isIdenticalTo(self actorCoroutine) not,
writeln("actorProcessQueue called from coro ", Coroutine currentCoroutine uniqueId, " instead of ", actorCoroutine uniqueId)
System exit
)
*/
loop(
while(future := actorQueue first,
e := try(
future setResult(self doMessage(future runMessage))
//stopStatus(future setResult(self doMessage(future runMessage)))
)
actorQueue removeFirst
if(e, handleActorException(e))
if(actorQueue first, yield)
)
/*
if(Coroutine currentCoroutine isIdenticalTo(self actorCoroutine) not,
writeln("actorProcessQueue1 called from coro ", Coroutine currentCoroutine uniqueId, " instead of ", self actorCoroutine uniqueId)
System exit
)
*/
self actorCoroutine pause
)
)
/*doc Object handleActorException(exception)
Callback for handling exceptions during asynchronous message processing.
<br/>
Default value: method(e, e showStack)
*/
handleActorException := method(e, e showStack)
/*doc Object @
Sends asynchronous message to an object, returns a FutureProxy.
<br/>
Caller coroutine is paused when proxy is accessed (i.e. message is sent)
till result is ready. Proxy will become an actual result when it is ready.
<br/>
See IoGuide for more information.
<br/>
Usage: obj @someMethod(a, b, c)
*/
setSlot("@", method(
//writeln("@ ", call argAt(0))
m := call argAt(0) asMessageWithEvaluatedArgs(call sender)
f := Future clone setRunTarget(self) setRunMessage(m)
self actorRun
self actorQueue append(f)
f futureProxy
))
futureSend := getSlot("@")
/*doc Object @@
Same as Object @, but returns nil instead of FutureProxy.
<br/>
Might be useful in a command line or as a last expression in a block/method when
you don't want to return a future.
*/
setSlot("@@", method(
//writeln(self type , "_", self uniqueId, " @@", call argAt(0)) //, " ", call argAt(0) label)
m := call argAt(0) asMessageWithEvaluatedArgs(call sender)
f := Future clone setRunTarget(self) setRunMessage(m)
self actorRun
self actorQueue append(f)
nil
))
asyncSend := getSlot("@@")
)