Put that in your pipe and smoke it
parent
ec4acc93c1
commit
ab881cbd7a
|
@ -1,10 +1,11 @@
|
||||||
! Copyright (C) 2004, 2008 Slava Pestov.
|
! Copyright (C) 2004, 2008 Slava Pestov.
|
||||||
! See http://factorcode.org/license.txt for BSD license.
|
! See http://factorcode.org/license.txt for BSD license.
|
||||||
USING: alien generic assocs kernel kernel.private math
|
USING: alien alien.c-types generic assocs kernel kernel.private
|
||||||
io.ports sequences strings structs sbufs threads unix
|
math io.ports sequences strings structs sbufs threads unix
|
||||||
vectors io.buffers io.backend io.encodings math.parser
|
vectors io.buffers io.backend io.encodings math.parser
|
||||||
continuations system libc qualified namespaces io.timeouts
|
continuations system libc qualified namespaces io.timeouts
|
||||||
io.encodings.utf8 destructors accessors summary combinators ;
|
io.encodings.utf8 destructors accessors summary combinators
|
||||||
|
locals ;
|
||||||
QUALIFIED: io
|
QUALIFIED: io
|
||||||
IN: io.unix.backend
|
IN: io.unix.backend
|
||||||
|
|
||||||
|
@ -12,17 +13,19 @@ GENERIC: handle-fd ( handle -- fd )
|
||||||
|
|
||||||
TUPLE: fd fd disposed ;
|
TUPLE: fd fd disposed ;
|
||||||
|
|
||||||
|
: init-fd ( fd -- fd )
|
||||||
|
[
|
||||||
|
|dispose
|
||||||
|
dup fd>> F_SETFL O_NONBLOCK fcntl io-error
|
||||||
|
dup fd>> F_SETFD FD_CLOEXEC fcntl io-error
|
||||||
|
] with-destructors ;
|
||||||
|
|
||||||
: <fd> ( n -- fd )
|
: <fd> ( n -- fd )
|
||||||
#! We drop the error code rather than calling io-error,
|
#! We drop the error code rather than calling io-error,
|
||||||
#! since on OS X 10.3, this operation fails from init-io
|
#! since on OS X 10.3, this operation fails from init-io
|
||||||
#! when running the Factor.app (presumably because fd 0 and
|
#! when running the Factor.app (presumably because fd 0 and
|
||||||
#! 1 are closed).
|
#! 1 are closed).
|
||||||
fd new
|
f fd boa ;
|
||||||
swap
|
|
||||||
[ F_SETFL O_NONBLOCK fcntl drop ]
|
|
||||||
[ F_SETFD FD_CLOEXEC fcntl drop ]
|
|
||||||
[ >>fd ]
|
|
||||||
tri ;
|
|
||||||
|
|
||||||
M: fd dispose
|
M: fd dispose
|
||||||
dup disposed>> [ drop ] [
|
dup disposed>> [ drop ] [
|
||||||
|
@ -146,8 +149,55 @@ M: unix (wait-to-write) ( port -- )
|
||||||
M: unix io-multiplex ( ms/f -- )
|
M: unix io-multiplex ( ms/f -- )
|
||||||
mx get-global wait-for-events ;
|
mx get-global wait-for-events ;
|
||||||
|
|
||||||
|
! On Unix, you're not supposed to set stdin to non-blocking
|
||||||
|
! because the fd might be shared with another process (either
|
||||||
|
! parent or child). So what we do is have the VM start a thread
|
||||||
|
! which pumps data from the real stdin to a pipe. We set the
|
||||||
|
! pipe to non-blocking, and read from it instead of the real
|
||||||
|
! stdin. Very crufty, but it will suffice until we get native
|
||||||
|
! threading support at the language level.
|
||||||
|
TUPLE: stdin control size data ;
|
||||||
|
|
||||||
|
M: stdin dispose
|
||||||
|
[
|
||||||
|
[ control>> &dispose drop ]
|
||||||
|
[ size>> &dispose drop ]
|
||||||
|
[ data>> &dispose drop ]
|
||||||
|
tri
|
||||||
|
] with-destructors ;
|
||||||
|
|
||||||
|
: wait-for-stdin ( stdin -- n )
|
||||||
|
[ control>> CHAR: X over io:stream-write1 io:stream-flush ]
|
||||||
|
[ size>> "uint" heap-size swap io:stream-read *uint ]
|
||||||
|
bi ;
|
||||||
|
|
||||||
|
:: refill-stdin ( buffer stdin size -- )
|
||||||
|
stdin data>> handle-fd buffer buffer-end size read
|
||||||
|
dup 0 < [
|
||||||
|
drop
|
||||||
|
err_no EINTR = [ buffer stdin size refill-stdin ] [ (io-error) ] if
|
||||||
|
] [
|
||||||
|
size = [ "Error reading stdin pipe" throw ] unless
|
||||||
|
size buffer n>buffer
|
||||||
|
] if ;
|
||||||
|
|
||||||
|
M: stdin refill
|
||||||
|
[ buffer>> ] [ dup wait-for-stdin ] bi* refill-stdin f ;
|
||||||
|
|
||||||
|
: control-write-fd ( -- fd ) "control_write" f dlsym *uint ;
|
||||||
|
|
||||||
|
: size-read-fd ( -- fd ) "size_read" f dlsym *uint ;
|
||||||
|
|
||||||
|
: data-read-fd ( -- fd ) "stdin_read" f dlsym *uint ;
|
||||||
|
|
||||||
|
: <stdin> ( -- stdin )
|
||||||
|
control-write-fd <fd> <output-port>
|
||||||
|
size-read-fd <fd> init-fd <input-port>
|
||||||
|
data-read-fd <fd>
|
||||||
|
stdin boa ;
|
||||||
|
|
||||||
M: unix (init-stdio) ( -- )
|
M: unix (init-stdio) ( -- )
|
||||||
0 <fd> <input-port>
|
<stdin> <input-port>
|
||||||
1 <fd> <output-port>
|
1 <fd> <output-port>
|
||||||
2 <fd> <output-port> ;
|
2 <fd> <output-port> ;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ M: unix cd ( path -- ) [ chdir ] unix-system-call drop ;
|
||||||
: open-read ( path -- fd ) O_RDONLY file-mode open-file ;
|
: open-read ( path -- fd ) O_RDONLY file-mode open-file ;
|
||||||
|
|
||||||
M: unix (file-reader) ( path -- stream )
|
M: unix (file-reader) ( path -- stream )
|
||||||
open-read <fd> <input-port> ;
|
open-read <fd> init-fd <input-port> ;
|
||||||
|
|
||||||
: write-flags { O_WRONLY O_CREAT O_TRUNC } flags ; inline
|
: write-flags { O_WRONLY O_CREAT O_TRUNC } flags ; inline
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ M: unix (file-reader) ( path -- stream )
|
||||||
write-flags file-mode open-file ;
|
write-flags file-mode open-file ;
|
||||||
|
|
||||||
M: unix (file-writer) ( path -- stream )
|
M: unix (file-writer) ( path -- stream )
|
||||||
open-write <fd> <output-port> ;
|
open-write <fd> init-fd <output-port> ;
|
||||||
|
|
||||||
: append-flags { O_WRONLY O_APPEND O_CREAT } flags ; inline
|
: append-flags { O_WRONLY O_APPEND O_CREAT } flags ; inline
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ M: unix (file-writer) ( path -- stream )
|
||||||
] with-destructors ;
|
] with-destructors ;
|
||||||
|
|
||||||
M: unix (file-appender) ( path -- stream )
|
M: unix (file-appender) ( path -- stream )
|
||||||
open-append <fd> <output-port> ;
|
open-append <fd> init-fd <output-port> ;
|
||||||
|
|
||||||
: touch-mode ( -- n )
|
: touch-mode ( -- n )
|
||||||
{ O_WRONLY O_APPEND O_CREAT O_EXCL } flags ; foldable
|
{ O_WRONLY O_APPEND O_CREAT O_EXCL } flags ; foldable
|
||||||
|
|
|
@ -30,17 +30,14 @@ USE: unix
|
||||||
} at set-priority
|
} at set-priority
|
||||||
] when* ;
|
] when* ;
|
||||||
|
|
||||||
|
: reset-fd ( fd -- )
|
||||||
|
[ F_SETFL 0 fcntl io-error ] [ F_SETFD 0 fcntl io-error ] bi ;
|
||||||
|
|
||||||
: redirect-fd ( oldfd fd -- )
|
: redirect-fd ( oldfd fd -- )
|
||||||
2dup = [ 2drop ] [ dup2 io-error ] if ;
|
2dup = [ 2drop ] [ dup2 io-error ] if ;
|
||||||
|
|
||||||
: reset-fd ( fd -- )
|
|
||||||
#! We drop the error code because on *BSD, fcntl of
|
|
||||||
#! /dev/null fails.
|
|
||||||
[ F_SETFL 0 fcntl drop ]
|
|
||||||
[ F_SETFD 0 fcntl drop ] bi ;
|
|
||||||
|
|
||||||
: redirect-inherit ( obj mode fd -- )
|
: redirect-inherit ( obj mode fd -- )
|
||||||
2nip reset-fd ;
|
3drop ;
|
||||||
|
|
||||||
: redirect-file ( obj mode fd -- )
|
: redirect-file ( obj mode fd -- )
|
||||||
>r >r normalize-path r> file-mode
|
>r >r normalize-path r> file-mode
|
||||||
|
|
|
@ -23,7 +23,7 @@ TUPLE: linux-monitor < monitor wd inotify watches disposed ;
|
||||||
: wd>monitor ( wd -- monitor ) watches get at ;
|
: wd>monitor ( wd -- monitor ) watches get at ;
|
||||||
|
|
||||||
: <inotify> ( -- port/f )
|
: <inotify> ( -- port/f )
|
||||||
inotify_init dup 0 < [ drop f ] [ <fd> <input-port> ] if ;
|
inotify_init dup 0 < [ drop f ] [ <fd> init-fd <input-port> ] if ;
|
||||||
|
|
||||||
: inotify-fd ( -- fd ) inotify get handle>> handle-fd ;
|
: inotify-fd ( -- fd ) inotify get handle>> handle-fd ;
|
||||||
|
|
||||||
|
|
|
@ -8,4 +8,4 @@ QUALIFIED: io.pipes
|
||||||
M: unix io.pipes:(pipe) ( -- pair )
|
M: unix io.pipes:(pipe) ( -- pair )
|
||||||
2 "int" <c-array>
|
2 "int" <c-array>
|
||||||
dup pipe io-error
|
dup pipe io-error
|
||||||
2 c-int-array> first2 [ <fd> ] bi@ io.pipes:pipe boa ;
|
2 c-int-array> first2 [ <fd> init-fd ] bi@ io.pipes:pipe boa ;
|
||||||
|
|
|
@ -13,7 +13,7 @@ EXCLUDE: io.sockets => accept ;
|
||||||
IN: io.unix.sockets
|
IN: io.unix.sockets
|
||||||
|
|
||||||
: socket-fd ( domain type -- fd )
|
: socket-fd ( domain type -- fd )
|
||||||
0 socket dup io-error <fd> |dispose ;
|
0 socket dup io-error <fd> init-fd |dispose ;
|
||||||
|
|
||||||
: set-socket-option ( fd level opt -- )
|
: set-socket-option ( fd level opt -- )
|
||||||
>r >r handle-fd r> r> 1 <int> "int" heap-size setsockopt io-error ;
|
>r >r handle-fd r> r> 1 <int> "int" heap-size setsockopt io-error ;
|
||||||
|
@ -77,7 +77,7 @@ M: object (server) ( addrspec -- handle )
|
||||||
M: object (accept) ( server addrspec -- fd sockaddr )
|
M: object (accept) ( server addrspec -- fd sockaddr )
|
||||||
2dup do-accept
|
2dup do-accept
|
||||||
{
|
{
|
||||||
{ [ over 0 >= ] [ >r 2nip <fd> r> ] }
|
{ [ over 0 >= ] [ >r 2nip <fd> init-fd r> ] }
|
||||||
{ [ err_no EINTR = ] [ 2drop (accept) ] }
|
{ [ err_no EINTR = ] [ 2drop (accept) ] }
|
||||||
{ [ err_no EAGAIN = ] [
|
{ [ err_no EAGAIN = ] [
|
||||||
2drop
|
2drop
|
||||||
|
|
|
@ -325,7 +325,6 @@ void find_code_references(CELL look_for_)
|
||||||
|
|
||||||
void factorbug(void)
|
void factorbug(void)
|
||||||
{
|
{
|
||||||
reset_stdio();
|
|
||||||
open_console();
|
open_console();
|
||||||
|
|
||||||
printf("Starting low level debugger...\n");
|
printf("Starting low level debugger...\n");
|
||||||
|
|
12
vm/factor.c
12
vm/factor.c
|
@ -28,7 +28,13 @@ void default_parameters(F_PARAMETERS *p)
|
||||||
|
|
||||||
p->secure_gc = false;
|
p->secure_gc = false;
|
||||||
p->fep = false;
|
p->fep = false;
|
||||||
|
|
||||||
|
#ifdef WINDOWS
|
||||||
p->console = false;
|
p->console = false;
|
||||||
|
#else
|
||||||
|
p->console = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
p->stack_traces = true;
|
p->stack_traces = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +98,9 @@ void init_factor(F_PARAMETERS *p)
|
||||||
init_c_io();
|
init_c_io();
|
||||||
init_signals();
|
init_signals();
|
||||||
|
|
||||||
|
if(p->console)
|
||||||
|
open_console();
|
||||||
|
|
||||||
stack_chain = NULL;
|
stack_chain = NULL;
|
||||||
profiling_p = false;
|
profiling_p = false;
|
||||||
performing_gc = false;
|
performing_gc = false;
|
||||||
|
@ -180,9 +189,6 @@ void init_factor_from_args(F_CHAR *image, int argc, F_CHAR **argv, bool embedded
|
||||||
userenv[EXECUTABLE_ENV] = tag_object(from_native_string(executable_path));
|
userenv[EXECUTABLE_ENV] = tag_object(from_native_string(executable_path));
|
||||||
userenv[EMBEDDED_ENV] = (embedded ? T : F);
|
userenv[EMBEDDED_ENV] = (embedded ? T : F);
|
||||||
|
|
||||||
if(p.console)
|
|
||||||
open_console();
|
|
||||||
|
|
||||||
if(p.fep)
|
if(p.fep)
|
||||||
factorbug();
|
factorbug();
|
||||||
|
|
||||||
|
|
|
@ -166,8 +166,6 @@ void mach_initialize (void)
|
||||||
{
|
{
|
||||||
mach_port_t self;
|
mach_port_t self;
|
||||||
exception_mask_t mask;
|
exception_mask_t mask;
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
self = mach_task_self ();
|
self = mach_task_self ();
|
||||||
|
|
||||||
|
@ -186,13 +184,7 @@ void mach_initialize (void)
|
||||||
mask = EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC;
|
mask = EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC;
|
||||||
|
|
||||||
/* Create the thread listening on the exception port. */
|
/* Create the thread listening on the exception port. */
|
||||||
if (pthread_attr_init (&attr) != 0)
|
start_thread(mach_exception_thread);
|
||||||
fatal_error("pthread_attr_init() failed",0);
|
|
||||||
if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0)
|
|
||||||
fatal_error("pthread_attr_setdetachstate() failed",0);
|
|
||||||
if (pthread_create (&thread, &attr, mach_exception_thread, NULL) != 0)
|
|
||||||
fatal_error("pthread_create() failed",0);
|
|
||||||
pthread_attr_destroy (&attr);
|
|
||||||
|
|
||||||
/* Replace the exception port info for these exceptions with our own.
|
/* Replace the exception port info for these exceptions with our own.
|
||||||
Note that we replace the exception port for the entire task, not only
|
Note that we replace the exception port for the entire task, not only
|
||||||
|
|
140
vm/os-unix.c
140
vm/os-unix.c
|
@ -1,5 +1,19 @@
|
||||||
#include "master.h"
|
#include "master.h"
|
||||||
|
|
||||||
|
void start_thread(void *(*start_routine)(void *))
|
||||||
|
{
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_t thread;
|
||||||
|
|
||||||
|
if (pthread_attr_init (&attr) != 0)
|
||||||
|
fatal_error("pthread_attr_init() failed",0);
|
||||||
|
if (pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED) != 0)
|
||||||
|
fatal_error("pthread_attr_setdetachstate() failed",0);
|
||||||
|
if (pthread_create (&thread, &attr, start_routine, NULL) != 0)
|
||||||
|
fatal_error("pthread_create() failed",0);
|
||||||
|
pthread_attr_destroy (&attr);
|
||||||
|
}
|
||||||
|
|
||||||
static void *null_dll;
|
static void *null_dll;
|
||||||
|
|
||||||
s64 current_millis(void)
|
s64 current_millis(void)
|
||||||
|
@ -264,10 +278,128 @@ void unix_init_signals(void)
|
||||||
sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
|
sigaction_safe(SIGPIPE,&ignore_sigaction,NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset_stdio(void)
|
/* On Unix, shared fds such as stdin cannot be set to non-blocking mode
|
||||||
|
(http://homepages.tesco.net/J.deBoynePollard/FGA/dont-set-shared-file-descriptors-to-non-blocking-mode.html)
|
||||||
|
so we kludge around this by spawning a thread, which waits on a control pipe
|
||||||
|
for a signal, upon receiving this signal it reads one block of data from stdin
|
||||||
|
and writes it to a data pipe. Upon completion, it writes a 4-byte integer to
|
||||||
|
the size pipe, indicating how much data was written to the data pipe.
|
||||||
|
|
||||||
|
The read end of the size pipe can be set to non-blocking. */
|
||||||
|
__attribute__((visibility("default"))) int stdin_read;
|
||||||
|
__attribute__((visibility("default"))) int stdin_write;
|
||||||
|
|
||||||
|
__attribute__((visibility("default"))) int control_read;
|
||||||
|
__attribute__((visibility("default"))) int control_write;
|
||||||
|
|
||||||
|
__attribute__((visibility("default"))) int size_read;
|
||||||
|
__attribute__((visibility("default"))) int size_write;
|
||||||
|
|
||||||
|
void safe_close(int fd)
|
||||||
{
|
{
|
||||||
fcntl(0,F_SETFL,0);
|
if(close(fd) < 0)
|
||||||
fcntl(1,F_SETFL,0);
|
fatal_error("error closing fd",errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void open_console(void) { }
|
bool check_write(int fd, void *data, size_t size)
|
||||||
|
{
|
||||||
|
if(write(fd,data,size) == size)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(errno == EINTR)
|
||||||
|
return check_write(fd,data,size);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void safe_write(int fd, void *data, size_t size)
|
||||||
|
{
|
||||||
|
if(!check_write(fd,data,size))
|
||||||
|
fatal_error("error writing fd",errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void safe_read(int fd, void *data, size_t size)
|
||||||
|
{
|
||||||
|
if(read(fd,data,size) != size)
|
||||||
|
fatal_error("error reading fd",errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *stdin_loop(void *arg)
|
||||||
|
{
|
||||||
|
unsigned char buf[4096];
|
||||||
|
bool loop_running = true;
|
||||||
|
|
||||||
|
while(loop_running)
|
||||||
|
{
|
||||||
|
safe_read(control_read,buf,1);
|
||||||
|
if(buf[0] != 'X')
|
||||||
|
fatal_error("stdin_loop: bad data on control fd",buf[0]);
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
size_t bytes = read(0,buf,sizeof(buf));
|
||||||
|
if(bytes < 0)
|
||||||
|
{
|
||||||
|
if(errno == EINTR)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loop_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(bytes >= 0)
|
||||||
|
{
|
||||||
|
safe_write(size_write,&bytes,sizeof(bytes));
|
||||||
|
|
||||||
|
if(write(stdin_write,buf,bytes) != bytes)
|
||||||
|
loop_running = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
safe_close(stdin_write);
|
||||||
|
safe_close(control_write);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_console(void)
|
||||||
|
{
|
||||||
|
int filedes[2];
|
||||||
|
|
||||||
|
if(pipe(filedes) < 0)
|
||||||
|
fatal_error("Error opening control pipe",errno);
|
||||||
|
|
||||||
|
control_read = filedes[0];
|
||||||
|
control_write = filedes[1];
|
||||||
|
|
||||||
|
if(pipe(filedes) < 0)
|
||||||
|
fatal_error("Error opening size pipe",errno);
|
||||||
|
|
||||||
|
size_read = filedes[0];
|
||||||
|
size_write = filedes[1];
|
||||||
|
|
||||||
|
if(pipe(filedes) < 0)
|
||||||
|
fatal_error("Error opening stdin pipe",errno);
|
||||||
|
|
||||||
|
stdin_read = filedes[0];
|
||||||
|
stdin_write = filedes[1];
|
||||||
|
|
||||||
|
start_thread(stdin_loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT void wait_for_stdin(void)
|
||||||
|
{
|
||||||
|
if(write(control_write,"X",1) != 1)
|
||||||
|
{
|
||||||
|
if(errno == EINTR)
|
||||||
|
wait_for_stdin();
|
||||||
|
else
|
||||||
|
fatal_error("Error writing control fd",errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,5 +38,4 @@ void dump_stack_signal(int signal, siginfo_t* siginfo, void* uap);
|
||||||
s64 current_millis(void);
|
s64 current_millis(void);
|
||||||
void sleep_millis(CELL msec);
|
void sleep_millis(CELL msec);
|
||||||
|
|
||||||
void reset_stdio(void);
|
|
||||||
void open_console(void);
|
void open_console(void);
|
||||||
|
|
|
@ -47,5 +47,3 @@ long getpagesize (void);
|
||||||
|
|
||||||
s64 current_millis(void);
|
s64 current_millis(void);
|
||||||
|
|
||||||
INLINE void reset_stdio(void) {}
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue