1 ! Copyright (C) 2004, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien.c-types alien.data alien.syntax classes
4 classes.struct combinators destructors destructors.private fry
5 io.backend io.backend.unix.multiplexers io.buffers io.files
6 io.ports io.timeouts kernel kernel.private libc locals make math
7 namespaces sequences summary system threads unix unix.ffi
8 unix.signals unix.stat unix.types ;
12 CONSTANT: file-mode 0o0666
14 GENERIC: handle-fd ( handle -- fd )
16 TUPLE: fd < disposable fd ;
18 : init-fd ( fd -- fd )
21 dup fd>> F_SETFL O_NONBLOCK [ fcntl ] unix-system-call drop
22 dup fd>> F_SETFD FD_CLOEXEC [ fcntl ] unix-system-call drop
26 fd new-disposable swap >>fd ;
33 [ unregister-disposable ]
38 M: fd handle-fd check-disposed fd>> ;
40 M: fd cancel-operation
44 [ remove-input-callbacks [ t swap resume-with ] each ]
45 [ remove-output-callbacks [ t swap resume-with ] each ]
50 fd>> 0 SEEK_CUR [ lseek ] unix-system-call [ io-error ] [ ] bi ;
54 { io:seek-absolute [ SEEK_SET ] }
55 { io:seek-relative [ SEEK_CUR ] }
56 { io:seek-end [ SEEK_END ] }
59 [ fd>> swap ] dip [ lseek ] unix-system-call drop ;
61 M: unix can-seek-handle?
62 fd>> SEEK_CUR 0 lseek -1 = not ;
65 fd>> \ stat new [ fstat -1 = not ] keep
66 swap [ st_size>> ] [ drop f ] if ;
70 M: io-timeout summary drop "I/O operation timed out" ;
73 dup +retry+ eq? [ 2drop ] [
74 [ [ self ] dip handle-fd mx get-global ] dip {
75 { +input+ [ add-input-callback ] }
76 { +output+ [ add-output-callback ] }
78 "I/O" suspend [ io-timeout ] when
81 : wait-for-port ( port event -- )
82 '[ handle>> _ wait-for-fd ] with-timeout ;
87 [ buffered-port check-instance buffer>> ] [ fd>> ] bi*
88 over [ buffer-end ] [ buffer-capacity ] bi read
89 { fixnum } declare dup 0 >= [
93 { EINTR [ 2drop +retry+ ] }
94 { EAGAIN [ 2drop +input+ ] }
99 M: unix (wait-to-read)
101 dup handle>> check-disposed refill dup
102 [ dupd wait-for-port (wait-to-read) ] [ 2drop ] if ;
106 [ buffered-port check-instance buffer>> ] [ fd>> ] bi*
107 over [ buffer@ ] [ buffer-length ] bi write
108 { fixnum } declare dup 0 >= [
110 buffer-empty? f +output+ ?
113 { EINTR [ 2drop +retry+ ] }
114 { EAGAIN [ 2drop +output+ ] }
119 M: unix (wait-to-write)
121 dup handle>> check-disposed drain
122 [ wait-for-port ] [ drop ] if* ;
125 mx get-global wait-for-events ;
127 ! On Unix, you're not supposed to set stdin to non-blocking
128 ! because the fd might be shared with another process (either
129 ! parent or child). So what we do is have the VM start a thread
130 ! which pumps data from the real stdin to a pipe. We set the
131 ! pipe to non-blocking, and read from it instead of the real
132 ! stdin. Very crufty, but it will suffice until we get native
133 ! threading support at the language level.
134 TUPLE: stdin < disposable control size data ;
138 [ control>> &dispose drop ]
139 [ size>> &dispose drop ]
140 [ data>> &dispose drop ]
144 : wait-for-stdin ( stdin -- size )
145 [ control>> CHAR: X over io:stream-write1 io:stream-flush ]
146 [ size>> ssize_t heap-size swap io:stream-read ssize_t deref ]
149 :: refill-stdin ( buffer stdin size -- )
150 stdin data>> handle-fd buffer buffer-end size read
154 buffer stdin size refill-stdin
159 size = [ "Error reading stdin pipe" throw ] unless
165 buffer>> _ dup wait-for-stdin refill-stdin f
168 M: stdin cancel-operation
169 [ size>> ] [ control>> ] bi [ cancel-operation ] bi@ ;
171 : control-write-fd ( -- fd ) &: control_write uint deref ;
173 : size-read-fd ( -- fd ) &: size_read uint deref ;
175 : data-read-fd ( -- fd ) &: stdin_read uint deref ;
177 : <stdin> ( -- stdin )
179 control-write-fd <fd> <output-port> >>control
180 size-read-fd <fd> init-fd <input-port> >>size
181 data-read-fd <fd> >>data ;
183 : signal-pipe-fd ( -- n )
184 OBJ-SIGNAL-PIPE special-object ; inline
186 : signal-pipe-loop ( port -- )
188 int heap-size _ io:stream-read
189 dup [ int deref dispatch-signal-hook get-global call( x -- ) ] when*
192 : start-signal-pipe-thread ( -- )
194 <fd> init-fd <input-port>
195 '[ _ signal-pipe-loop ] "Signals" spawn drop
204 ! mx io-task for embedding an fd-based mx inside another mx
205 TUPLE: mx-port < port mx ;
207 : <mx-port> ( mx -- port )
208 dup fd>> mx-port <port> swap >>mx ;
210 : multiplexer-error ( n -- n )
212 errno [ EAGAIN = ] [ EINTR = ] bi or
213 [ drop 0 ] [ throw-errno ] if
216 :: ?flag ( n mask symbol -- n )
217 n mask bitand 0 > [ symbol , ] when n ;