]> gitweb.factorcode.org Git - factor.git/blob - basis/io/launcher/launcher.factor
Merge branch 'master' into startup
[factor.git] / basis / io / launcher / launcher.factor
1 ! Copyright (C) 2008 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     [
132         dup handle>>
133         [
134             dup [ processes get at push ] curry
135             "process" suspend drop
136         ] when
137         dup killed>>
138         [ process-was-killed ] [ status>> ] if
139     ] with-timeout ;
140
141 : run-detached ( desc -- process )
142     >process
143     dup dup run-process* process-started
144     dup timeout>> [ over set-timeout ] when* ;
145
146 : run-process ( desc -- process )
147     run-detached
148     dup detached>> [ dup wait-for-process drop ] unless ;
149
150 ERROR: process-failed process code ;
151
152 M: process-failed error.
153     dup "Process exited with error code " write code>> . nl
154     "Launch descriptor:" print nl
155     process>> . ;
156
157 : wait-for-success ( process -- )
158     dup wait-for-process dup 0 =
159     [ 2drop ] [ process-failed ] if ;
160
161 : try-process ( desc -- )
162     run-process wait-for-success ;
163
164 HOOK: kill-process* io-backend ( handle -- )
165
166 : kill-process ( process -- )
167     t >>killed
168     handle>> [ kill-process* ] when* ;
169
170 M: process timeout timeout>> ;
171
172 M: process set-timeout swap >>timeout drop ;
173
174 M: process cancel-operation kill-process ;
175
176 M: object run-pipeline-element
177     [ >process swap >>stdout swap >>stdin run-detached ]
178     [ drop [ [ dispose ] when* ] bi@ ]
179     3bi
180     wait-for-process ;
181
182 : <process-reader*> ( desc encoding -- stream process )
183     [
184         [
185             (pipe) {
186                 [ |dispose drop ]
187                 [
188                     swap >process
189                         [ swap out>> or ] change-stdout
190                     run-detached
191                 ]
192                 [ out>> dispose ]
193                 [ in>> <input-port> ]
194             } cleave
195         ] dip <decoder> swap
196     ] with-destructors ;
197
198 : <process-reader> ( desc encoding -- stream )
199     <process-reader*> drop ; inline
200
201 : with-process-reader ( desc encoding quot -- )
202     [ <process-reader*> ] dip
203     swap [ with-input-stream ] dip
204     wait-for-success ; inline
205
206 : <process-writer*> ( desc encoding -- stream process )
207     [
208         [
209             (pipe) {
210                 [ |dispose drop ]
211                 [
212                     swap >process
213                         [ swap in>> or ] change-stdin
214                     run-detached
215                 ]
216                 [ in>> dispose ]
217                 [ out>> <output-port> ]
218             } cleave
219         ] dip <encoder> swap
220     ] with-destructors ;
221
222 : <process-writer> ( desc encoding -- stream )
223     <process-writer*> drop ; inline
224
225 : with-process-writer ( desc encoding quot -- )
226     [ <process-writer*> ] dip
227     swap [ with-output-stream ] dip
228     wait-for-success ; inline
229
230 : <process-stream*> ( desc encoding -- stream process )
231     [
232         [
233             (pipe) (pipe) {
234                 [ [ |dispose drop ] bi@ ]
235                 [
236                     rot >process
237                         [ swap in>> or ] change-stdin
238                         [ swap out>> or ] change-stdout
239                     run-detached
240                 ]
241                 [ [ out>> dispose ] [ in>> dispose ] bi* ]
242                 [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
243             } 2cleave
244         ] dip <encoder-duplex> swap
245     ] with-destructors ;
246
247 : <process-stream> ( desc encoding -- stream )
248     <process-stream*> drop ; inline
249
250 : with-process-stream ( desc encoding quot -- )
251     [ <process-stream*> ] dip
252     swap [ with-stream ] dip
253     wait-for-success ; inline
254
255 ERROR: output-process-error { output string } { process process } ;
256
257 M: output-process-error error.
258     [ "Process:" print process>> . nl ]
259     [ "Output:" print output>> print ]
260     bi ;
261
262 : try-output-process ( command -- )
263     >process
264     +stdout+ >>stderr
265     [ +closed+ or ] change-stdin
266     utf8 <process-reader*>
267     [ stream-contents ] [ dup wait-for-process ] bi*
268     0 = [ 2drop ] [ output-process-error ] if ;
269
270 : notify-exit ( process status -- )
271     >>status
272     [ processes get delete-at* drop [ resume ] each ] keep
273     f >>handle
274     drop ;
275
276 {
277     { [ os unix? ] [ "io.launcher.unix" require ] }
278     { [ os winnt? ] [ "io.launcher.windows.nt" require ] }
279     [ ]
280 } cond