aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/configure.in68
-rw-r--r--erts/emulator/sys/common/erl_poll.c78
-rw-r--r--erts/emulator/sys/common/erl_poll.h6
3 files changed, 84 insertions, 68 deletions
diff --git a/erts/configure.in b/erts/configure.in
index ca4228270f..9dec562f33 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -3463,62 +3463,24 @@ case $poll_works-$host_os in
esac
#
-# If kqueue() found, check that it can be selected or polled on...
+# If kqueue() found
#
if test $have_kernel_poll = kqueue; then
- if test $poll_works = yes; then
- kqueue_with=poll
- else
- kqueue_with=select
- fi
- AC_MSG_CHECKING([whether kqueue() fd can be ${kqueue_with}()ed on])
- AC_TRY_RUN([
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/event.h>
-#include <sys/time.h>
-#ifdef ERTS_USE_POLL
-#include <poll.h>
-#else
-#include <unistd.h>
-#endif
-int main(void) {
- int kq = kqueue();
- if (kq < 0) return 2;
- {
-#ifdef ERTS_USE_POLL
- struct pollfd pfds = {kq, POLLIN, 0};
- if (poll(&pfds, 1, 0) < 0) return 1;
-#else
- struct timeval tv = {0, 0};
- fd_set set; FD_ZERO(&set); FD_SET(kq, &set);
- if (select(kq+1, &set, NULL, NULL, &tv) < 0) return 1;
-#endif
- }
- return 0;
-}
- ],
- ok_kqueue=yes,
- ok_kqueue=no,
- [
- case X$erl_xcomp_kqueue in
- X) ok_kqueue=cross;;
- Xyes|Xno) ok_kqueue=$erl_xcomp_kqueue;;
- *) AC_MSG_ERROR([Bad erl_xcomp_kqueue value: $erl_xcomp_kqueue]);;
- esac
- ])
- AC_MSG_RESULT($ok_kqueue);
- case $ok_kqueue in
- yes)
- ;;
- cross)
- have_kernel_poll=no
- AC_MSG_WARN([result no guessed because of cross compilation]);;
- *)
- have_kernel_poll=no;;
- esac
+## Some OS X kernel version seems to have bugs in them with regards to kqueue
+## Disable kernel poll on those versions
+ AC_MSG_CHECKING([whether host os has known kqueue bugs])
+ case $host_os in
+ # Any OS X version < 16 has known problems with using kqueue
+ # so we don't use it there. See erl_poll.c for details.
+ darwin[[0-9]].*|darwin1[[0-5]].*)
+ AC_MSG_RESULT([yes, disabling kernel poll])
+ have_kernel_poll=no
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
fi
-
#
# If epoll() found, check that it is level triggered.
#
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index fc32dbf370..30a595c17a 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -186,6 +186,8 @@ int ERTS_SELECT(int nfds, ERTS_fd_set *readfds, ERTS_fd_set *writefds,
#define ERTS_POLL_USE_CONCURRENT_UPDATE (ERTS_POLL_USE_EPOLL || ERTS_POLL_USE_KQUEUE)
+#define ERTS_POLL_USE_WAKEUP_PIPE (!ERTS_POLL_USE_CONCURRENT_UPDATE)
+
#if !ERTS_POLL_USE_CONCURRENT_UPDATE
#define ERTS_POLLSET_SET_HAVE_UPDATE_REQUESTS(PS) \
@@ -279,9 +281,12 @@ struct ERTS_POLL_EXPORT(erts_pollset) {
ErtsPollSetUpdateRequestsBlock *curr_upd_req_block;
erts_atomic32_t have_update_requests;
erts_mtx_t mtx;
- int wake_fds[2];
erts_atomic32_t wakeup_state;
#endif
+
+#if ERTS_POLL_USE_WAKEUP_PIPE
+ int wake_fds[2];
+#endif
};
void erts_silence_warn_unused_result(long unused);
@@ -397,11 +402,12 @@ woke_up(ErtsPollSet *ps)
* --- Wakeup pipe -----------------------------------------------------------
*/
-#if !ERTS_POLL_USE_CONCURRENT_UPDATE
+#if ERTS_POLL_USE_WAKEUP_PIPE
static ERTS_INLINE void
wake_poller(ErtsPollSet *ps, int interrupted)
{
+#if !ERTS_POLL_USE_CONCURRENT_UPDATE
int wake;
erts_aint32_t wakeup_state;
if (!interrupted)
@@ -413,7 +419,9 @@ wake_poller(ErtsPollSet *ps, int interrupted)
ERTS_POLL_WOKEN_INTR);
wake = wakeup_state == ERTS_POLL_NOT_WOKEN;
- if (wake) {
+ if (wake)
+#endif
+ {
ssize_t res;
if (ps->wake_fds[1] < 0)
return; /* Not initialized yet */
@@ -452,8 +460,10 @@ cleanup_wakeup_pipe(ErtsPollSet *ps)
fd,
erl_errno_id(errno), errno);
}
+#if !ERTS_POLL_USE_CONCURRENT_UPDATE
if (intr)
erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_WOKEN_INTR);
+#endif
}
static void
@@ -489,11 +499,14 @@ create_wakeup_pipe(ErtsPollSet *ps)
ps->wake_fds[1] = wake_fds[1];
}
+#endif
/*
* --- Poll set update requests ----------------------------------------------
*/
+#if !ERTS_POLL_USE_CONCURRENT_UPDATE
+
static ERTS_INLINE void
enqueue_update_request(ErtsPollSet *ps, int fd)
{
@@ -893,9 +906,9 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events)
#endif
if (op == ERTS_POLL_OP_ADD)
erts_atomic_dec_nob(&ps->no_of_user_fds);
- return ERTS_POLL_EV_NVAL;
- }
- return 0;
+ events = ERTS_POLL_EV_NVAL;
+ } else
+ events = 0;
}
return events;
}
@@ -1216,7 +1229,7 @@ poll_control(ErtsPollSet *ps, int fd, ErtsPollOp op,
goto done;
}
#endif
-#if !ERTS_POLL_USE_CONCURRENT_UPDATE
+#if ERTS_POLL_USE_WAKEUP_PIPE
if (fd == ps->wake_fds[0] || fd == ps->wake_fds[1]) {
new_events = ERTS_POLL_EV_NVAL;
goto done;
@@ -1294,10 +1307,10 @@ ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet *ps,
static ERTS_INLINE int
ERTS_POLL_EXPORT(save_result)(ErtsPollSet *ps, ErtsPollResFd pr[], int max_res, int chk_fds_res, int ebadf)
{
-#if !ERTS_POLL_USE_CONCURRENT_UPDATE || ERTS_POLL_DEBUG_PRINT
+#if !ERTS_POLL_USE_CONCURRENT_UPDATE || ERTS_POLL_DEBUG_PRINT || ERTS_POLL_USE_WAKEUP_PIPE
int n = chk_fds_res < max_res ? chk_fds_res : max_res, i;
int res = n;
-#if !ERTS_POLL_USE_CONCURRENT_UPDATE
+#if ERTS_POLL_USE_WAKEUP_PIPE
int wake_fd = ps->wake_fds[0];
#endif
@@ -1318,12 +1331,16 @@ ERTS_POLL_EXPORT(save_result)(ErtsPollSet *ps, ErtsPollResFd pr[], int max_res,
#endif
);
-#if !ERTS_POLL_USE_CONCURRENT_UPDATE
-
+#if ERTS_POLL_USE_WAKEUP_PIPE
if (fd == wake_fd) {
cleanup_wakeup_pipe(ps);
ERTS_POLL_RES_SET_EVTS(&pr[i], ERTS_POLL_EV_NONE);
- } else {
+ if (n == 1)
+ return 0;
+ }
+#endif
+#if !ERTS_POLL_USE_CONCURRENT_UPDATE
+ else {
/* Reset the events to emulate ONESHOT semantics */
ps->fds_status[fd].events = 0;
enqueue_update_request(ps, fd);
@@ -1820,6 +1837,43 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(int id)
handle_update_requests(ps, NULL, 0);
cleanup_wakeup_pipe(ps);
#endif
+#if ERTS_POLL_USE_KERNEL_POLL && (defined(__DARWIN__) || defined(__APPLE__) && defined(__MACH__))
+ {
+ /*
+ * Using kqueue on OS X is a mess of brokenness...
+ *
+ * On OS X version older than 15.6 (i.e. OS X El Capitan released in July 2015),
+ * a thread waiting in kevent is not woken if an event is inserted into the kqueue
+ * by another thread and the event becomes ready. However if a new call to kevent
+ * is done by the waiting thread, the new event is found.
+ *
+ * So on effected OS X versions we could trigger the wakeup pipe so that
+ * the waiters will be woken and re-issue the kevent. However...
+ *
+ * On OS X version older then 16 (i.e. OS X Sierra released in September 2016),
+ * running the emulator driver_SUITE smp_select testcase consistently causes a
+ * kernel panic. I don't know why or what events that trigger it. But it seems
+ * like updates of the pollset while another thread is sleeping in it Creates
+ * some kind of race that triggers the kernel panic.
+ *
+ * So to deal with this, the erts configure check what OS X version is run
+ * and only enabled kernel poll on OS X 16 or newer. In addition, if someone
+ * attempts to compile Erlang on OS X 16 and then run it on OS X 15, we do the
+ * run-time check below to disallow this.
+ */
+ int major, minor, build;
+ os_version(&major,&minor,&build);
+ if (major < 16) {
+ erts_fprintf(stderr,"BROKEN KQUEUE!\n"
+ "Erlang has been compiled with kernel-poll support,\n"
+ "but this OS X version is known to have kernel bugs\n"
+ "when using kernel-poll. You have two options:\n"
+ " 1) update to a newer OS X version (OS X Sierra or newer)\n"
+ " 2) recompile erlang without kernel-poll support\n");
+ erts_exit(1, "");
+ }
+ }
+#endif
erts_atomic_set_nob(&ps->no_of_user_fds, 0); /* Don't count wakeup pipe and fallback fd */
return ps;
diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h
index a1e2709a9f..e9a667cac1 100644
--- a/erts/emulator/sys/common/erl_poll.h
+++ b/erts/emulator/sys/common/erl_poll.h
@@ -134,9 +134,9 @@
typedef Uint32 ErtsPollEvents;
typedef enum {
- ERTS_POLL_OP_ADD = 0, /* Add the FD to the pollset */
- ERTS_POLL_OP_MOD = 1, /* Modify the FD in the pollset */
- ERTS_POLL_OP_DEL = 2 /* Delete the FD from the pollset */
+ ERTS_POLL_OP_ADD = 0, /* Add the FD to the pollset */
+ ERTS_POLL_OP_MOD = 1, /* Modify the FD in the pollset */
+ ERTS_POLL_OP_DEL = 2 /* Delete the FD from the pollset */
} ErtsPollOp;
#define op2str(op) (op == ERTS_POLL_OP_ADD ? "add" : \