1 ! Copyright (C) 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: system kernel namespaces strings hashtables sequences
4 assocs combinators vocabs.loader init threads continuations
5 math accessors concurrency.flags destructors
6 io io.backend io.timeouts io.pipes io.pipes.private io.encodings
7 io.streams.duplex io.ports debugger prettyprint summary ;
10 TUPLE: process < identity-tuple
32 TUPLE: appender path ;
34 : <appender> ( path -- appender ) appender boa ;
36 SYMBOL: +prepend-environment+
37 SYMBOL: +replace-environment+
38 SYMBOL: +append-environment+
40 SYMBOL: +lowest-priority+
41 SYMBOL: +low-priority+
42 SYMBOL: +normal-priority+
43 SYMBOL: +high-priority+
44 SYMBOL: +highest-priority+
45 SYMBOL: +realtime-priority+
47 : <process> ( -- process )
49 H{ } clone >>environment
50 +append-environment+ >>environment-mode ;
52 : process-started? ( process -- ? )
53 dup handle>> swap status>> or ;
55 : process-running? ( process -- ? )
58 ! Non-blocking process exit notification facility
61 [ H{ } clone processes set-global ] "io.launcher" add-init-hook
63 HOOK: wait-for-processes io-backend ( -- ? )
68 processes get assoc-empty?
69 [ wait-flag get-global lower-flag ]
70 [ wait-for-processes [ 100 sleep ] when ] if ;
72 : start-wait-thread ( -- )
73 <flag> wait-flag set-global
74 [ wait-loop t ] "Process wait" spawn-server drop ;
76 [ start-wait-thread ] "io.launcher" add-init-hook
78 : process-started ( process handle -- )
80 V{ } clone swap processes get set-at
81 wait-flag get-global raise-flag ;
83 M: process hashcode* handle>> hashcode* ;
85 : pass-environment? ( process -- ? )
86 dup environment>> assoc-empty? not
87 swap environment-mode>> +replace-environment+ eq? or ;
89 : get-environment ( process -- env )
91 swap environment-mode>> {
92 { +prepend-environment+ [ os-envs assoc-union ] }
93 { +append-environment+ [ os-envs swap assoc-union ] }
94 { +replace-environment+ [ ] }
97 : string-array? ( obj -- ? )
98 dup sequence? [ [ string? ] all? ] [ drop f ] if ;
100 GENERIC: >process ( obj -- process )
102 ERROR: process-already-started process ;
104 M: process-already-started error.
105 "Process has already been started" print nl
106 "Launch descriptor:" print nl
110 dup process-started? [
111 process-already-started
115 M: object >process <process> swap >>command ;
117 HOOK: current-process-handle io-backend ( -- handle )
119 HOOK: run-process* io-backend ( process -- handle )
121 ERROR: process-was-killed process ;
123 M: process-was-killed error.
124 "Process was killed as a result of a call to" print
125 "kill-process, or a timeout" print
127 "Launch descriptor:" print nl
130 : wait-for-process ( process -- status )
134 dup [ processes get at push ] curry
135 "process" suspend drop
138 [ process-was-killed ] [ status>> ] if
141 : run-detached ( desc -- process )
143 dup dup run-process* process-started
144 dup timeout>> [ over set-timeout ] when* ;
146 : run-process ( desc -- process )
148 dup detached>> [ dup wait-for-process drop ] unless ;
150 ERROR: process-failed process code ;
152 M: process-failed error.
153 dup "Process exited with error code " write code>> . nl
154 "Launch descriptor:" print nl
157 : wait-for-success ( process -- )
158 dup wait-for-process dup zero?
159 [ 2drop ] [ process-failed ] if ;
161 : try-process ( desc -- )
162 run-process wait-for-success ;
164 HOOK: kill-process* io-backend ( handle -- )
166 : kill-process ( process -- )
168 handle>> [ kill-process* ] when* ;
170 M: process timeout timeout>> ;
172 M: process set-timeout swap >>timeout drop ;
174 M: process cancel-operation kill-process ;
176 M: object run-pipeline-element
177 [ >process swap >>stdout swap >>stdin run-detached ]
178 [ drop [ [ dispose ] when* ] bi@ ]
182 : <process-reader*> ( desc encoding -- stream process )
188 [ swap out>> or ] change-stdout
192 [ in>> <input-port> ]
193 } cleave r> <decoder> swap
196 : <process-reader> ( desc encoding -- stream )
197 <process-reader*> drop ; inline
199 : with-process-reader ( desc encoding quot -- )
200 [ <process-reader*> ] dip
201 swap [ with-input-stream ] dip
202 wait-for-success ; inline
204 : <process-writer*> ( desc encoding -- stream process )
210 [ swap in>> or ] change-stdin
214 [ out>> <output-port> ]
215 } cleave r> <encoder> swap
218 : <process-writer> ( desc encoding -- stream )
219 <process-writer*> drop ; inline
221 : with-process-writer ( desc encoding quot -- )
222 [ <process-writer*> ] dip
223 swap [ with-output-stream ] dip
224 wait-for-success ; inline
226 : <process-stream*> ( desc encoding -- stream process )
229 [ [ |dispose drop ] bi@ ]
232 [ swap in>> or ] change-stdin
233 [ swap out>> or ] change-stdout
236 [ [ out>> dispose ] [ in>> dispose ] bi* ]
237 [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
238 } 2cleave r> <encoder-duplex> swap
241 : <process-stream> ( desc encoding -- stream )
242 <process-stream*> drop ; inline
244 : with-process-stream ( desc encoding quot -- )
245 [ <process-stream*> ] dip
246 swap [ with-stream ] dip
247 wait-for-success ; inline
249 : notify-exit ( process status -- )
251 [ processes get delete-at* drop [ resume ] each ] keep
255 GENERIC: underlying-handle ( stream -- handle )
257 M: port underlying-handle handle>> ;
259 ERROR: invalid-duplex-stream ;
261 M: duplex-stream underlying-handle
262 [ in>> underlying-handle ]
263 [ out>> underlying-handle ] bi
264 [ = [ invalid-duplex-stream ] when ] keep ;
266 M: encoder underlying-handle
267 stream>> underlying-handle ;
269 M: decoder underlying-handle
270 stream>> underlying-handle ;
273 { [ os unix? ] [ "io.unix.launcher" require ] }
274 { [ os winnt? ] [ "io.windows.nt.launcher" require ] }
275 { [ os wince? ] [ "io.windows.launcher" require ] }