diff --git a/runtime/caml/io.h b/runtime/caml/io.h index 162cd9018..bc8316084 100644 --- a/runtime/caml/io.h +++ b/runtime/caml/io.h @@ -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 *); diff --git a/runtime/caml/osdeps.h b/runtime/caml/osdeps.h index d41779d3f..74a3558fd 100644 --- a/runtime/caml/osdeps.h +++ b/runtime/caml/osdeps.h @@ -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 diff --git a/runtime/caml/signals.h b/runtime/caml/signals.h index feaa17eb4..953acc851 100644 --- a/runtime/caml/signals.h +++ b/runtime/caml/signals.h @@ -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]). */ diff --git a/runtime/io.c b/runtime/io.c index 1db7ef0f7..f36f3251c 100644 --- a/runtime/io.c +++ b/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; diff --git a/runtime/signals.c b/runtime/signals.c index 58f37775a..c5daa02d1 100644 --- a/runtime/signals.c +++ b/runtime/signals.c @@ -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); diff --git a/runtime/unix.c b/runtime/unix.c index c0ddbaaaf..e381690b0 100644 --- a/runtime/unix.c +++ b/runtime/unix.c @@ -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 diff --git a/runtime/win32.c b/runtime/win32.c index 948d03c3d..2ab56c462 100644 --- a/runtime/win32.c +++ b/runtime/win32.c @@ -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());