]> gitweb.factorcode.org Git - factor.git/blob - basis/io/launcher/launcher.factor
7f1a3f45075212a952934e4dede80589276a4cba
[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 
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 ;
8 IN: io.launcher
9
10 TUPLE: process < identity-tuple
11
12 command
13 detached
14
15 environment
16 environment-mode
17
18 stdin
19 stdout
20 stderr
21
22 priority
23
24 timeout
25
26 handle status
27 killed ;
28
29 SYMBOL: +closed+
30 SYMBOL: +stdout+
31
32 TUPLE: appender path ;
33
34 : <appender> ( path -- appender ) appender boa ;
35
36 SYMBOL: +prepend-environment+
37 SYMBOL: +replace-environment+
38 SYMBOL: +append-environment+
39
40 SYMBOL: +lowest-priority+
41 SYMBOL: +low-priority+
42 SYMBOL: +normal-priority+
43 SYMBOL: +high-priority+
44 SYMBOL: +highest-priority+
45 SYMBOL: +realtime-priority+
46
47 : <process> ( -- process )
48     process new
49     H{ } clone >>environment
50     +append-environment+ >>environment-mode ;
51
52 : process-started? ( process -- ? )
53     dup handle>> swap status>> or ;
54
55 : process-running? ( process -- ? )
56     handle>> >boolean ;
57
58 ! Non-blocking process exit notification facility
59 SYMBOL: processes
60
61 [ H{ } clone processes set-global ] "io.launcher" add-init-hook
62
63 HOOK: wait-for-processes io-backend ( -- ? )
64
65 SYMBOL: wait-flag
66
67 : wait-loop ( -- )
68     processes get assoc-empty?
69     [ wait-flag get-global lower-flag ]
70     [ wait-for-processes [ 100 sleep ] when ] if ;
71
72 : start-wait-thread ( -- )
73     <flag> wait-flag set-global
74     [ wait-loop t ] "Process wait" spawn-server drop ;
75
76 [ start-wait-thread ] "io.launcher" add-init-hook
77
78 : process-started ( process handle -- )
79     >>handle
80     V{ } clone swap processes get set-at
81     wait-flag get-global raise-flag ;
82
83 M: process hashcode* handle>> hashcode* ;
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 zero?
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         >r (pipe) {
185             [ |dispose drop ]
186             [
187                 swap >process
188                     [ swap out>> or ] change-stdout
189                 run-detached
190             ]
191             [ out>> dispose ]
192             [ in>> <input-port> ]
193         } cleave r> <decoder> swap
194     ] with-destructors ;
195
196 : <process-reader> ( desc encoding -- stream )
197     <process-reader*> drop ; inline
198
199 : with-process-reader ( desc encoding quot -- )
200     [ <process-reader*> ] dip
201     swap [ with-input-stream ] dip
202     wait-for-success ; inline
203
204 : <process-writer*> ( desc encoding -- stream process )
205     [
206         >r (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 r> <encoder> swap
216     ] with-destructors ;
217
218 : <process-writer> ( desc encoding -- stream )
219     <process-writer*> drop ; inline
220
221 : with-process-writer ( desc encoding quot -- )
222     [ <process-writer*> ] dip
223     swap [ with-output-stream ] dip
224     wait-for-success ; inline
225
226 : <process-stream*> ( desc encoding -- stream process )
227     [
228         >r (pipe) (pipe) {
229             [ [ |dispose drop ] bi@ ]
230             [
231                 rot >process
232                     [ swap in>> or ] change-stdin
233                     [ swap out>> or ] change-stdout
234                 run-detached
235             ]
236             [ [ out>> dispose ] [ in>> dispose ] bi* ]
237             [ [ in>> <input-port> ] [ out>> <output-port> ] bi* ]
238         } 2cleave r> <encoder-duplex> swap
239     ] with-destructors ;
240
241 : <process-stream> ( desc encoding -- stream )
242     <process-stream*> drop ; inline
243
244 : with-process-stream ( desc encoding quot -- )
245     [ <process-stream*> ] dip
246     swap [ with-stream ] dip
247     wait-for-success ; inline
248
249 : notify-exit ( process status -- )
250     >>status
251     [ processes get delete-at* drop [ resume ] each ] keep
252     f >>handle
253     drop ;
254
255 GENERIC: underlying-handle ( stream -- handle )
256
257 M: port underlying-handle handle>> ;
258
259 ERROR: invalid-duplex-stream ;
260
261 M: duplex-stream underlying-handle
262     [ in>> underlying-handle ]
263     [ out>> underlying-handle ] bi
264     [ = [ invalid-duplex-stream ] when ] keep ;
265
266 M: encoder underlying-handle
267     stream>> underlying-handle ;
268
269 M: decoder underlying-handle
270     stream>> underlying-handle ;
271
272 {
273     { [ os unix? ] [ "io.unix.launcher" require ] }
274     { [ os winnt? ] [ "io.windows.nt.launcher" require ] }
275     { [ os wince? ] [ "io.windows.launcher" require ] }
276     [ ]
277 } cond