1 ! Copyright (C) 2004, 2008 Slava Pestov.
2 ! Copyright (C) 2005 Mackenzie Straight.
3 ! See http://factorcode.org/license.txt for BSD license.
5 USING: arrays hashtables heaps kernel kernel.private math
6 namespaces sequences vectors continuations continuations.private
7 dlists assocs system combinators init boxes accessors ;
12 name quot exit-handler
15 mailbox variables sleep-entry ;
17 : self ( -- thread ) 40 getenv ; inline
19 ! Thread-local storage
20 : tnamespace ( -- assoc )
21 self variables>> [ H{ } clone dup self (>>variables) ] unless* ;
23 : tget ( key -- value )
26 : tset ( value key -- )
29 : tchange ( key quot -- )
30 tnamespace swap change-at ; inline
34 : thread ( id -- thread ) threads at ;
36 : thread-registered? ( thread -- ? )
40 dup thread-registered?
41 [ "Thread already stopped" throw ] when ;
44 dup thread-registered?
45 [ "Thread is not running" throw ] unless ;
49 : register-thread ( thread -- )
50 check-unregistered dup id>> threads set-at ;
52 : unregister-thread ( thread -- )
53 check-registered id>> threads delete-at ;
55 : set-self ( thread -- ) 40 setenv ; inline
59 : new-thread ( quot name class -- thread )
65 [ ] >>exit-handler ; inline
67 : <thread> ( quot name -- thread )
70 : run-queue 42 getenv ;
72 : sleep-queue 43 getenv ;
74 : resume ( thread -- )
76 check-registered run-queue push-front ;
78 : resume-now ( thread -- )
80 check-registered run-queue push-back ;
82 : resume-with ( obj thread -- )
84 check-registered 2array run-queue push-front ;
86 : sleep-time ( -- ms/f )
88 { [ run-queue dlist-empty? not ] [ 0 ] }
89 { [ sleep-queue heap-empty? ] [ f ] }
90 [ sleep-queue heap-peek nip millis [-] ]
97 : schedule-sleep ( thread ms -- )
98 >r check-registered dup r> sleep-queue heap-push*
101 : expire-sleep? ( heap -- ? )
103 [ drop f ] [ heap-peek nip millis <= ] if ;
105 : expire-sleep ( thread -- )
106 f >>sleep-entry resume ;
108 : expire-sleep-loop ( -- )
110 [ dup expire-sleep? ]
111 [ dup heap-pop drop expire-sleep ]
115 : start ( namestack thread -- )
122 self quot>> [ call stop ] call-clear
127 : no-runnable-threads ( -- * )
128 ! We should never be in a state where the only threads
129 ! are sleeping; the I/O wait thread is always runnable.
130 ! However, if it dies, we handle this case
133 ! And if sleep-time outputs f, there are no sleeping
134 ! threads either... so WTF.
135 sleep-time [ die 0 ] unless* (sleep) next ;
137 : (next) ( arg thread -- * )
140 dup continuation>> ?box
141 [ nip continue-with ] [ drop start ] if ;
145 run-queue dup dlist-empty? [
146 drop no-runnable-threads
148 pop-back dup array? [ first2 ] [ f swap ] if (next)
154 self [ exit-handler>> call ] [ unregister-thread ] bi next ;
156 : suspend ( quot state -- obj )
161 r> self continuation>> >box
163 ] callcc1 2nip ; inline
165 : yield ( -- ) [ resume ] f suspend drop ;
167 GENERIC: sleep-until ( time/f -- )
169 M: integer sleep-until
170 [ schedule-sleep ] curry "sleep" suspend drop ;
173 drop [ drop ] "interrupt" suspend drop ;
175 GENERIC: sleep ( ms -- )
178 millis + >integer sleep-until ;
180 : interrupt ( thread -- )
182 dup sleep-entry>> [ sleep-queue heap-delete ] when*
187 : (spawn) ( thread -- )
188 [ register-thread ] [ namestack swap resume-with ] bi ;
190 : spawn ( quot name -- thread )
191 <thread> [ (spawn) ] keep ;
193 : spawn-server ( quot name -- thread )
194 >r [ [ ] [ ] while ] curry r> spawn ;
196 : in-thread ( quot -- )
198 [ >r set-datastack r> call ] 2curry
199 "Thread" spawn drop ;
201 GENERIC: error-in-thread ( error thread -- )
205 : init-threads ( -- )
209 initial-thread global
210 [ drop f "Initial" <thread> ] cache
216 [ self error-in-thread stop ]
217 thread-error-hook set-global
221 [ init-threads ] "threads" add-init-hook