Avoid running signal handlers while holding locks in io.c

master
Stephen Dolan 2020-06-24 17:16:21 +01:00
parent 32feddc1fc
commit 63d516bda1
7 changed files with 104 additions and 44 deletions

View File

@ -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 *);

View File

@ -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

View File

@ -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]). */

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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());