factor/library/unix/io.factor

359 lines
8.7 KiB
Factor
Raw Normal View History

2005-04-14 19:37:13 -04:00
! Copyright (C) 2004, 2005 Slava Pestov.
! 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
sequences stdio streams strings threads unix-internals vectors ;
! We want this bind to shadow the bind system call from
! unix-internals
USING: namespaces ;
2005-04-14 01:32:06 -04:00
! These let us load the code into a CFactor instance using the
! old C-based I/O. They will be removed soon.
2005-04-09 00:09:49 -04:00
FORGET: can-read-line?
FORGET: can-read-count?
FORGET: can-write?
2005-04-14 01:32:06 -04:00
FORGET: add-write-io-task
2005-04-14 19:37:13 -04:00
FORGET: blocking-read-line
FORGET: blocking-write
FORGET: wait-to-read
FORGET: wait-to-read-line
FORGET: wait-to-write
2005-04-09 00:09:49 -04: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-17 18:34:09 -04:00
F_SETFL O_NONBLOCK 1 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 )
[ >r <buffer> r> set-delegate ] keep
[ >r dup init-handle r> set-port-handle ] keep ;
: buffered-port 8192 <port> ;
: >port< dup port-handle swap delegate ;
: pending-error ( reader -- ) port-error throw ;
: 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
: handle-fd ( fd -- quot )
2005-04-14 19:37:13 -04:00
io-tasks get hash dup do-io-task [
pop-callback
2005-04-14 19:37:13 -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
] [
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 ;
: io-multiplex ( -- )
2005-04-17 18:34:09 -04:00
make-pollfds 2dup -1 poll drop do-io-tasks io-multiplex ;
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
TUPLE: reader line ready? ;
C: reader ( handle -- reader )
[ >r buffered-port r> set-delegate ] keep ;
: 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-17 18:34:09 -04:00
M: reader stream-close ( stream -- ) port-handle close ;
! Reading lines
2005-04-03 18:28:55 -04:00
: read-line-loop ( line buffer -- ? )
2005-04-03 16:55:56 -04:00
dup buffer-length 0 = [
2drop f
] [
2005-04-05 22:18:36 -04:00
dup buffer-pop dup CHAR: \n = [
2005-04-03 16:55:56 -04:00
3drop t
] [
2005-04-19 20:28:01 -04:00
pick push read-line-loop
2005-04-03 16:55:56 -04:00
] ifte
] ifte ;
: read-line-step ( reader -- ? )
[ dup reader-line swap 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 -- ? )
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
: read-step ( port -- )
2005-04-08 23:50:36 -04:00
>port<
2005-04-17 18:34:09 -04:00
tuck dup buffer-end swap buffer-capacity read
dup 0 >= [ swap n>buffer ] [ drop postpone-error ] ifte ;
2005-04-03 18:28:55 -04:00
: refill ( reader -- )
dup buffer-length 0 = [ read-step ] [ 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 ;
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 )
drop read-events ;
: wait-to-read-line ( port -- )
dup can-read-line? [
drop
] [
[
swap <read-line-task> add-io-task io-multiplex
] callcc0 drop
] ifte ;
M: reader stream-readln ( stream -- line )
dup wait-to-read-line read-fin ;
! Reading character counts
2005-04-03 18:28:55 -04:00
: read-count-step ( count reader -- ? )
dup reader-line -rot >r over length - r>
2dup buffer-fill <= [
2005-04-19 20:28:01 -04:00
buffer> swap nappend t
2005-04-03 18:28:55 -04:00
] [
2005-04-19 20:28:01 -04:00
buffer>> nip swap nappend f
2005-04-03 18:28:55 -04:00
] ifte ;
: can-read-count? ( count reader -- ? )
dup pending-error
2dup reader-line length >= [
2drop t
] [
2dup init-reader read-count-step
] ifte ;
2005-04-12 18:31:50 -04:00
TUPLE: read-task count ;
2005-04-14 19:37:13 -04:00
C: read-task ( port -- task )
2005-04-12 18:31:50 -04:00
[ >r <io-task> r> set-delegate ] keep ;
: >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 -- ? )
>read-task< dup refill dup eof? [
2005-04-12 18:31:50 -04:00
nip reader-eof t
] [
read-count-step
] ifte ;
2005-04-14 01:32:06 -04:00
M: read-task io-task-events ( task -- events )
drop read-events ;
2005-04-14 19:37:13 -04:00
: wait-to-read ( count port -- )
2dup can-read-count? [
2drop
] [
[
swap <read-task> add-io-task io-multiplex
] callcc0 2drop
] ifte ;
M: reader stream-read ( count stream -- string )
2dup wait-to-read read-fin ;
! 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-08 23:50:36 -04:00
C: writer ( fd -- writer )
[ >r buffered-port r> set-delegate ] keep ;
: write-step ( fd buffer -- )
2005-04-17 18:34:09 -04:00
tuck dup buffer@ swap buffer-length write dup 0 >= [
swap buffer-consume
] [
drop postpone-error
] ifte ;
: 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 [
buffer-extend t
] [
[ buffer-fill + ] keep buffer-capacity <=
] ifte ;
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
] [
>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 )
drop write-events ;
: 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 -- )
[
swap <write-task> add-write-io-task io-multiplex
] 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-17 18:34:09 -04:00
dup stream-flush port-handle close ;
! Make a duplex stream for reading/writing a pair of fds
: <fd-stream> ( infd outfd flush? -- )
>r >r <reader> r> <writer> r> <duplex-stream> ;
2005-04-14 01:32:06 -04:00
2005-04-14 19:37:13 -04:00
! Copying from a reader to a writer
2005-04-14 01:32:06 -04:00
2005-04-14 19:37:13 -04:00
: can-copy? ( from -- ? )
dup eof? [ read-step ] [ drop t ] ifte ;
2005-04-12 18:31:50 -04:00
2005-04-14 19:37:13 -04:00
: copy-from-task ( from to -- ? )
over can-copy? [
over eof? [
2drop t
] [
over buffer-fill over can-write? [
dupd buffer-append 0 swap buffer-reset
] [
2drop
] ifte f
] ifte
] [
2drop f
] ifte ;
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
] bind ;