3 docSlot("description", "Returns a description of the receiver as a String.")
7 s
:= self target
type .. " " .. m name
8 s
alignLeft(36) .. " " .. m label lastPathComponent
.. " " .. m lineNumber
11 delegateTo
:= method(target
, altSender
,
12 call
relayStopStatus(target
doMessage(self message clone setNext
, altSender
ifNilEval(self sender)))
15 delegateToMethod
:= method(target
, methodName
,
16 call
relayStopStatus(target
doMessage(self message clone setNext
setName(methodName
), self sender))
19 evalArgs
:= method(self message argsEvaluatedIn(sender))
20 hasArgs
:= method(argCount
> 0)
21 argCount
:= method(self message argCount
)
24 Message description
:= method(
25 self name
alignLeft(36) .. self label lastPathComponent
.. " " .. self lineNumber
28 Scheduler
:= Object clone do(
29 docSlot("yieldingCoros", "The List of yielding Coroutine objects.")
30 docSlot("setYieldingCoros(aListOfCoros)", "Sets the list of yielding Coroutine objects.")
31 newSlot("yieldingCoros", List clone)
33 docSlot("timers", "The List of active timers.")
34 docSlot("setTimers(aListOfTimers)", "Sets the list of active timers.")
35 newSlot("timers", List clone)
37 docSlot("currentCoroutine", "Returns the currently running coroutine.")
38 currentCoroutine
:= method(Coroutine currentCoroutine
)
42 docSlot("stackSize", "Stack size allocated for each new coroutine. Coroutines will automatically chain themselves as need if more stack space is required.")
43 newSlot("stackSize", 128000) // PPC needs
128k
for current parser
45 docSlot("exception", "Returns the current exception or nil if there is none.")
48 docSlot("parentCoroutine", "Returns the parent coroutine this one was chained from or nil if it wasn't chained. When a Coroutine ends, it will attempt to resume it's parent.")
49 newSlot("parentCoroutine")
51 docSlot("runTarget", "The object which the coroutine will send a message to when it starts.")
54 docSlot("runLocals", "The locals object in whose context the coroutine will send it's run message.")
57 docSlot("runMessage", "The message to send to the runTarget when the coroutine starts.")
60 docSlot("result", "The result set when the coroutine ends.")
63 docSlot("label", "A label slot useful for debugging purposes.")
66 docSlot("inException", "Set to true when processing an exception in the coroutine.")
67 newSlot("inException", false
)
69 docSlot("yieldingCoros", "Reference to Scheduler yieldingCoros.")
70 newSlot("yieldingCoros", Scheduler yieldingCoros
)
73 label
:= method(self uniqueId)
74 setLabel
:= method(s
, self label
= s
.. "_" .. self uniqueId)
76 showYielding
:= method(s
,
77 writeln(" ", label
, " ", s
)
78 yieldingCoros
foreach(v
, writeln(" ", v
uniqueId))
81 isYielding
:= method(yieldingCoros
contains(self))
84 //showYielding("yield")
85 //writeln("Coro ", self uniqueId, " yielding - yieldingCoros = ", yieldingCoros size
)
86 if(yieldingCoros isEmpty
, return)
87 yieldingCoros
append(self)
88 next
:= yieldingCoros removeFirst
89 if(next
== self, return)
90 //writeln(Scheduler currentCoroutine label
, " yield - ", next label
, " resume")
94 resumeLater
:= method(
95 yieldingCoros
remove(self)
96 yieldingCoros
atInsert(0, self)
97 //writeln(self label
, " resumeLater")
101 yieldingCoros
remove(self)
103 next
:= yieldingCoros removeFirst
106 //Exception raise("Scheduler: nothing left to resume so we are exiting")
107 writeln("Scheduler: nothing left to resume so we are exiting")
112 yieldingCoros
remove(self)
116 yieldCurrentAndResumeSelf
:= method(
117 //showYielding("yieldCurrentAndResumeSelf")
118 yieldingCoros
remove(self)
119 isCurrent
ifFalse(resume)
122 pauseCurrentAndResumeSelf
:= method(
123 //showYielding("pauseCurrentAndResumeSelf")
124 yieldingCoros
remove(self)
125 isCurrent
ifFalse(resume)
128 typeId
:= method(self type .. "_0x" .. self uniqueId asString
toBase(16))
130 ignoredCoroutineMethodNames
:= list("setResult", "main", "pauseCurrentAndResumeSelf", "resumeParentCoroutine", "raiseException")
134 stack
selectInPlace(v
, Object argIsCall(getSlot("v"))) reverse
135 stack
selectInPlace(v
,
136 (v target
type == "Coroutine" and ignoredCoroutineMethodNames
contains(v
message name
)) not
138 stack
foreach(i
, v
, if(v target
type == "Importer" and v
message name
== "import", stack
sliceInPlace(i
+1); break) )
139 stack
:= stack unique
143 backTraceString
:= method(
144 if(Coroutine inException
,
145 writeln("\n", exception
type, ": ", exception error
, "\n\n")
146 writeln("Coroutine Exception loop detected");
149 Coroutine
setInException(true
)
150 buf
:= Sequence
clone
152 if(getSlot("CGI") != nil
and CGI isInWebScript
, buf
appendSeq("<pre>"))
154 if(exception
, buf
appendSeq("\n ", exception
type, ": ", exception error
, "\n"))
156 if(callStack size
> 0) then(
157 buf
appendSeq(" ---------\n")
159 if(exception
and exception caughtMessage
,
160 buf
appendSeq(" ", exception caughtMessage description
, "\n")
165 if(exception
and exception originalCall
,
166 index
:= frames
indexOf(exception originalCall
)
168 frames
sliceInPlace(index
)
173 buf
appendSeq(" ", v description
, "\n")
177 buf
appendSeq(" ---------\n")
178 m
:= exception caughtMessage
179 buf
appendSeq(" message '" .. m name
.. "' in '" .. m label
.. "' on line " .. m lineNumber
.. "\n")
183 Coroutine
setInException(false
)
187 showStack
:= method(write(backTraceString
))
189 resumeParentCoroutine
:= method(
190 if(parentCoroutine
, parentCoroutine pauseCurrentAndResumeSelf
)
194 setResult(self getSlot("runTarget") doMessage(runMessage
, self getSlot("runLocals")))
195 resumeParentCoroutine
199 raiseException
:= method(e
,
201 resumeParentCoroutine
205 Object wait := method(s
,
206 endDate
:= Date clone now
+ Duration clone setSeconds(s
)
207 loop(endDate isPast
ifTrue(break); yield)
211 codeOfLength
:= method(length
,
213 if (c size
< length
, c
, c
slice(0, length
) .. "...") asMutable
replaceSeq("\n", ";")
216 asStackEntry
:= method(
217 label
:= label lastPathComponent fileName
218 label
alignLeft(19) .. lineNumber asString
alignLeft(7) .. name
224 coro
:= Coroutine
clone
225 coro
setParentCoroutine(Scheduler currentCoroutine
)
226 coro
setRunTarget(call
sender)
227 coro
setRunLocals(call
sender)
228 coro
setRunMessage(call
argAt(0))
230 if(coro exception
, coro exception
, nil
)
234 coro
:= Coroutine
clone
235 coro
setRunTarget(call
sender)
236 coro
setRunLocals(call
sender)
237 coro
setRunMessage(call
argAt(0))
242 coro
:= Coroutine
clone
243 coro
setRunTarget(call
sender)
244 coro
setRunLocals(call
sender)
245 coro
setRunMessage(call
argAt(0))
246 Coroutine yieldingCoros
atInsert(0, Scheduler currentCoroutine
)
251 coroDoLater
:= method(
252 coro
:= Coroutine
clone
253 coro
setRunTarget(self)
254 coro
setRunLocals(call
sender)
255 coro
setRunMessage(call
argAt(0))
256 Coroutine yieldingCoros
atInsert(0, coro
)
261 coro
:= Coroutine
clone
262 coro
setRunTarget(self)
263 coro
setRunLocals(call
sender)
264 coro
setRunMessage(call
argAt(0))
268 currentCoro
:= method(Coroutine currentCoroutine
)
280 newSlot("caughtMessage")
281 newSlot("nestedException")
282 newSlot("originalCall")
284 raise := method(error
, nestedException
,
285 coro
:= Scheduler currentCoroutine
286 coro
raiseException(self clone setError(error
) setCoroutine(coro
) setNestedException(nestedException
))
289 raiseFrom
:= method(originalCall
, error
, nestedException
,
290 coro
:= Scheduler currentCoroutine
291 coro
raiseException(self clone setError(error
) setCoroutine(coro
) setNestedException(nestedException
) setOriginalCall(originalCall
))
294 catch := method(exceptionProto
,
295 if (self isKindOf(exceptionProto
), call
evalArgAt(1); nil
, self)
298 pass := method(Scheduler currentCoroutine
raiseException(self))
303 writeln("Nested Exception: '", nestedException
, "'")
304 nestedException showStack