1 ! Copyright (C) 2008, 2011 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: system kernel namespaces strings hashtables sequences
4 assocs combinators vocabs init threads continuations math
5 accessors concurrency.flags destructors environment fry io
6 io.encodings.ascii io.backend io.timeouts io.pipes
7 io.pipes.private io.encodings io.encodings.utf8
8 io.streams.duplex io.ports debugger prettyprint summary calendar ;
11 TUPLE: process < identity-tuple
36 TUPLE: appender path ;
38 : <appender> ( path -- appender ) appender boa ;
40 SYMBOL: +prepend-environment+
41 SYMBOL: +replace-environment+
42 SYMBOL: +append-environment+
44 SYMBOL: +lowest-priority+
45 SYMBOL: +low-priority+
46 SYMBOL: +normal-priority+
47 SYMBOL: +high-priority+
48 SYMBOL: +highest-priority+
49 SYMBOL: +realtime-priority+
55 : <process> ( -- process )
57 H{ } clone >>environment
58 +append-environment+ >>environment-mode
59 +same-group+ >>group ;
61 : process-started? ( process -- ? )
62 dup handle>> swap status>> or ;
64 : process-running? ( process -- ? )
67 ! Non-blocking process exit notification facility
70 HOOK: wait-for-processes io-backend ( -- ? )
75 processes get assoc-empty?
76 [ wait-flag get-global lower-flag ]
77 [ wait-for-processes [ 100 milliseconds sleep ] when ] if ;
79 : start-wait-thread ( -- )
80 <flag> wait-flag set-global
81 [ wait-loop t ] "Process wait" spawn-server drop ;
84 H{ } clone processes set-global
86 ] "io.launcher" add-startup-hook
88 : process-started ( process handle -- )
90 V{ } clone swap processes get set-at
91 wait-flag get-global raise-flag ;
93 : pass-environment? ( process -- ? )
94 dup environment>> assoc-empty? not
95 swap environment-mode>> +replace-environment+ eq? or ;
97 : get-environment ( process -- env )
99 swap environment-mode>> {
100 { +prepend-environment+ [ os-envs assoc-union ] }
101 { +append-environment+ [ os-envs swap assoc-union ] }
102 { +replace-environment+ [ ] }
105 : string-array? ( obj -- ? )
106 dup sequence? [ [ string? ] all? ] [ drop f ] if ;
108 GENERIC: >process ( obj -- process )
110 ERROR: process-already-started process ;
112 M: process-already-started error.
113 "Process has already been started" print nl
114 "Launch descriptor:" print nl
118 dup process-started? [
119 process-already-started
123 M: object >process <process> swap >>command ;
125 HOOK: current-process-handle io-backend ( -- handle )
127 HOOK: run-process* io-backend ( process -- handle )
129 ERROR: process-was-killed process ;
131 M: process-was-killed error.
132 "Process was killed as a result of a call to" print
133 "kill-process, or a timeout" print
135 "Launch descriptor:" print nl
138 : (wait-for-process) ( process -- status )
140 [ self over processes get at push "process" suspend drop ] when
141 dup killed>> [ process-was-killed ] [ status>> ] if ;
143 : wait-for-process ( process -- status )
144 [ (wait-for-process) ] with-timeout ;
146 : run-detached ( desc -- process )
147 >process [ dup run-process* process-started ] keep ;
149 : run-process ( desc -- process )
151 dup detached>> [ dup wait-for-process drop ] unless ;
153 ERROR: process-failed process ;
155 M: process-failed error.
157 "Process exited with error code " write process>> status>> . nl
158 "Launch descriptor:" print nl
159 ] [ process>> . ] bi ;
161 : wait-for-success ( process -- )
162 dup wait-for-process 0 =
163 [ drop ] [ process-failed ] if ;
165 : try-process ( desc -- )
166 run-process wait-for-success ;
168 HOOK: kill-process* io-backend ( process -- )
170 : kill-process ( process -- )
172 [ pipe>> [ dispose ] when* ]
173 [ dup handle>> [ kill-process* ] [ drop ] if ] bi ;
175 M: process timeout timeout>> ;
177 M: process set-timeout timeout<< ;
179 M: process cancel-operation kill-process ;
181 M: object run-pipeline-element
182 [ >process swap >>stdout swap >>stdin run-detached ]
183 [ drop [ [ dispose ] when* ] bi@ ]
189 : <process-with-pipe> ( desc -- process pipe )
190 >process (pipe) |dispose [ >>pipe ] keep ;
194 : <process-reader*> ( desc encoding -- stream process )
197 <process-with-pipe> {
198 [ '[ _ out>> or ] change-stdout ]
199 [ drop run-detached ]
201 [ in>> <input-port> ]
206 : <process-reader> ( desc encoding -- stream )
207 <process-reader*> drop ; inline
209 : with-process-reader ( desc encoding quot -- )
210 [ <process-reader*> ] dip
211 swap [ with-input-stream ] dip
212 wait-for-success ; inline
214 : <process-writer*> ( desc encoding -- stream process )
217 <process-with-pipe> {
218 [ '[ _ in>> or ] change-stdin ]
219 [ drop run-detached ]
221 [ out>> <output-port> ]
226 : <process-writer> ( desc encoding -- stream )
227 <process-writer*> drop ; inline
229 : with-process-writer ( desc encoding quot -- )
230 [ <process-writer*> ] dip
231 swap [ with-output-stream ] dip
232 wait-for-success ; inline
234 : <process-stream*> ( desc encoding -- stream process )
238 [ [ |dispose drop ] bi@ ]
241 [ swap in>> or ] change-stdin
242 [ swap out>> or ] change-stdout
245 [ [ out>> dispose ] [ in>> dispose ] bi* ]
246 [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
248 ] dip <encoder-duplex> swap
251 : <process-stream> ( desc encoding -- stream )
252 <process-stream*> drop ; inline
254 : with-process-stream ( desc encoding quot -- )
255 [ <process-stream*> ] dip
256 swap [ with-stream ] dip
257 wait-for-success ; inline
259 ERROR: output-process-error { output string } { process process } ;
261 M: output-process-error error.
262 [ "Process:" print process>> . nl ]
263 [ "Output:" print output>> print ]
266 : try-output-process ( command -- )
269 [ +closed+ or ] change-stdin
270 utf8 <process-reader*>
271 [ [ stream-contents ] [ dup (wait-for-process) ] bi* ] with-timeout
272 0 = [ 2drop ] [ output-process-error ] if ;
274 : notify-exit ( process status -- )
276 [ processes get delete-at* drop [ resume ] each ] keep
281 { [ os unix? ] [ "io.launcher.unix" require ] }
282 { [ os windows? ] [ "io.launcher.windows" require ] }