]> gitweb.factorcode.org Git - factor.git/blob - basis/io/backend/unix/unix.factor
1a52ce6f345df6486f87ca11771cb3b520c66b72
[factor.git] / basis / io / backend / unix / unix.factor
1 ! Copyright (C) 2004, 2008 Slava Pestov.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: alien alien.c-types alien.syntax generic assocs kernel
4 kernel.private math io.ports sequences strings sbufs threads
5 unix vectors io.buffers io.backend io.encodings math.parser
6 continuations system libc namespaces make io.timeouts
7 io.encodings.utf8 destructors accessors summary combinators
8 locals unix.time fry io.backend.unix.multiplexers ;
9 QUALIFIED: io
10 IN: io.backend.unix
11
12 GENERIC: handle-fd ( handle -- fd )
13
14 TUPLE: fd fd disposed ;
15
16 : init-fd ( fd -- fd )
17     [
18         |dispose
19         dup fd>> F_SETFL O_NONBLOCK fcntl io-error
20         dup fd>> F_SETFD FD_CLOEXEC fcntl io-error
21     ] with-destructors ;
22
23 : <fd> ( n -- fd )
24     #! We drop the error code rather than calling io-error,
25     #! since on OS X 10.3, this operation fails from init-io
26     #! when running the Factor.app (presumably because fd 0 and
27     #! 1 are closed).
28     f fd boa ;
29
30 M: fd dispose
31     dup disposed>> [ drop ] [
32         [ cancel-operation ]
33         [ t >>disposed drop ]
34         [ fd>> close-file ]
35         tri
36     ] if ;
37
38 M: fd handle-fd dup check-disposed fd>> ;
39
40 M: fd cancel-operation ( fd -- )
41     dup disposed>> [ drop ] [
42         fd>>
43         mx get-global
44         [ remove-input-callbacks [ t swap resume-with ] each ]
45         [ remove-output-callbacks [ t swap resume-with ] each ]
46         2bi
47     ] if ;
48
49 M: unix seek-handle ( n seek-type handle -- )
50     swap {
51         { io:seek-absolute [ SEEK_SET ] }
52         { io:seek-relative [ SEEK_CUR ] }
53         { io:seek-end [ SEEK_END ] }
54         [ io:bad-seek-type ]
55     } case
56     [ fd>> swap ] dip lseek io-error ;
57
58 SYMBOL: +retry+ ! just try the operation again without blocking
59 SYMBOL: +input+
60 SYMBOL: +output+
61
62 ERROR: io-timeout ;
63
64 M: io-timeout summary drop "I/O operation timed out" ;
65
66 : wait-for-fd ( handle event -- )
67     dup +retry+ eq? [ 2drop ] [
68         '[
69             swap handle-fd mx get-global _ {
70                 { +input+ [ add-input-callback ] }
71                 { +output+ [ add-output-callback ] }
72             } case
73         ] "I/O" suspend nip [ io-timeout ] when
74     ] if ;
75
76 : wait-for-port ( port event -- )
77     '[ handle>> _ wait-for-fd ] with-timeout ;
78
79 ! Some general stuff
80 CONSTANT: file-mode OCT: 0666
81  
82 ! Readers
83 : (refill) ( port -- n )
84     [ handle>> ]
85     [ buffer>> buffer-end ]
86     [ buffer>> buffer-capacity ] tri read ;
87
88 ! Returns an event to wait for which will ensure completion of
89 ! this request
90 GENERIC: refill ( port handle -- event/f )
91
92 M: fd refill
93     fd>> over buffer>> [ buffer-end ] [ buffer-capacity ] bi read
94     {
95         { [ dup 0 >= ] [ swap buffer>> n>buffer f ] }
96         { [ errno EINTR = ] [ 2drop +retry+ ] }
97         { [ errno EAGAIN = ] [ 2drop +input+ ] }
98         [ (io-error) ]
99     } cond ;
100
101 M: unix (wait-to-read) ( port -- )
102     dup
103     dup handle>> dup check-disposed refill dup
104     [ dupd wait-for-port (wait-to-read) ] [ 2drop ] if ;
105
106 ! Writers
107 GENERIC: drain ( port handle -- event/f )
108
109 M: fd drain
110     fd>> over buffer>> [ buffer@ ] [ buffer-length ] bi write
111     {
112         { [ dup 0 >= ] [
113             over buffer>> buffer-consume
114             buffer>> buffer-empty? f +output+ ?
115         ] }
116         { [ errno EINTR = ] [ 2drop +retry+ ] }
117         { [ errno EAGAIN = ] [ 2drop +output+ ] }
118         [ (io-error) ]
119     } cond ;
120
121 M: unix (wait-to-write) ( port -- )
122     dup
123     dup handle>> dup check-disposed drain
124     dup [ wait-for-port ] [ 2drop ] if ;
125
126 M: unix io-multiplex ( ms/f -- )
127     mx get-global wait-for-events ;
128
129 ! On Unix, you're not supposed to set stdin to non-blocking
130 ! because the fd might be shared with another process (either
131 ! parent or child). So what we do is have the VM start a thread
132 ! which pumps data from the real stdin to a pipe. We set the
133 ! pipe to non-blocking, and read from it instead of the real
134 ! stdin. Very crufty, but it will suffice until we get native
135 ! threading support at the language level.
136 TUPLE: stdin control size data disposed ;
137
138 M: stdin dispose*
139     [
140         [ control>> &dispose drop ]
141         [ size>> &dispose drop ]
142         [ data>> &dispose drop ]
143         tri
144     ] with-destructors ;
145
146 : wait-for-stdin ( stdin -- n )
147     [ control>> CHAR: X over io:stream-write1 io:stream-flush ]
148     [ size>> "ssize_t" heap-size swap io:stream-read *int ]
149     bi ;
150
151 :: refill-stdin ( buffer stdin size -- )
152     stdin data>> handle-fd buffer buffer-end size read
153     dup 0 < [
154         drop
155         errno EINTR = [ buffer stdin size refill-stdin ] [ (io-error) ] if
156     ] [
157         size = [ "Error reading stdin pipe" throw ] unless
158         size buffer n>buffer
159     ] if ;
160
161 M: stdin refill
162     [ buffer>> ] [ dup wait-for-stdin ] bi* refill-stdin f ;
163
164 : control-write-fd ( -- fd ) &: control_write *uint ;
165
166 : size-read-fd ( -- fd ) &: size_read *uint ;
167
168 : data-read-fd ( -- fd ) &: stdin_read *uint ;
169
170 : <stdin> ( -- stdin )
171     stdin new
172         control-write-fd <fd> <output-port> >>control
173         size-read-fd <fd> init-fd <input-port> >>size
174         data-read-fd <fd> >>data ;
175
176 M: unix init-stdio
177     <stdin> <input-port>
178     1 <fd> <output-port>
179     2 <fd> <output-port>
180     set-stdio ;
181
182 ! mx io-task for embedding an fd-based mx inside another mx
183 TUPLE: mx-port < port mx ;
184
185 : <mx-port> ( mx -- port )
186     dup fd>> mx-port <port> swap >>mx ;
187
188 : multiplexer-error ( n -- n )
189     dup 0 < [
190         errno [ EAGAIN = ] [ EINTR = ] bi or
191         [ drop 0 ] [ (io-error) ] if
192     ] when ;
193
194 : ?flag ( n mask symbol -- n )
195     pick rot bitand 0 > [ , ] [ drop ] if ;