aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/sys')
-rw-r--r--erts/emulator/sys/common/erl_check_io.c1
-rw-r--r--erts/emulator/sys/common/erl_poll.c17
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c3
-rw-r--r--erts/emulator/sys/unix/sys.c4
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c109
-rw-r--r--erts/emulator/sys/unix/sys_uds.c13
-rw-r--r--erts/emulator/sys/win32/erl_poll.c18
7 files changed, 107 insertions, 58 deletions
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index b03e19ee71..f421794f91 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -1507,7 +1507,6 @@ send_event_tuple(struct erts_nif_select_event* e, ErtsResource* resource,
}
tuple = TUPLE4(hp, am_select, resource_term, ref_term, event_atom);
- ERL_MESSAGE_TOKEN(mp) = am_undefined;
erts_queue_message(rp, rp_locks, mp, tuple, am_system);
if (rp_locks)
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index 70b5532af9..b4d1575ee5 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -803,6 +803,23 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events)
struct kevent evts[2];
struct timespec ts = {0, 0};
+ if (op == ERTS_POLL_OP_ADD) {
+ /* This is a hack to make the "noshell" option work; kqueue can poll
+ * these fds but will not report EV_EOF, so we return NVAL to use the
+ * fallback instead.
+ *
+ * This may be common to all pipes but we have no way to tell whether
+ * an fd is a pipe or not. */
+ switch (fd) {
+ case STDIN_FILENO:
+ case STDOUT_FILENO:
+ case STDERR_FILENO:
+ return ERTS_POLL_EV_NVAL;
+ default:
+ break;
+ }
+ }
+
#if defined(EV_DISPATCH) && !defined(__OpenBSD__)
/* If we have EV_DISPATCH we use it, unless we are on OpenBSD as the
behavior of EV_EOF seems to be edge triggered there and we need it
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 2541ab5d31..d34e1a9ec0 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -176,6 +176,7 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
double af;
Uint64 int_part, frac_part;
int neg;
+ int has_decimals = decimals != 0;
char *p = buffer;
if (decimals < 0)
@@ -257,7 +258,7 @@ sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
}
/* Delete trailing zeroes */
- if (compact)
+ if (compact && has_decimals)
p = find_first_trailing_zero(p);
*p = '\0';
return p - buffer;
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 189ca083d7..36579ffdb4 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -452,9 +452,9 @@ prepare_crash_dump(int secs)
envsz = sizeof(env);
i = erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
- if (i >= 0) {
+ if (i != 0) {
int nice_val;
- nice_val = i != 1 ? 0 : atoi(env);
+ nice_val = (i != 1) ? 0 : atoi(env);
if (nice_val > 39) {
nice_val = 39;
}
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 872c3a80b1..816bdea9c5 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -685,7 +685,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
/* we send the request to do the fork */
if ((res = writev(ofd[1], io_vector, iov_len > MAXIOV ? MAXIOV : iov_len)) < 0) {
- if (errno == ERRNO_BLOCK) {
+ if (errno == ERRNO_BLOCK || errno == EINTR) {
res = 0;
} else {
int err = errno;
@@ -1000,7 +1000,7 @@ static void clear_fd_data(ErtsSysFdData *fdd)
static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd)
{
- driver_select(prt, abs(fdd->fd), DO_READ|DO_WRITE, 0);
+ driver_select(prt, abs(fdd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0);
clear_fd_data(fdd);
SET_BLOCKING(abs(fdd->fd));
@@ -1257,6 +1257,8 @@ static int port_inp_failure(ErtsSysDriverData *dd, int res)
}
driver_failure_eof(dd->port_num);
} else if (dd->ifd) {
+ if (dd->alive == -1)
+ errno = dd->status;
erl_drv_init_ack(dd->port_num, ERL_DRV_ERROR_ERRNO);
} else {
driver_failure_posix(dd->port_num, err);
@@ -1287,10 +1289,10 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
int res;
if((res = read(ready_fd, &proto, sizeof(proto))) <= 0) {
+ if (res < 0 && (errno == ERRNO_BLOCK || errno == EINTR))
+ return;
/* hmm, child setup seems to have closed the pipe too early...
we close the port as there is not much else we can do */
- if (res < 0 && errno == ERRNO_BLOCK)
- return;
driver_select(port_num, ready_fd, ERL_DRV_READ, 0);
if (res == 0)
errno = EPIPE;
@@ -1424,7 +1426,7 @@ static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
continue;
}
else { /* The last message we got was split */
- char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
+ char *buf = erts_alloc_fnf(ERTS_ALC_T_FD_ENTRY_BUF, h);
if (!buf) {
errno = ENOMEM;
port_inp_failure(dd, -1);
@@ -1670,15 +1672,37 @@ static void forker_stop(ErlDrvData e)
the port has been closed by the user. */
}
+static ErlDrvSizeT forker_deq(ErlDrvPort port_num, ErtsSysForkerProto *proto)
+{
+ close(proto->u.start.fds[0]);
+ close(proto->u.start.fds[1]);
+ if (proto->u.start.fds[1] != proto->u.start.fds[2])
+ close(proto->u.start.fds[2]);
+
+ return driver_deq(port_num, sizeof(*proto));
+}
+
+static void forker_sigchld(Eterm port_id, int error)
+{
+ ErtsSysForkerProto *proto = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(*proto));
+ proto->action = ErtsSysForkerProtoAction_SigChld;
+ proto->u.sigchld.error_number = error;
+ proto->u.sigchld.port_id = port_id;
+
+ /* ideally this would be a port_command call, but as command is
+ already used by the spawn_driver, we use control instead.
+ Note that when using erl_drv_port_control it is an asynchronous
+ control. */
+ erl_drv_port_control(port_id, 'S', (char*)proto, sizeof(*proto));
+}
+
static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd)
{
int res;
- ErtsSysForkerProto *proto;
+ ErtsSysForkerProto proto;
- proto = erts_alloc(ERTS_ALC_T_DRV_CTRL_DATA, sizeof(*proto));
-
- if ((res = read(fd, proto, sizeof(*proto))) < 0) {
- if (errno == ERRNO_BLOCK)
+ if ((res = read(fd, &proto, sizeof(proto))) < 0) {
+ if (errno == ERRNO_BLOCK || errno == EINTR)
return;
erts_exit(ERTS_DUMP_EXIT, "Failed to read from erl_child_setup: %d\n", errno);
}
@@ -1686,10 +1710,10 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd)
if (res == 0)
erts_exit(ERTS_DUMP_EXIT, "erl_child_setup closed\n");
- ASSERT(res == sizeof(*proto));
+ ASSERT(res == sizeof(proto));
#ifdef FORKER_PROTO_START_ACK
- if (proto->action == ErtsSysForkerProtoAction_StartAck) {
+ if (proto.action == ErtsSysForkerProtoAction_StartAck) {
/* Ideally we would like to not have to ack each Start
command being sent over the uds, but it would seem
that some operating systems (only observed on FreeBSD)
@@ -1699,28 +1723,15 @@ static void forker_ready_input(ErlDrvData e, ErlDrvEvent fd)
ErlDrvPort port_num = (ErlDrvPort)e;
int vlen;
SysIOVec *iov = driver_peekq(port_num, &vlen);
- ErtsSysForkerProto *proto = (ErtsSysForkerProto *)iov[0].iov_base;
-
- close(proto->u.start.fds[0]);
- close(proto->u.start.fds[1]);
- if (proto->u.start.fds[1] != proto->u.start.fds[2])
- close(proto->u.start.fds[2]);
+ ErtsSysForkerProto *qproto = (ErtsSysForkerProto *)iov[0].iov_base;
- driver_deq(port_num, sizeof(*proto));
-
- if (driver_sizeq(port_num) > 0)
+ if (forker_deq(port_num, qproto))
driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
} else
#endif
{
- ASSERT(proto->action == ErtsSysForkerProtoAction_SigChld);
-
- /* ideally this would be a port_command call, but as command is
- already used by the spawn_driver, we use control instead.
- Note that when using erl_drv_port_control it is an asynchronous
- control. */
- erl_drv_port_control(proto->u.sigchld.port_id, 'S',
- (char*)proto, sizeof(*proto));
+ ASSERT(proto.action == ErtsSysForkerProtoAction_SigChld);
+ forker_sigchld(proto.u.sigchld.port_id, proto.u.sigchld.error_number);
}
}
@@ -1730,7 +1741,8 @@ static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd)
ErlDrvPort port_num = (ErlDrvPort)e;
#ifndef FORKER_PROTO_START_ACK
- while (driver_sizeq(port_num) > 0) {
+ int loops = 10;
+ while (driver_sizeq(port_num) > 0 && --loops) {
#endif
int vlen;
SysIOVec *iov = driver_peekq(port_num, &vlen);
@@ -1738,20 +1750,24 @@ static void forker_ready_output(ErlDrvData e, ErlDrvEvent fd)
ASSERT(iov[0].iov_len >= (sizeof(*proto)));
if (sys_uds_write(forker_fd, (char*)proto, sizeof(*proto),
proto->u.start.fds, 3, 0) < 0) {
- if (errno == ERRNO_BLOCK)
+ if (errno == ERRNO_BLOCK || errno == EINTR) {
return;
- erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
+ } else if (errno == EMFILE) {
+ forker_sigchld(proto->u.start.port_id, errno);
+ if (forker_deq(port_num, proto) == 0)
+ driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0);
+ return;
+ } else {
+ erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
+ }
}
#ifndef FORKER_PROTO_START_ACK
- close(proto->u.start.fds[0]);
- close(proto->u.start.fds[1]);
- if (proto->u.start.fds[1] != proto->u.start.fds[2])
- close(proto->u.start.fds[2]);
- driver_deq(port_num, sizeof(*proto));
+ if (forker_deq(port_num, proto) == 0)
+ driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0);
}
-#endif
-
+#else
driver_select(port_num, forker_fd, ERL_DRV_WRITE, 0);
+#endif
}
static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf,
@@ -1777,20 +1793,21 @@ static ErlDrvSSizeT forker_control(ErlDrvData e, unsigned int cmd, char *buf,
if ((res = sys_uds_write(forker_fd, (char*)proto, sizeof(*proto),
proto->u.start.fds, 3, 0)) < 0) {
- if (errno == ERRNO_BLOCK) {
+ if (errno == ERRNO_BLOCK || errno == EINTR) {
driver_select(port_num, forker_fd, ERL_DRV_WRITE|ERL_DRV_USE, 1);
return 0;
+ } else if (errno == EMFILE) {
+ forker_sigchld(proto->u.start.port_id, errno);
+ forker_deq(port_num, proto);
+ return 0;
+ } else {
+ erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
}
- erts_exit(ERTS_DUMP_EXIT, "Failed to write to erl_child_setup: %d\n", errno);
}
#ifndef FORKER_PROTO_START_ACK
ASSERT(res == sizeof(*proto));
- close(proto->u.start.fds[0]);
- close(proto->u.start.fds[1]);
- if (proto->u.start.fds[1] != proto->u.start.fds[2])
- close(proto->u.start.fds[2]);
- driver_deq(port_num, sizeof(*proto));
+ forker_deq(port_num, proto);
#endif
return 0;
diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c
index c328fd00bb..39a4866065 100644
--- a/erts/emulator/sys/unix/sys_uds.c
+++ b/erts/emulator/sys/unix/sys_uds.c
@@ -132,7 +132,7 @@ sys_uds_writev(int fd, struct iovec *iov, size_t iov_len,
struct msghdr msg;
struct cmsghdr *cmsg = NULL;
- int res, i;
+ int res, i, error;
/* initialize socket message */
memset(&msg, 0, sizeof(struct msghdr));
@@ -173,11 +173,22 @@ sys_uds_writev(int fd, struct iovec *iov, size_t iov_len,
res = sendmsg(fd, &msg, flags);
+#ifdef ETOOMANYREFS
+ /* Linux may give ETOOMANYREFS when there are too many fds in transit.
+ We map this to EMFILE as bsd and other use this error code and we want
+ the behaviour to be the same on all OSs */
+ if (errno == ETOOMANYREFS)
+ errno = EMFILE;
+#endif
+ error = errno;
+
if (iov_len > MAXIOV)
free(iov[0].iov_base);
free(msg.msg_control);
+ errno = error;
+
return res;
}
diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c
index fd4c745c3b..fcf5a0d533 100644
--- a/erts/emulator/sys/win32/erl_poll.c
+++ b/erts/emulator/sys/win32/erl_poll.c
@@ -362,11 +362,11 @@ is_io_ready(ErtsPollSet *ps)
}
static ERTS_INLINE void
-woke_up(ErtsPollSet *ps)
+woke_up(ErtsPollSet *ps, int waketype)
{
if (erts_atomic32_read_nob(&ps->wakeup_state) == ERTS_POLL_NOT_WOKEN)
erts_atomic32_cmpxchg_nob(&ps->wakeup_state,
- ERTS_POLL_WOKEN_TIMEDOUT,
+ waketype,
ERTS_POLL_NOT_WOKEN);
#ifdef DEBUG
{
@@ -960,12 +960,12 @@ static int cancel_driver_select(ErtsPollSet *ps, HANDLE event)
void erts_poll_interrupt(ErtsPollSet *ps, int set /* bool */)
{
- HARDTRACEF(("In erts_poll_interrupt(%d)",set));
+ HARDTRACEF(("In erts_poll_interrupt(%p, %d)",ps,set));
if (!set)
reset_interrupt(ps);
else
set_interrupt(ps);
- HARDTRACEF(("Out erts_poll_interrupt(%d)",set));
+ HARDTRACEF(("Out erts_poll_interrupt(%p, %d)",ps,set));
}
@@ -1051,19 +1051,20 @@ int erts_poll_wait(ErtsPollSet *ps,
if (!erts_atomic32_read_nob(&break_waiter_state)) {
HANDLE harr[2] = {ps->event_io_ready, break_happened_event};
- int num_h = 2;
+ int num_h = 2, handle;
ERTS_MSACC_PUSH_STATE();
HARDDEBUGF(("Start waiting %d [%d]",num_h, (int) timeout));
ERTS_POLLSET_UNLOCK(ps);
erts_thr_progress_prepare_wait(NULL);
ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_SLEEP);
- WaitForMultipleObjects(num_h, harr, FALSE, timeout);
+ handle = WaitForMultipleObjects(num_h, harr, FALSE, timeout);
erts_thr_progress_finalize_wait(NULL);
ERTS_MSACC_POP_STATE();
ERTS_POLLSET_LOCK(ps);
HARDDEBUGF(("Stop waiting %d [%d]",num_h, (int) timeout));
- woke_up(ps);
+ if (handle == WAIT_OBJECT_0)
+ woke_up(ps, ERTS_POLL_WOKEN_TIMEDOUT);
}
ERTS_UNSET_BREAK_REQUESTED;
@@ -1075,7 +1076,10 @@ int erts_poll_wait(ErtsPollSet *ps,
erts_mtx_unlock(&break_waiter_lock);
switch (break_state) {
case BREAK_WAITER_GOT_BREAK:
+ woke_up(ps, ERTS_POLL_WOKEN_INTR);
ERTS_SET_BREAK_REQUESTED;
+ /* Wake aux thread to get handle break */
+ erts_aux_thread_poke();
break;
case BREAK_WAITER_GOT_HALT:
erts_exit(0,"");