factor/core/io/unix/io.factor

215 lines
5.7 KiB
Factor
Raw Normal View History

! Copyright (C) 2004, 2007 Slava Pestov.
2006-04-29 17:23:16 -04:00
! See http://factorcode.org/license.txt for BSD license.
USING: alien bit-arrays errors generic hashtables io
kernel kernel-internals math nonblocking-io sequences strings
sbufs threads unix-internals vectors buffers ;
IN: io-internals
2005-04-17 18:34:09 -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-06-13 01:42:16 -04:00
! Global variables
SYMBOL: read-fdset
SYMBOL: read-tasks
SYMBOL: write-fdset
SYMBOL: write-tasks
2005-04-14 19:37:13 -04:00
! Some general stuff
: file-mode OCT: 0600 ;
: (io-error) ( -- * ) err_no strerror throw ;
2005-04-14 19:37:13 -04:00
2006-01-28 15:49:31 -05:00
: check-null ( n -- ) zero? [ (io-error) ] when ;
: io-error ( n -- ) 0 < [ (io-error) ] when ;
M: integer init-handle ( fd -- )
2006-07-19 17:15:13 -04:00
#! We drop the error code rather than calling io-error,
#! since on OS X 10.3, this operation fails from init-io
#! when running the Factor.app (presumably because fd 0 and
#! 1 are closed).
F_SETFL O_NONBLOCK fcntl drop ;
2005-04-14 19:37:13 -04:00
2005-06-18 21:15:07 -04:00
: pending-error ( port -- )
2005-09-17 22:25:18 -04:00
dup port-error f rot set-port-error [ throw ] when* ;
2005-04-14 19:37:13 -04:00
: report-error ( error port -- )
[ "Error on fd " % dup port-handle # ": " % swap % ] "" make
swap set-port-error ;
2006-11-08 22:57:47 -05:00
: ignorable-error? ( n -- ? )
dup EAGAIN number= swap EINTR number= or ;
2005-06-18 21:15:07 -04:00
: defer-error ( port -- ? )
#! Return t if it is an unrecoverable error.
2006-11-08 22:57:47 -05:00
err_no dup ignorable-error?
2005-09-24 15:21:17 -04:00
[ 2drop f ] [ strerror swap report-error t ] if ;
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 ;
2006-08-15 04:57:12 -04:00
C: io-task ( port -- task )
[ set-io-task-port ] keep
2006-06-17 02:29:46 -04:00
V{ } clone over set-io-task-callbacks ;
2005-04-14 19:37:13 -04:00
! Multiplexer
2005-04-12 18:31:50 -04:00
GENERIC: do-io-task ( task -- ? )
2005-06-13 01:42:16 -04:00
GENERIC: task-container ( task -- vector )
2005-05-23 20:56:38 -04:00
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 -- )
2006-06-17 02:29:46 -04:00
[ io-task-callbacks push ] keep
2005-06-13 01:42:16 -04:00
dup io-task-fd over task-container 2dup hash [
2006-05-09 21:37:07 -04:00
"Cannot perform multiple reads from the same port" throw
2005-04-14 19:37:13 -04:00
] when set-hash ;
2005-04-08 23:50:36 -04:00
2005-04-14 19:37:13 -04:00
: remove-io-task ( task -- )
2005-06-13 01:42:16 -04:00
dup io-task-fd swap task-container remove-hash ;
2005-04-08 23:50:36 -04:00
2006-06-17 02:29:46 -04:00
: pop-callbacks ( task -- )
dup io-task-callbacks swap remove-io-task
[ schedule-thread ] each ;
2005-04-03 18:28:55 -04:00
2005-06-13 01:42:16 -04:00
: handle-fd ( task -- )
2006-06-17 02:29:46 -04:00
dup io-task-port touch-port
dup do-io-task [ pop-callbacks ] [ drop ] if ;
2005-04-03 18:28:55 -04:00
2005-06-13 01:42:16 -04:00
: handle-fdset ( fdset tasks -- )
2005-04-14 19:37:13 -04:00
[
2005-11-27 17:45:48 -05:00
nip dup io-task-port timeout? [
dup io-task-port "Timeout" swap report-error
2006-06-17 02:29:46 -04:00
nip pop-callbacks
] [
tuck io-task-fd swap nth
2005-09-24 15:21:17 -04:00
[ handle-fd ] [ drop ] if
] if
2005-06-13 01:42:16 -04:00
] hash-each-with ;
2005-05-23 20:56:38 -04:00
2006-08-15 04:57:12 -04:00
: init-fdset ( fdset tasks -- fdset )
>r dup dup clear-bits r>
[ drop t swap rot set-nth ] hash-each-with
bit-array-store ;
2005-05-23 20:56:38 -04:00
2006-05-11 01:46:32 -04:00
: read-fdset/tasks
read-fdset get-global read-tasks get-global ;
: write-fdset/tasks
write-fdset get-global write-tasks get-global ;
2005-06-13 01:42:16 -04:00
: init-fdsets ( -- read write except )
2006-05-11 01:46:32 -04:00
read-fdset/tasks init-fdset
write-fdset/tasks init-fdset
f ;
2005-04-14 19:37:13 -04:00
2006-11-08 22:57:47 -05:00
: (io-multiplex) ( ms -- )
[ FD_SETSIZE init-fdsets ] keep make-timeval select 0 < [
err_no ignorable-error? [ (io-multiplex) ] [ drop ] if
] [
drop
] if ;
2006-08-16 21:55:53 -04:00
: io-multiplex ( ms -- )
2006-11-08 22:57:47 -05:00
(io-multiplex)
2006-05-11 01:46:32 -04:00
read-fdset/tasks handle-fdset
write-fdset/tasks handle-fdset ;
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-03 16:55:56 -04:00
: reader-eof ( reader -- )
2006-09-16 00:20:52 -04:00
dup buffer-empty? [ t over set-port-eof? ] when drop ;
2005-04-03 18:28:55 -04:00
: (refill) ( port -- n )
dup port-handle over buffer-end rot buffer-capacity read ;
2005-06-18 21:15:07 -04:00
: refill ( port -- ? )
#! Return f if there is a recoverable error
2006-08-05 20:14:14 -04:00
dup buffer-empty? [
2005-06-18 21:15:07 -04:00
dup (refill) dup 0 >= [
swap n>buffer t
] [
drop defer-error
2005-09-24 15:21:17 -04:00
] if
2005-04-27 01:40:09 -04:00
] [
2005-06-18 21:15:07 -04:00
drop t
2005-09-24 15:21:17 -04:00
] if ;
2005-04-03 18:28:55 -04:00
TUPLE: read-task ;
2006-08-05 20:14:14 -04:00
C: read-task ( port -- task )
swap <io-task> over set-delegate ;
2006-08-05 20:14:14 -04:00
M: read-task do-io-task
2006-09-16 00:20:52 -04:00
io-task-port dup refill
[ [ reader-eof ] [ drop ] if ] keep ;
2006-08-05 20:14:14 -04:00
2006-04-27 21:36:29 -04:00
M: read-task task-container drop read-tasks get-global ;
2005-04-14 01:32:06 -04:00
M: input-port (wait-to-read)
[ swap <read-task> add-io-task stop ] callcc0
pending-error ;
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 ;
: write-step ( port -- ? )
dup port-handle over buffer@ pick buffer-length write
dup 0 >= [ swap buffer-consume f ] [ drop defer-error ] if ;
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
2006-04-28 00:03:48 -04:00
io-task-port dup buffer-length zero? over port-error or
[ 0 swap buffer-reset t ] [ write-step ] if ;
2005-04-12 18:31:50 -04:00
2006-04-27 21:36:29 -04:00
M: write-task task-container drop write-tasks get-global ;
2005-04-14 01:32:06 -04:00
2005-04-14 19:37:13 -04:00
: add-write-io-task ( callback task -- )
2006-04-29 17:22:42 -04:00
dup io-task-fd write-tasks get-global hash
2006-06-17 02:29:46 -04:00
[ io-task-callbacks push ] [ add-io-task ] ?if ;
2005-04-14 01:32:06 -04:00
: (wait-to-write) ( port -- )
2005-09-18 23:22:58 -04:00
[ swap <write-task> add-write-io-task stop ] callcc0 drop ;
2005-04-14 01:32:06 -04:00
: port-flush ( port -- )
dup buffer-empty? [ drop ] [ (wait-to-write) ] if ;
M: output-port stream-flush
dup port-flush pending-error ;
M: port stream-close
2005-09-18 23:22:58 -04:00
dup port-type closed eq? [
2006-04-28 00:03:48 -04:00
dup port-type >r closed over set-port-type r>
output eq? [ dup port-flush ] when
dup port-handle close
dup delegate [ buffer-free ] when*
f over set-delegate
2005-09-18 23:22:58 -04:00
] unless drop ;
2005-04-17 18:34:09 -04:00
! Make a duplex stream for reading/writing a pair of fds
: open-r/w ( path -- fd ) O_RDWR file-mode open dup io-error ;
2005-12-16 21:12:35 -05:00
: <fd-stream> ( infd outfd -- stream )
>r <reader> r> <writer> <duplex-stream> ;
2005-04-14 01:32:06 -04:00
USE: io
2005-04-24 23:02:19 -04:00
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 [
H{ } clone read-tasks set
FD_SETSIZE 8 * <bit-array> read-fdset set
H{ } clone write-tasks set
FD_SETSIZE 8 * <bit-array> write-fdset set
2005-12-16 21:12:35 -05:00
0 1 <fd-stream> stdio set
2005-08-23 15:50:32 -04:00
] bind ;