Avoid running signal handlers while holding locks in io.c
parent
32feddc1fc
commit
63d516bda1
|
@ -64,8 +64,15 @@ enum {
|
|||
[offset] is the absolute position of the logical end of the buffer, [max].
|
||||
*/
|
||||
|
||||
/* Functions and macros that can be called from C. Take arguments of
|
||||
type struct channel *. The channel must be locked before calling these. */
|
||||
/* Creating and closing channels from C */
|
||||
|
||||
CAMLextern struct channel * caml_open_descriptor_in (int);
|
||||
CAMLextern struct channel * caml_open_descriptor_out (int);
|
||||
CAMLextern void caml_close_channel (struct channel *);
|
||||
|
||||
|
||||
/* I/O on channels from C. The channel must be locked (see below) before
|
||||
calling any of the functions and macros below */
|
||||
|
||||
#define caml_putch(channel, ch) do{ \
|
||||
if ((channel)->curr >= (channel)->end) caml_flush_partial(channel); \
|
||||
|
@ -77,11 +84,8 @@ enum {
|
|||
? caml_refill(channel) \
|
||||
: (unsigned char) *((channel)->curr)++)
|
||||
|
||||
CAMLextern struct channel * caml_open_descriptor_in (int);
|
||||
CAMLextern struct channel * caml_open_descriptor_out (int);
|
||||
CAMLextern void caml_close_channel (struct channel *);
|
||||
CAMLextern int caml_channel_binary_mode (struct channel *);
|
||||
CAMLextern value caml_alloc_channel(struct channel *chan);
|
||||
CAMLextern int caml_channel_binary_mode (struct channel *);
|
||||
|
||||
CAMLextern int caml_flush_partial (struct channel *);
|
||||
CAMLextern void caml_flush (struct channel *);
|
||||
|
|
|
@ -30,12 +30,16 @@ extern unsigned short caml_win32_revision;
|
|||
#include "misc.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define Io_interrupted (-1)
|
||||
|
||||
/* Read at most [n] bytes from file descriptor [fd] into buffer [buf].
|
||||
[flags] indicates whether [fd] is a socket
|
||||
(bit [CHANNEL_FLAG_FROM_SOCKET] is set in this case, see [io.h]).
|
||||
(This distinction matters for Win32, but not for Unix.)
|
||||
Return number of bytes read.
|
||||
In case of error, raises [Sys_error] or [Sys_blocked_io]. */
|
||||
In case of error, raises [Sys_error] or [Sys_blocked_io].
|
||||
If interrupted by a signal and no bytes where read, returns
|
||||
Io_interrupted without raising. */
|
||||
extern int caml_read_fd(int fd, int flags, void * buf, int n);
|
||||
|
||||
/* Write at most [n] bytes from buffer [buf] onto file descriptor [fd].
|
||||
|
@ -43,7 +47,9 @@ extern int caml_read_fd(int fd, int flags, void * buf, int n);
|
|||
(bit [CHANNEL_FLAG_FROM_SOCKET] is set in this case, see [io.h]).
|
||||
(This distinction matters for Win32, but not for Unix.)
|
||||
Return number of bytes written.
|
||||
In case of error, raises [Sys_error] or [Sys_blocked_io]. */
|
||||
In case of error, raises [Sys_error] or [Sys_blocked_io].
|
||||
If interrupted by a signal and no bytes were written, returns
|
||||
Io_interrupted without raising. */
|
||||
extern int caml_write_fd(int fd, int flags, void * buf, int n);
|
||||
|
||||
/* Decompose the given path into a list of directories, and add them
|
||||
|
|
|
@ -31,6 +31,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
CAMLextern void caml_enter_blocking_section (void);
|
||||
CAMLextern void caml_enter_blocking_section_no_pending (void);
|
||||
CAMLextern void caml_leave_blocking_section (void);
|
||||
|
||||
CAMLextern void caml_process_pending_actions (void);
|
||||
|
@ -39,6 +40,9 @@ CAMLextern void caml_process_pending_actions (void);
|
|||
Memprof callbacks. Assumes that the runtime lock is held. Can raise
|
||||
exceptions asynchronously into OCaml code. */
|
||||
|
||||
CAMLextern int caml_check_pending_actions (void);
|
||||
/* Returns 1 if there are pending actions, 0 otherwise. */
|
||||
|
||||
CAMLextern value caml_process_pending_actions_exn (void);
|
||||
/* Same as [caml_process_pending_actions], but returns the exception
|
||||
if any (otherwise returns [Val_unit]). */
|
||||
|
|
80
runtime/io.c
80
runtime/io.c
|
@ -69,13 +69,24 @@ CAMLexport struct channel * caml_all_opened_channels = NULL;
|
|||
|
||||
/* Functions shared between input and output */
|
||||
|
||||
static void check_pending(struct channel *channel)
|
||||
{
|
||||
if (caml_check_pending_actions()) {
|
||||
/* Temporarily unlock the channel, to ensure locks are not held
|
||||
while any signal handlers (or finalisers, etc) are running */
|
||||
Unlock(channel);
|
||||
caml_process_pending_actions();
|
||||
Lock(channel);
|
||||
}
|
||||
}
|
||||
|
||||
CAMLexport struct channel * caml_open_descriptor_in(int fd)
|
||||
{
|
||||
struct channel * channel;
|
||||
|
||||
channel = (struct channel *) caml_stat_alloc(sizeof(struct channel));
|
||||
channel->fd = fd;
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
channel->offset = lseek(fd, 0, SEEK_CUR);
|
||||
caml_leave_blocking_section();
|
||||
channel->curr = channel->max = channel->buff;
|
||||
|
@ -131,12 +142,13 @@ CAMLexport file_offset caml_channel_size(struct channel *channel)
|
|||
file_offset offset;
|
||||
file_offset end;
|
||||
int fd;
|
||||
check_pending(channel);
|
||||
|
||||
/* We extract data from [channel] before dropping the OCaml lock, in case
|
||||
someone else touches the block. */
|
||||
fd = channel->fd;
|
||||
offset = channel->offset;
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
end = lseek(fd, 0, SEEK_END);
|
||||
if (end == -1 || lseek(fd, offset, SEEK_SET) != offset) {
|
||||
caml_leave_blocking_section();
|
||||
|
@ -167,12 +179,15 @@ CAMLexport int caml_channel_binary_mode(struct channel *channel)
|
|||
CAMLexport int caml_flush_partial(struct channel *channel)
|
||||
{
|
||||
int towrite, written;
|
||||
again:
|
||||
check_pending(channel);
|
||||
|
||||
towrite = channel->curr - channel->buff;
|
||||
CAMLassert (towrite >= 0);
|
||||
if (towrite > 0) {
|
||||
written = caml_write_fd(channel->fd, channel->flags,
|
||||
channel->buff, towrite);
|
||||
if (written == Io_interrupted) goto again;
|
||||
channel->offset += written;
|
||||
if (written < towrite)
|
||||
memmove(channel->buff, channel->buff + written, towrite - written);
|
||||
|
@ -202,7 +217,7 @@ CAMLexport void caml_putword(struct channel *channel, uint32_t w)
|
|||
|
||||
CAMLexport int caml_putblock(struct channel *channel, char *p, intnat len)
|
||||
{
|
||||
int n, free, towrite, written;
|
||||
int n, free;
|
||||
|
||||
n = len >= INT_MAX ? INT_MAX : (int) len;
|
||||
free = channel->end - channel->curr;
|
||||
|
@ -215,13 +230,8 @@ CAMLexport int caml_putblock(struct channel *channel, char *p, intnat len)
|
|||
/* Write request overflows buffer (or just fills it up): transfer whatever
|
||||
fits to buffer and write the buffer */
|
||||
memmove(channel->curr, p, free);
|
||||
towrite = channel->end - channel->buff;
|
||||
written = caml_write_fd(channel->fd, channel->flags,
|
||||
channel->buff, towrite);
|
||||
if (written < towrite)
|
||||
memmove(channel->buff, channel->buff + written, towrite - written);
|
||||
channel->offset += written;
|
||||
channel->curr = channel->end - written;
|
||||
channel->curr = channel->end;
|
||||
caml_flush_partial(channel);
|
||||
return free;
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +250,7 @@ CAMLexport void caml_really_putblock(struct channel *channel,
|
|||
CAMLexport void caml_seek_out(struct channel *channel, file_offset dest)
|
||||
{
|
||||
caml_flush(channel);
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
if (lseek(channel->fd, dest, SEEK_SET) != dest) {
|
||||
caml_leave_blocking_section();
|
||||
caml_sys_error(NO_ARG);
|
||||
|
@ -259,16 +269,22 @@ CAMLexport file_offset caml_pos_out(struct channel *channel)
|
|||
/* caml_do_read is exported for Cash */
|
||||
CAMLexport int caml_do_read(int fd, char *p, unsigned int n)
|
||||
{
|
||||
return caml_read_fd(fd, 0, p, n);
|
||||
int r;
|
||||
do {
|
||||
r = caml_read_fd(fd, 0, p, n);
|
||||
} while (r == Io_interrupted);
|
||||
return r;
|
||||
}
|
||||
|
||||
CAMLexport unsigned char caml_refill(struct channel *channel)
|
||||
{
|
||||
int n;
|
||||
|
||||
again:
|
||||
check_pending(channel);
|
||||
n = caml_read_fd(channel->fd, channel->flags,
|
||||
channel->buff, channel->end - channel->buff);
|
||||
if (n == 0) caml_raise_end_of_file();
|
||||
if (n == Io_interrupted) goto again;
|
||||
else if (n == 0) caml_raise_end_of_file();
|
||||
channel->offset += n;
|
||||
channel->max = channel->buff + n;
|
||||
channel->curr = channel->buff + 1;
|
||||
|
@ -292,7 +308,8 @@ CAMLexport uint32_t caml_getword(struct channel *channel)
|
|||
CAMLexport int caml_getblock(struct channel *channel, char *p, intnat len)
|
||||
{
|
||||
int n, avail, nread;
|
||||
|
||||
again:
|
||||
check_pending(channel);
|
||||
n = len >= INT_MAX ? INT_MAX : (int) len;
|
||||
avail = channel->max - channel->curr;
|
||||
if (n <= avail) {
|
||||
|
@ -306,6 +323,7 @@ CAMLexport int caml_getblock(struct channel *channel, char *p, intnat len)
|
|||
} else {
|
||||
nread = caml_read_fd(channel->fd, channel->flags, channel->buff,
|
||||
channel->end - channel->buff);
|
||||
if (nread == Io_interrupted) goto again;
|
||||
channel->offset += nread;
|
||||
channel->max = channel->buff + nread;
|
||||
if (n > nread) n = nread;
|
||||
|
@ -335,7 +353,7 @@ CAMLexport void caml_seek_in(struct channel *channel, file_offset dest)
|
|||
dest <= channel->offset) {
|
||||
channel->curr = channel->max - (channel->offset - dest);
|
||||
} else {
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
if (lseek(channel->fd, dest, SEEK_SET) != dest) {
|
||||
caml_leave_blocking_section();
|
||||
caml_sys_error(NO_ARG);
|
||||
|
@ -355,7 +373,8 @@ CAMLexport intnat caml_input_scan_line(struct channel *channel)
|
|||
{
|
||||
char * p;
|
||||
int n;
|
||||
|
||||
again:
|
||||
check_pending(channel);
|
||||
p = channel->curr;
|
||||
do {
|
||||
if (p >= channel->max) {
|
||||
|
@ -378,7 +397,8 @@ CAMLexport intnat caml_input_scan_line(struct channel *channel)
|
|||
/* Fill the buffer as much as possible */
|
||||
n = caml_read_fd(channel->fd, channel->flags,
|
||||
channel->max, channel->end - channel->max);
|
||||
if (n == 0) {
|
||||
if (n == Io_interrupted) goto again;
|
||||
else if (n == 0) {
|
||||
/* End-of-file encountered. Return the number of characters in the
|
||||
buffer, with negative sign since we haven't encountered
|
||||
a newline. */
|
||||
|
@ -545,7 +565,7 @@ CAMLprim value caml_ml_close_channel(value vchannel)
|
|||
channel->curr = channel->max = channel->end;
|
||||
|
||||
if (do_syscall) {
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
result = close(fd);
|
||||
caml_leave_blocking_section();
|
||||
}
|
||||
|
@ -563,16 +583,27 @@ CAMLprim value caml_ml_close_channel(value vchannel)
|
|||
#define EOVERFLOW ERANGE
|
||||
#endif
|
||||
|
||||
static file_offset ml_channel_size(value vchannel)
|
||||
{
|
||||
CAMLparam1 (vchannel);
|
||||
struct channel * channel = Channel(vchannel);
|
||||
file_offset size;
|
||||
|
||||
Lock(channel);
|
||||
size = caml_channel_size(Channel(vchannel));
|
||||
Unlock(channel);
|
||||
if (size > Max_long) { errno = EOVERFLOW; caml_sys_error(NO_ARG); }
|
||||
CAMLreturnT(file_offset, size);
|
||||
}
|
||||
|
||||
CAMLprim value caml_ml_channel_size(value vchannel)
|
||||
{
|
||||
file_offset size = caml_channel_size(Channel(vchannel));
|
||||
if (size > Max_long) { errno = EOVERFLOW; caml_sys_error(NO_ARG); }
|
||||
return Val_long(size);
|
||||
return Val_long(ml_channel_size(vchannel));
|
||||
}
|
||||
|
||||
CAMLprim value caml_ml_channel_size_64(value vchannel)
|
||||
{
|
||||
return Val_file_offset(caml_channel_size(Channel(vchannel)));
|
||||
return Val_file_offset(ml_channel_size(vchannel));
|
||||
}
|
||||
|
||||
CAMLprim value caml_ml_set_binary_mode(value vchannel, value mode)
|
||||
|
@ -731,6 +762,8 @@ CAMLprim value caml_ml_input(value vchannel, value buff, value vstart,
|
|||
int n, avail, nread;
|
||||
|
||||
Lock(channel);
|
||||
again:
|
||||
check_pending(channel);
|
||||
/* We cannot call caml_getblock here because buff may move during
|
||||
caml_read_fd */
|
||||
start = Long_val(vstart);
|
||||
|
@ -747,6 +780,7 @@ CAMLprim value caml_ml_input(value vchannel, value buff, value vstart,
|
|||
} else {
|
||||
nread = caml_read_fd(channel->fd, channel->flags, channel->buff,
|
||||
channel->end - channel->buff);
|
||||
if (nread == Io_interrupted) goto again;
|
||||
channel->offset += nread;
|
||||
channel->max = channel->buff + nread;
|
||||
if (n > nread) n = nread;
|
||||
|
|
|
@ -163,6 +163,11 @@ CAMLexport void caml_enter_blocking_section(void)
|
|||
}
|
||||
}
|
||||
|
||||
CAMLexport void caml_enter_blocking_section_no_pending(void)
|
||||
{
|
||||
caml_enter_blocking_section_hook ();
|
||||
}
|
||||
|
||||
CAMLexport void caml_leave_blocking_section(void)
|
||||
{
|
||||
int saved_errno;
|
||||
|
@ -183,7 +188,7 @@ CAMLexport void caml_leave_blocking_section(void)
|
|||
[signals_are_pending] is 0 but the signal needs to be
|
||||
handled at this point. */
|
||||
signals_are_pending = 1;
|
||||
caml_raise_if_exception(caml_process_pending_signals_exn());
|
||||
//caml_raise_if_exception(caml_process_pending_signals_exn());
|
||||
|
||||
errno = saved_errno;
|
||||
}
|
||||
|
@ -322,6 +327,12 @@ Caml_inline value process_pending_actions_with_root_exn(value extra_root)
|
|||
return extra_root;
|
||||
}
|
||||
|
||||
CAMLno_tsan /* The access to [caml_something_to_do] is not synchronized. */
|
||||
int caml_check_pending_actions()
|
||||
{
|
||||
return caml_something_to_do;
|
||||
}
|
||||
|
||||
value caml_process_pending_actions_with_root(value extra_root)
|
||||
{
|
||||
value res = process_pending_actions_with_root_exn(extra_root);
|
||||
|
|
|
@ -74,12 +74,13 @@
|
|||
int caml_read_fd(int fd, int flags, void * buf, int n)
|
||||
{
|
||||
int retcode;
|
||||
do {
|
||||
caml_enter_blocking_section();
|
||||
retcode = read(fd, buf, n);
|
||||
caml_leave_blocking_section();
|
||||
} while (retcode == -1 && errno == EINTR);
|
||||
if (retcode == -1) caml_sys_io_error(NO_ARG);
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = read(fd, buf, n);
|
||||
caml_leave_blocking_section();
|
||||
if (retcode == -1) {
|
||||
if (errno == EINTR) return Io_interrupted;
|
||||
else caml_sys_io_error(NO_ARG);
|
||||
}
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
@ -92,14 +93,14 @@ int caml_write_fd(int fd, int flags, void * buf, int n)
|
|||
retcode = write(fd, buf, n);
|
||||
} else {
|
||||
#endif
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = write(fd, buf, n);
|
||||
caml_leave_blocking_section();
|
||||
#if defined(NATIVE_CODE) && defined(WITH_SPACETIME)
|
||||
}
|
||||
#endif
|
||||
if (retcode == -1) {
|
||||
if (errno == EINTR) goto again;
|
||||
if (errno == EINTR) return Io_interrupted;
|
||||
if ((errno == EAGAIN || errno == EWOULDBLOCK) && n > 1) {
|
||||
/* We couldn't do a partial write here, probably because
|
||||
n <= PIPE_BUF and POSIX says that writes of less than
|
||||
|
|
|
@ -87,7 +87,7 @@ int caml_read_fd(int fd, int flags, void * buf, int n)
|
|||
{
|
||||
int retcode;
|
||||
if ((flags & CHANNEL_FLAG_FROM_SOCKET) == 0) {
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = read(fd, buf, n);
|
||||
/* Large reads from console can fail with ENOMEM. Reduce requested size
|
||||
and try again. */
|
||||
|
@ -97,7 +97,7 @@ int caml_read_fd(int fd, int flags, void * buf, int n)
|
|||
caml_leave_blocking_section();
|
||||
if (retcode == -1) caml_sys_io_error(NO_ARG);
|
||||
} else {
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = recv((SOCKET) _get_osfhandle(fd), buf, n, 0);
|
||||
caml_leave_blocking_section();
|
||||
if (retcode == -1) caml_win32_sys_error(WSAGetLastError());
|
||||
|
@ -114,7 +114,7 @@ int caml_write_fd(int fd, int flags, void * buf, int n)
|
|||
retcode = write(fd, buf, n);
|
||||
} else {
|
||||
#endif
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = write(fd, buf, n);
|
||||
caml_leave_blocking_section();
|
||||
#if defined(NATIVE_CODE) && defined(WITH_SPACETIME)
|
||||
|
@ -122,7 +122,7 @@ int caml_write_fd(int fd, int flags, void * buf, int n)
|
|||
#endif
|
||||
if (retcode == -1) caml_sys_io_error(NO_ARG);
|
||||
} else {
|
||||
caml_enter_blocking_section();
|
||||
caml_enter_blocking_section_no_pending();
|
||||
retcode = send((SOCKET) _get_osfhandle(fd), buf, n, 0);
|
||||
caml_leave_blocking_section();
|
||||
if (retcode == -1) caml_win32_sys_error(WSAGetLastError());
|
||||
|
|
Loading…
Reference in New Issue