]> gitweb.factorcode.org Git - factor.git/blob - basis/io/launcher/launcher.factor
dfbbd33d2e905fc7cc46f10aaac8bde1eabeb607
[factor.git] / basis / io / launcher / launcher.factor
1 ! Copyright (C) 2008, 2010 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: system kernel namespaces strings hashtables sequences assocs
4 combinators vocabs.loader init threads continuations math accessors
5 concurrency.flags destructors environment io io.encodings.ascii
6 io.backend io.timeouts io.pipes io.pipes.private io.encodings
7 io.encodings.utf8 io.streams.duplex io.ports debugger prettyprint
8 summary calendar ;
9 IN: io.launcher
10
11 TUPLE: process < identity-tuple
12
13 command
14 detached
15
16 environment
17 environment-mode
18
19 stdin
20 stdout
21 stderr
22
23 priority
24
25 timeout
26
27 handle status
28 killed ;
29
30 SYMBOL: +closed+
31 SYMBOL: +stdout+
32
33 TUPLE: appender path ;
34
35 : <appender> ( path -- appender ) appender boa ;
36
37 SYMBOL: +prepend-environment+
38 SYMBOL: +replace-environment+
39 SYMBOL: +append-environment+
40
41 SYMBOL: +lowest-priority+
42 SYMBOL: +low-priority+
43 SYMBOL: +normal-priority+
44 SYMBOL: +high-priority+
45 SYMBOL: +highest-priority+
46 SYMBOL: +realtime-priority+
47
48 : <process> ( -- process )
49     process new
50     H{ } clone >>environment
51     +append-environment+ >>environment-mode ;
52
53 : process-started? ( process -- ? )
54     dup handle>> swap status>> or ;
55
56 : process-running? ( process -- ? )
57     handle>> >boolean ;
58
59 ! Non-blocking process exit notification facility
60 SYMBOL: processes
61
62 HOOK: wait-for-processes io-backend ( -- ? )
63
64 SYMBOL: wait-flag
65
66 : wait-loop ( -- )
67     processes get assoc-empty?
68     [ wait-flag get-global lower-flag ]
69     [ wait-for-processes [ 100 milliseconds sleep ] when ] if ;
70
71 : start-wait-thread ( -- )
72     <flag> wait-flag set-global
73     [ wait-loop t ] "Process wait" spawn-server drop ;
74
75 [
76     H{ } clone processes set-global
77     start-wait-thread
78 ] "io.launcher" add-startup-hook
79
80 : process-started ( process handle -- )
81     >>handle
82     V{ } clone swap processes get set-at
83     wait-flag get-global raise-flag ;
84
85 : pass-environment? ( process -- ? )
86     dup environment>> assoc-empty? not
87     swap environment-mode>> +replace-environment+ eq? or ;
88
89 : get-environment ( process -- env )
90     dup environment>>
91     swap environment-mode>> {
92         { +prepend-environment+ [ os-envs assoc-union ] }
93         { +append-environment+ [ os-envs swap assoc-union ] }
94         { +replace-environment+ [ ] }
95     } case ;
96
97 : string-array? ( obj -- ? )
98     dup sequence? [ [ string? ] all? ] [ drop f ] if ;
99
100 GENERIC: >process ( obj -- process )
101
102 ERROR: process-already-started process ;
103
104 M: process-already-started error.
105     "Process has already been started" print nl
106     "Launch descriptor:" print nl
107     process>> . ;
108
109 M: process >process
110     dup process-started? [
111         process-already-started
112     ] when
113     clone ;
114
115 M: object >process <process> swap >>command ;
116
117 HOOK: current-process-handle io-backend ( -- handle )
118
119 HOOK: run-process* io-backend ( process -- handle )
120
121 ERROR: process-was-killed process ;
122
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
126     nl
127     "Launch descriptor:" print nl
128     process>> . ;
129
130 : (wait-for-process) ( process -- status )
131     dup handle>>
132     [ self over processes get at push "process" suspend drop ] when
133     dup killed>> [ process-was-killed ] [ status>> ] if ;
134
135 : wait-for-process ( process -- status )
136     [ (wait-for-process) ] with-timeout ;
137
138 : run-detached ( desc -- process )
139     >process
140     dup dup run-process* process-started
141     dup timeout>> [ over set-timeout ] when* ;
142
143 : run-process ( desc -- process )
144     run-detached
145     dup detached>> [ dup wait-for-process drop ] unless ;
146
147 ERROR: process-failed process code ;
148
149 M: process-failed error.
150     dup "Process exited with error code " write code>> . nl
151     "Launch descriptor:" print nl
152     process>> . ;
153
154 : wait-for-success ( process -- )
155     dup wait-for-process dup 0 =
156     [ 2drop ] [ process-failed ] if ;
157
158 : try-process ( desc -- )
159     run-process wait-for-success ;
160
161 HOOK: kill-process* io-backend ( handle -- )
162
163 : kill-process ( process -- )
164     t >>killed
165     handle>> [ kill-process* ] when* ;
166
167 M: process timeout timeout>> ;
168
169 M: process set-timeout swap >>timeout drop ;
170
171 M: process cancel-operation kill-process ;
172
173 M: object run-pipeline-element
174     [ >process swap >>stdout swap >>stdin run-detached ]
175     [ drop [ [ dispose ] when* ] bi@ ]
176     3bi
177     wait-for-process ;
178
179 : <process-reader*> ( desc encoding -- stream process )
180     [
181         [
182             (pipe) {
183                 [ |dispose drop ]
184                 [
185                     swap >process
186                         [ swap out>> or ] change-stdout
187                     run-detached
188                 ]
189                 [ out>> dispose ]
190                 [ in>> <input-port> ]
191             } cleave
192         ] dip <decoder> swap
193     ] with-destructors ;
194
195 : <process-reader> ( desc encoding -- stream )
196     <process-reader*> drop ; inline
197
198 : with-process-reader ( desc encoding quot -- )
199     [ <process-reader*> ] dip
200     swap [ with-input-stream ] dip
201     wait-for-success ; inline
202
203 : <process-writer*> ( desc encoding -- stream process )
204     [
205         [
206             (pipe) {
207                 [ |dispose drop ]
208                 [
209                     swap >process
210                         [ swap in>> or ] change-stdin
211                     run-detached
212                 ]
213                 [ in>> dispose ]
214                 [ out>> <output-port> ]
215             } cleave
216         ] dip <encoder> swap
217     ] with-destructors ;
218
219 : <process-writer> ( desc encoding -- stream )
220     <process-writer*> drop ; inline
221
222 : with-process-writer ( desc encoding quot -- )
223     [ <process-writer*> ] dip
224     swap [ with-output-stream ] dip
225     wait-for-success ; inline
226
227 : <process-stream*> ( desc encoding -- stream process )
228     [
229         [
230             (pipe) (pipe) {
231                 [ [ |dispose drop ] bi@ ]
232                 [
233                     rot >process
234                         [ swap in>> or ] change-stdin
235                         [ swap out>> or ] change-stdout
236                     run-detached
237                 ]
238                 [ [ out>> dispose ] [ in>> dispose ] bi* ]
239                 [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
240             } 2cleave
241         ] dip <encoder-duplex> swap
242     ] with-destructors ;
243
244 : <process-stream> ( desc encoding -- stream )
245     <process-stream*> drop ; inline
246
247 : with-process-stream ( desc encoding quot -- )
248     [ <process-stream*> ] dip
249     swap [ with-stream ] dip
250     wait-for-success ; inline
251
252 ERROR: output-process-error { output string } { process process } ;
253
254 M: output-process-error error.
255     [ "Process:" print process>> . nl ]
256     [ "Output:" print output>> print ]
257     bi ;
258
259 : try-output-process ( command -- )
260     >process
261     +stdout+ >>stderr
262     [ +closed+ or ] change-stdin
263     utf8 <process-reader*>
264     [ [ stream-contents ] [ dup (wait-for-process) ] bi* ] with-timeout
265     0 = [ 2drop ] [ output-process-error ] if ;
266
267 : notify-exit ( process status -- )
268     >>status
269     [ processes get delete-at* drop [ resume ] each ] keep
270     f >>handle
271     drop ;
272
273 {
274     { [ os unix? ] [ "io.launcher.unix" require ] }
275     { [ os winnt? ] [ "io.launcher.windows.nt" require ] }
276     [ ]
277 } cond