1 ! Copyright (C) 2004, 2009 Slava Pestov.
2 ! Copyright (C) 2005 Mackenzie Straight.
3 ! See http://factorcode.org/license.txt for BSD license.
4 USING: arrays hashtables heaps kernel kernel.private math
5 namespaces sequences vectors continuations continuations.private
6 dlists assocs system combinators combinators.private init boxes
7 accessors math.order deques strings quotations fry ;
12 ! (set-context) and (start-context) are sub-primitives, but
13 ! we don't want them inlined into callers since their behavior
14 ! depends on what frames are on the callstack
15 : start-context ( obj quot: ( obj -- * ) -- ) (start-context) ;
16 : set-context ( context -- ) (set-context) ;
20 SYMBOL: initial-thread
24 { quot callable initial: [ ] }
25 { exit-handler callable initial: [ ] }
34 : self ( -- thread ) 63 special-object ; inline
36 ! Thread-local storage
37 : tnamespace ( -- assoc )
38 self variables>> [ H{ } clone dup self (>>variables) ] unless* ;
40 : tget ( key -- value )
43 : tset ( value key -- )
46 : tchange ( key quot -- )
47 tnamespace swap change-at ; inline
49 : threads ( -- assoc ) 64 special-object ;
51 : thread ( id -- thread ) threads at ;
53 : thread-registered? ( thread -- ? )
56 ERROR: already-stopped thread ;
58 : check-unregistered ( thread -- thread )
59 dup thread-registered? [ already-stopped ] when ;
61 ERROR: not-running thread ;
63 : check-registered ( thread -- thread )
64 dup thread-registered? [ not-running ] unless ;
68 : register-thread ( thread -- )
69 check-unregistered dup id>> threads set-at ;
71 : unregister-thread ( thread -- )
72 check-registered id>> threads delete-at ;
74 : set-self ( thread -- ) 63 set-special-object ; inline
78 : new-thread ( quot name class -- thread )
83 <box> >>continuation ; inline
85 : <thread> ( quot name -- thread )
88 : run-queue ( -- dlist ) 65 special-object ;
90 : sleep-queue ( -- heap ) 66 special-object ;
92 : resume ( thread -- )
94 check-registered run-queue push-front ;
96 : resume-now ( thread -- )
98 check-registered run-queue push-back ;
100 : resume-with ( obj thread -- )
102 check-registered 2array run-queue push-front ;
104 : sleep-time ( -- nanos/f )
106 { [ run-queue deque-empty? not ] [ 0 ] }
107 { [ sleep-queue heap-empty? ] [ f ] }
108 [ sleep-queue heap-peek nip nano-count [-] ]
115 : schedule-sleep ( thread dt -- )
116 [ check-registered dup ] dip sleep-queue heap-push*
119 : expire-sleep? ( heap -- ? )
121 [ drop f ] [ heap-peek nip nano-count <= ] if ;
123 : expire-sleep ( thread -- )
124 f >>sleep-entry resume ;
126 : expire-sleep-loop ( -- )
128 [ dup expire-sleep? ]
129 [ dup heap-pop drop expire-sleep ]
133 : start ( namestack thread -- * )
140 self quot>> [ call stop ] call-clear
141 ] (( namestack thread -- * )) call-effect-unsafe ;
145 : no-runnable-threads ( -- * )
146 ! We should never be in a state where the only threads
147 ! are sleeping; the I/O wait thread is always runnable.
148 ! However, if it dies, we handle this case
151 ! And if sleep-time outputs f, there are no sleeping
152 ! threads either... so WTF.
154 { [ dup not ] [ drop die ] }
155 { [ dup 0 = ] [ drop ] }
159 : (next) ( arg thread -- * )
163 continuation>> box> continue-with
170 run-queue dup deque-empty? [
171 drop no-runnable-threads
173 pop-back dup array? [ first2 ] [ f swap ] if (next)
179 self [ exit-handler>> call( -- ) ] [ unregister-thread ] bi next ;
181 : suspend ( quot state -- obj )
183 [ [ self swap call ] dip self (>>state) ] dip
184 self continuation>> >box
186 ] callcc1 2nip ; inline
188 : yield ( -- ) [ resume ] f suspend drop ;
190 GENERIC: sleep-until ( n/f -- )
192 M: integer sleep-until
193 '[ _ schedule-sleep ] "sleep" suspend drop ;
196 drop [ drop ] "interrupt" suspend drop ;
198 GENERIC: sleep ( dt -- )
201 >integer nano-count + sleep-until ;
203 : interrupt ( thread -- )
205 dup sleep-entry>> [ sleep-queue heap-delete ] when*
210 : (spawn) ( thread -- )
211 [ register-thread ] [ namestack swap resume-with ] bi ;
213 : spawn ( quot name -- thread )
214 <thread> [ (spawn) ] keep ;
216 : spawn-server ( quot name -- thread )
217 [ '[ _ loop ] ] dip spawn ;
219 : in-thread ( quot -- )
221 '[ _ set-datastack _ call ]
222 "Thread" spawn drop ;
224 GENERIC: error-in-thread ( error thread -- )
228 : init-threads ( -- )
229 H{ } clone 64 set-special-object
230 <dlist> 65 set-special-object
231 <min-heap> 66 set-special-object
232 initial-thread global
233 [ drop [ ] "Initial" <thread> ] cache
242 [ init-threads ] "threads" add-startup-hook