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 [ handle>> ] [ status>> ] bi 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 = [ drop ] [ process-failed ] if ;
164 : try-process ( desc -- )
165 run-process wait-for-success ;
167 HOOK: kill-process* io-backend ( process -- )
169 : kill-process ( process -- )
171 [ pipe>> [ dispose ] when* ]
172 [ dup handle>> [ kill-process* ] [ drop ] if ] bi ;
174 M: process timeout timeout>> ;
176 M: process set-timeout timeout<< ;
178 M: process cancel-operation kill-process ;
180 M: object run-pipeline-element
181 [ >process swap >>stdout swap >>stdin run-detached ]
182 [ [ drop [ [ &dispose drop ] when* ] bi@ ] with-destructors ]
188 : <process-with-pipe> ( desc -- process pipe )
189 >process (pipe) |dispose [ >>pipe ] keep ;
193 : <process-reader*> ( desc encoding -- stream process )
196 <process-with-pipe> {
197 [ '[ _ out>> or ] change-stdout ]
198 [ drop run-detached ]
200 [ in>> <input-port> ]
205 : <process-reader> ( desc encoding -- stream )
206 <process-reader*> drop ; inline
208 : with-process-reader ( desc encoding quot -- )
209 [ <process-reader*> ] dip
210 swap [ with-input-stream ] dip
211 wait-for-success ; inline
213 : <process-writer*> ( desc encoding -- stream process )
216 <process-with-pipe> {
217 [ '[ _ in>> or ] change-stdin ]
218 [ drop run-detached ]
220 [ out>> <output-port> ]
225 : <process-writer> ( desc encoding -- stream )
226 <process-writer*> drop ; inline
228 : with-process-writer ( desc encoding quot -- )
229 [ <process-writer*> ] dip
230 swap [ with-output-stream ] dip
231 wait-for-success ; inline
233 : <process-stream*> ( desc encoding -- stream process )
240 [ swap in>> or ] change-stdin
241 [ swap out>> or ] change-stdout
244 [ [ out>> &dispose drop ] [ in>> &dispose drop ] bi* ]
245 [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
247 ] dip <encoder-duplex> swap
250 : <process-stream> ( desc encoding -- stream )
251 <process-stream*> drop ; inline
253 : with-process-stream ( desc encoding quot -- )
254 [ <process-stream*> ] dip
255 swap [ with-stream ] dip
256 wait-for-success ; inline
258 ERROR: output-process-error { output string } { process process } ;
260 M: output-process-error error.
261 [ "Process:" print process>> . nl ]
262 [ "Output:" print output>> print ]
265 : try-output-process ( command -- )
268 [ +closed+ or ] change-stdin
269 utf8 <process-reader*>
270 [ [ stream-contents ] [ dup (wait-for-process) ] bi* ] with-timeout
271 0 = [ 2drop ] [ output-process-error ] if ;
273 : notify-exit ( process status -- )
275 [ processes get delete-at* drop [ resume ] each ] keep
280 { [ os unix? ] [ "io.launcher.unix" require ] }
281 { [ os windows? ] [ "io.launcher.windows" require ] }