2005-04-14 19:37:13 -04:00
|
|
|
! Copyright (C) 2004, 2005 Slava Pestov.
|
2005-03-29 19:58:22 -05:00
|
|
|
! See http://factor.sf.net/license.txt for BSD license.
|
|
|
|
IN: io-internals
|
2005-04-17 18:34:09 -04:00
|
|
|
USING: errors generic hashtables kernel lists math
|
2005-04-24 23:02:19 -04:00
|
|
|
sequences streams strings threads unix-internals vectors ;
|
2005-04-17 18:34:09 -04:00
|
|
|
|
2005-04-22 20:09:46 -04:00
|
|
|
! We want namespaces::bind to shadow the bind system call from
|
2005-04-17 18:34:09 -04:00
|
|
|
! unix-internals
|
|
|
|
USING: namespaces ;
|
2005-03-29 19:58:22 -05:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
! Some general stuff
|
|
|
|
: file-mode OCT: 0600 ;
|
|
|
|
|
|
|
|
: io-error ( n -- ) 0 < [ errno strerror throw ] when ;
|
|
|
|
|
|
|
|
: init-handle ( fd -- )
|
2005-04-30 00:43:39 -04:00
|
|
|
F_SETFL O_NONBLOCK fcntl io-error ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Common delegate of native stream readers and writers
|
|
|
|
TUPLE: port handle buffer error ;
|
|
|
|
|
|
|
|
C: port ( handle buffer -- port )
|
2005-04-24 23:02:19 -04:00
|
|
|
[
|
|
|
|
>r dup 0 > [ <buffer> ] [ drop f ] ifte r> set-delegate
|
|
|
|
] keep
|
2005-04-14 19:37:13 -04:00
|
|
|
[ >r dup init-handle r> set-port-handle ] keep ;
|
|
|
|
|
2005-04-24 23:02:19 -04:00
|
|
|
M: port stream-close ( port -- )
|
2005-04-29 02:37:12 -04:00
|
|
|
dup port-handle close
|
|
|
|
delegate [ buffer-free ] when* ;
|
2005-04-24 23:02:19 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: buffered-port 8192 <port> ;
|
|
|
|
|
|
|
|
: >port< dup port-handle swap delegate ;
|
|
|
|
|
|
|
|
: pending-error ( reader -- ) port-error throw ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: postpone-error ( reader -- )
|
|
|
|
errno strerror swap set-port-error ;
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
! Associates a port with a list of continuations waiting on the
|
|
|
|
! port to finish I/O
|
2005-04-14 01:32:06 -04:00
|
|
|
TUPLE: io-task port callbacks ;
|
2005-04-14 19:37:13 -04:00
|
|
|
C: io-task ( port -- ) [ set-io-task-port ] keep ;
|
|
|
|
|
|
|
|
! Multiplexer
|
2005-04-12 18:31:50 -04:00
|
|
|
GENERIC: do-io-task ( task -- ? )
|
2005-04-14 01:32:06 -04:00
|
|
|
GENERIC: io-task-events ( task -- events )
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2005-04-14 01:32:06 -04:00
|
|
|
! A hashtable in the global namespace mapping fd numbers to
|
|
|
|
! io-tasks. This is not a vector, since we need a quick way
|
|
|
|
! to find the number of elements, and a hashtable gives us
|
|
|
|
! this with the hash-size call.
|
2005-04-12 18:31:50 -04:00
|
|
|
SYMBOL: io-tasks
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: io-task-fd io-task-port port-handle ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: add-io-task ( callback task -- )
|
|
|
|
[ >r unit r> set-io-task-callbacks ] keep
|
|
|
|
dup io-task-fd io-tasks get 2dup hash [
|
|
|
|
"Cannot perform multiple I/O ops on the same port" throw
|
|
|
|
] when set-hash ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: remove-io-task ( task -- )
|
|
|
|
io-task-fd io-tasks get remove-hash ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: pop-callback ( task -- callback )
|
|
|
|
dup io-task-callbacks uncons dup [
|
|
|
|
rot set-io-task-callbacks
|
|
|
|
] [
|
|
|
|
drop swap remove-io-task
|
|
|
|
] ifte ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: handle-fd ( fd -- quot )
|
2005-04-14 19:37:13 -04:00
|
|
|
io-tasks get hash dup do-io-task [
|
2005-04-15 22:28:37 -04:00
|
|
|
pop-callback
|
2005-04-14 19:37:13 -04:00
|
|
|
] [
|
2005-04-15 22:28:37 -04:00
|
|
|
drop f
|
2005-04-14 19:37:13 -04:00
|
|
|
] ifte ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: do-io-tasks ( pollfds n -- )
|
|
|
|
[
|
|
|
|
dup pick pollfd-nth dup pollfd-revents 0 = [
|
|
|
|
drop
|
|
|
|
] [
|
2005-04-15 22:28:37 -04:00
|
|
|
pollfd-fd handle-fd [ call ] when*
|
2005-04-14 19:37:13 -04:00
|
|
|
] ifte
|
|
|
|
] repeat drop ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: init-pollfd ( task pollfd -- )
|
|
|
|
over io-task-fd over set-pollfd-fd
|
|
|
|
swap io-task-events swap set-pollfd-events ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: make-pollfds ( -- pollfds n )
|
|
|
|
io-tasks get dup hash-size [
|
|
|
|
swap >r <pollfd-array> 0 swap r> hash-values [
|
|
|
|
( n pollfds iotask )
|
|
|
|
pick pick pollfd-nth init-pollfd >r 1 + r>
|
|
|
|
] each nip
|
|
|
|
] keep ;
|
|
|
|
|
2005-04-30 17:17:10 -04:00
|
|
|
: io-multiplex ( timeout -- )
|
2005-05-01 01:25:22 -04:00
|
|
|
>r make-pollfds 2dup r> poll drop do-io-tasks ;
|
2005-04-30 17:17:10 -04:00
|
|
|
|
|
|
|
: pending-io? ( -- ? )
|
|
|
|
#! Output if there are waiting I/O requests.
|
|
|
|
io-tasks get hash-size 0 > ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Readers
|
|
|
|
|
|
|
|
: open-read ( path -- fd )
|
2005-04-17 18:34:09 -04:00
|
|
|
O_RDONLY file-mode open dup io-error ;
|
2005-04-08 23:50:36 -04:00
|
|
|
|
2005-04-25 03:33:33 -04:00
|
|
|
! The cr slot is set to true by read-line-loop if the last
|
|
|
|
! character read was \r.
|
|
|
|
TUPLE: reader line ready? cr ;
|
2005-04-07 20:02:59 -04:00
|
|
|
|
|
|
|
C: reader ( handle -- reader )
|
|
|
|
[ >r buffered-port r> set-delegate ] keep ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: pop-line ( reader -- str )
|
|
|
|
dup reader-line dup [ sbuf>string ] when >r
|
|
|
|
f over set-reader-line
|
|
|
|
f swap set-reader-ready? r> ;
|
|
|
|
|
|
|
|
: read-fin ( reader -- str )
|
|
|
|
dup pending-error dup reader-ready? [
|
|
|
|
pop-line
|
|
|
|
] [
|
|
|
|
"reader not ready" throw
|
|
|
|
] ifte ;
|
|
|
|
|
2005-04-25 03:33:33 -04:00
|
|
|
: reader-cr> ( reader -- ? )
|
|
|
|
dup reader-cr >r f swap set-reader-cr r> ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
! Reading lines
|
2005-04-25 03:33:33 -04:00
|
|
|
: read-line-char ( reader ch -- )
|
|
|
|
f pick set-reader-cr swap reader-line push ;
|
|
|
|
|
|
|
|
: read-line-loop ( reader -- ? )
|
2005-04-03 16:55:56 -04:00
|
|
|
dup buffer-length 0 = [
|
2005-04-25 03:33:33 -04:00
|
|
|
drop f
|
2005-04-03 16:55:56 -04:00
|
|
|
] [
|
2005-04-25 03:33:33 -04:00
|
|
|
dup buffer-pop
|
|
|
|
dup CHAR: \r = [
|
|
|
|
drop t swap set-reader-cr t
|
2005-04-03 16:55:56 -04:00
|
|
|
] [
|
2005-04-25 03:33:33 -04:00
|
|
|
dup CHAR: \n = [
|
|
|
|
drop dup reader-cr> [
|
|
|
|
read-line-loop
|
|
|
|
] [
|
|
|
|
drop t
|
|
|
|
] ifte
|
|
|
|
] [
|
|
|
|
dupd read-line-char read-line-loop
|
|
|
|
] ifte
|
2005-04-03 16:55:56 -04:00
|
|
|
] ifte
|
|
|
|
] ifte ;
|
|
|
|
|
2005-04-03 19:02:50 -04:00
|
|
|
: read-line-step ( reader -- ? )
|
2005-04-25 03:33:33 -04:00
|
|
|
[ read-line-loop dup ] keep set-reader-ready? ;
|
2005-04-03 16:55:56 -04:00
|
|
|
|
2005-04-03 18:28:55 -04:00
|
|
|
: init-reader ( count reader -- ) >r <sbuf> r> set-reader-line ;
|
2005-04-03 16:55:56 -04:00
|
|
|
|
|
|
|
: prepare-line ( reader -- ? )
|
2005-04-03 19:02:50 -04:00
|
|
|
80 over init-reader read-line-step ;
|
2005-04-03 16:55:56 -04:00
|
|
|
|
|
|
|
: can-read-line? ( reader -- ? )
|
2005-04-03 18:28:55 -04:00
|
|
|
dup pending-error
|
2005-04-03 16:55:56 -04:00
|
|
|
dup reader-ready? [ drop t ] [ prepare-line ] ifte ;
|
|
|
|
|
|
|
|
: reader-eof ( reader -- )
|
|
|
|
dup reader-line dup [
|
2005-04-03 18:28:55 -04:00
|
|
|
length 0 = [ f over set-reader-line ] when
|
2005-04-03 16:55:56 -04:00
|
|
|
] [
|
|
|
|
drop
|
|
|
|
] ifte t swap set-reader-ready? ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-27 01:40:09 -04:00
|
|
|
: refill ( port -- )
|
|
|
|
dup buffer-length 0 = [
|
|
|
|
>port<
|
|
|
|
tuck dup buffer-end swap buffer-capacity read
|
|
|
|
dup 0 >= [ swap n>buffer ] [ drop postpone-error ] ifte
|
|
|
|
] [
|
|
|
|
drop
|
|
|
|
] ifte ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-08 23:50:36 -04:00
|
|
|
: eof? ( buffer -- ? ) buffer-fill 0 = ;
|
2005-04-03 18:28:55 -04:00
|
|
|
|
2005-04-12 18:31:50 -04:00
|
|
|
TUPLE: read-line-task ;
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
C: read-line-task ( port -- task )
|
2005-04-12 18:31:50 -04:00
|
|
|
[ >r <io-task> r> set-delegate ] keep ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
M: read-line-task do-io-task ( task -- ? )
|
2005-04-12 18:31:50 -04:00
|
|
|
io-task-port dup refill dup eof? [
|
|
|
|
reader-eof t
|
|
|
|
] [
|
|
|
|
read-line-step
|
|
|
|
] ifte ;
|
|
|
|
|
2005-04-14 01:32:06 -04:00
|
|
|
M: read-line-task io-task-events ( task -- events )
|
2005-04-30 00:43:39 -04:00
|
|
|
drop POLLIN ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: wait-to-read-line ( port -- )
|
|
|
|
dup can-read-line? [
|
2005-04-25 03:33:33 -04:00
|
|
|
[ swap <read-line-task> add-io-task stop ] callcc0
|
|
|
|
] unless drop ;
|
2005-04-15 22:28:37 -04:00
|
|
|
|
|
|
|
M: reader stream-readln ( stream -- line )
|
|
|
|
dup wait-to-read-line read-fin ;
|
|
|
|
|
2005-04-27 01:40:09 -04:00
|
|
|
: trailing-cr ( reader -- )
|
|
|
|
#! Handle a corner case. If the previous request was a line
|
|
|
|
#! read and the line ends with \r\n, the reader stopped
|
|
|
|
#! reading at \r and set the reader-cr flag to true. But we
|
|
|
|
#! must ignore the \n.
|
|
|
|
dup buffer-length 1 >= over reader-cr and [
|
|
|
|
dup buffer-peek CHAR: \n = [
|
|
|
|
1 swap buffer-consume
|
|
|
|
] [
|
|
|
|
drop
|
|
|
|
] ifte
|
|
|
|
] [
|
|
|
|
drop
|
|
|
|
] ifte ;
|
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
! Reading character counts
|
2005-04-27 01:40:09 -04:00
|
|
|
: read-loop ( count reader -- ? )
|
|
|
|
dup trailing-cr
|
2005-04-25 03:33:33 -04:00
|
|
|
dup reader-line -rot >r over length - ( remaining) r>
|
2005-04-27 01:40:09 -04:00
|
|
|
2dup buffer-length <= [
|
2005-04-25 03:33:33 -04:00
|
|
|
buffer> nappend t
|
2005-04-03 18:28:55 -04:00
|
|
|
] [
|
2005-04-25 03:33:33 -04:00
|
|
|
buffer>> nip nappend f
|
2005-04-03 18:28:55 -04:00
|
|
|
] ifte ;
|
|
|
|
|
2005-04-27 01:40:09 -04:00
|
|
|
: read-step ( count reader -- ? )
|
|
|
|
[ read-loop dup ] keep set-reader-ready? ;
|
|
|
|
|
2005-04-03 18:28:55 -04:00
|
|
|
: can-read-count? ( count reader -- ? )
|
|
|
|
dup pending-error
|
2005-04-25 03:33:33 -04:00
|
|
|
2dup init-reader
|
|
|
|
2dup reader-line length <= [
|
|
|
|
t swap set-reader-ready? drop t
|
2005-04-03 18:28:55 -04:00
|
|
|
] [
|
2005-04-27 01:40:09 -04:00
|
|
|
read-step
|
2005-04-03 18:28:55 -04:00
|
|
|
] ifte ;
|
|
|
|
|
2005-04-12 18:31:50 -04:00
|
|
|
TUPLE: read-task count ;
|
|
|
|
|
2005-04-25 03:33:33 -04:00
|
|
|
C: read-task ( count port -- task )
|
|
|
|
[ >r <io-task> r> set-delegate ] keep
|
|
|
|
[ set-read-task-count ] keep ;
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: >read-task< dup read-task-count swap io-task-port ;
|
|
|
|
|
2005-04-17 18:34:09 -04:00
|
|
|
M: read-task do-io-task ( task -- ? )
|
2005-04-15 22:28:37 -04:00
|
|
|
>read-task< dup refill dup eof? [
|
2005-04-12 18:31:50 -04:00
|
|
|
nip reader-eof t
|
|
|
|
] [
|
2005-04-27 01:40:09 -04:00
|
|
|
read-step
|
2005-04-12 18:31:50 -04:00
|
|
|
] ifte ;
|
|
|
|
|
2005-04-14 01:32:06 -04:00
|
|
|
M: read-task io-task-events ( task -- events )
|
2005-04-30 00:43:39 -04:00
|
|
|
drop POLLIN ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: wait-to-read ( count port -- )
|
|
|
|
2dup can-read-count? [
|
2005-04-25 03:33:33 -04:00
|
|
|
[ -rot <read-task> add-io-task stop ] callcc0
|
|
|
|
] unless 2drop ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
M: reader stream-read ( count stream -- string )
|
2005-04-25 03:33:33 -04:00
|
|
|
[ wait-to-read ] keep read-fin ;
|
2005-04-14 19:37:13 -04:00
|
|
|
|
|
|
|
! Writers
|
|
|
|
|
|
|
|
: open-write ( path -- fd )
|
2005-04-17 18:34:09 -04:00
|
|
|
O_WRONLY O_CREAT bitor O_TRUNC bitor file-mode open
|
2005-04-14 19:37:13 -04:00
|
|
|
dup io-error ;
|
|
|
|
|
2005-04-08 23:50:36 -04:00
|
|
|
TUPLE: writer ;
|
2005-04-03 19:02:50 -04:00
|
|
|
|
2005-04-08 23:50:36 -04:00
|
|
|
C: writer ( fd -- writer )
|
|
|
|
[ >r buffered-port r> set-delegate ] keep ;
|
2005-04-07 20:02:59 -04:00
|
|
|
|
2005-04-15 22:28:37 -04:00
|
|
|
: write-step ( fd buffer -- )
|
2005-04-17 18:34:09 -04:00
|
|
|
tuck dup buffer@ swap buffer-length write dup 0 >= [
|
2005-04-15 22:28:37 -04:00
|
|
|
swap buffer-consume
|
|
|
|
] [
|
|
|
|
drop postpone-error
|
|
|
|
] ifte ;
|
2005-04-03 19:02:50 -04:00
|
|
|
|
2005-04-07 20:02:59 -04:00
|
|
|
: can-write? ( len writer -- ? )
|
|
|
|
#! If the buffer is empty and the string is too long,
|
|
|
|
#! extend the buffer.
|
|
|
|
dup pending-error
|
2005-04-08 23:50:36 -04:00
|
|
|
dup eof? >r 2dup buffer-capacity > r> and [
|
2005-04-07 20:02:59 -04:00
|
|
|
buffer-extend t
|
|
|
|
] [
|
|
|
|
[ buffer-fill + ] keep buffer-capacity <=
|
|
|
|
] ifte ;
|
2005-04-03 19:02:50 -04:00
|
|
|
|
2005-04-12 18:31:50 -04:00
|
|
|
TUPLE: write-task ;
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
C: write-task ( port -- task )
|
2005-04-12 18:31:50 -04:00
|
|
|
[ >r <io-task> r> set-delegate ] keep ;
|
|
|
|
|
|
|
|
M: write-task do-io-task
|
2005-04-14 01:32:06 -04:00
|
|
|
io-task-port dup buffer-length 0 = over port-error or [
|
2005-04-12 18:31:50 -04:00
|
|
|
0 swap buffer-reset t
|
|
|
|
] [
|
2005-04-15 22:28:37 -04:00
|
|
|
>port< write-step f
|
2005-04-12 18:31:50 -04:00
|
|
|
] ifte ;
|
|
|
|
|
2005-04-14 01:32:06 -04:00
|
|
|
M: write-task io-task-events ( task -- events )
|
2005-04-30 00:43:39 -04:00
|
|
|
drop POLLOUT ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-07 20:02:59 -04:00
|
|
|
: write-fin ( str writer -- )
|
2005-04-14 19:37:13 -04:00
|
|
|
dup pending-error >buffer ;
|
2005-04-12 18:31:50 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: add-write-io-task ( callback task -- )
|
2005-04-14 01:32:06 -04:00
|
|
|
dup io-task-fd io-tasks get hash [
|
|
|
|
dup write-task? [
|
|
|
|
[
|
2005-04-14 19:37:13 -04:00
|
|
|
nip io-task-callbacks cons
|
2005-04-14 01:32:06 -04:00
|
|
|
] keep set-io-task-callbacks
|
|
|
|
] [
|
2005-04-15 22:42:01 -04:00
|
|
|
drop add-io-task
|
2005-04-14 01:32:06 -04:00
|
|
|
] ifte
|
|
|
|
] [
|
|
|
|
add-io-task
|
|
|
|
] ifte* ;
|
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
M: writer stream-flush ( stream -- )
|
|
|
|
[
|
2005-04-23 17:42:42 -04:00
|
|
|
swap <write-task> add-write-io-task stop
|
2005-04-14 19:37:13 -04:00
|
|
|
] callcc0 drop ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
M: writer stream-auto-flush ( stream -- ) drop ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: wait-to-write ( len port -- )
|
|
|
|
tuck can-write? [ drop ] [ stream-flush ] ifte ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
: blocking-write ( str writer -- )
|
|
|
|
over length over wait-to-write write-fin ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
M: writer stream-write-attr ( string style writer -- )
|
|
|
|
nip >r dup string? [ ch>string ] unless r> blocking-write ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-04-14 19:37:13 -04:00
|
|
|
M: writer stream-close ( stream -- )
|
2005-04-24 23:02:19 -04:00
|
|
|
dup stream-flush delegate stream-close ;
|
2005-04-17 18:34:09 -04:00
|
|
|
|
|
|
|
! Make a duplex stream for reading/writing a pair of fds
|
2005-04-22 02:24:38 -04:00
|
|
|
: <fd-stream> ( infd outfd flush? -- stream )
|
2005-04-17 18:34:09 -04:00
|
|
|
>r >r <reader> r> <writer> r> <duplex-stream> ;
|
2005-04-14 01:32:06 -04:00
|
|
|
|
2005-05-03 20:09:04 -04:00
|
|
|
: idle-io-task ( -- )
|
|
|
|
[ schedule-thread 10 io-multiplex ] callcc0 idle-io-task ;
|
|
|
|
|
2005-04-24 23:02:19 -04:00
|
|
|
USE: stdio
|
|
|
|
|
2005-04-17 18:34:09 -04:00
|
|
|
: init-io ( -- )
|
|
|
|
#! Should only be called on startup. Calling this at any
|
|
|
|
#! other time can have unintended consequences.
|
|
|
|
global [
|
|
|
|
<namespace> io-tasks set
|
|
|
|
0 1 t <fd-stream> stdio set
|
2005-05-03 20:09:04 -04:00
|
|
|
] bind
|
|
|
|
[ idle-io-task ] in-thread ;
|