* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: socket.c,v 1.207.2.19.2.58 2008/10/17 21:53:54 jinmei Exp $ */
+/* $Id: socket.c,v 1.207.2.19.2.59 2008/11/08 22:42:56 jinmei Exp $ */
#include <config.h>
#endif /* __APPLE__ */
#endif /* USE_SELECT */
+#ifdef ISC_SOCKET_USE_POLLWATCH
+/*
+ * If this macro is defined, enable workaround for a Solaris /dev/poll kernel
+ * bug: DP_POLL ioctl could keep sleeping even if socket I/O is possible for
+ * some of the specified FD. The idea is based on the observation that it's
+ * likely for a busy server to keep receiving packets. It specifically works
+ * as follows: the socket watcher is first initialized with the state of
+ * "poll_idle". While it's in the idle state it keeps sleeping until a socket
+ * event occurs. When it wakes up for a socket I/O event, it moves to the
+ * poll_active state, and sets the poll timeout to a short period
+ * (ISC_SOCKET_POLLWATCH_TIMEOUT msec). If timeout occurs in this state, the
+ * watcher goes to the poll_checking state with the same timeout period.
+ * In this state, the watcher tries to detect whether this is a break
+ * during intermittent events or the kernel bug is triggered. If the next
+ * polling reports an event within the short period, the previous timeout is
+ * likely to be a kernel bug, and so the watcher goes back to the active state.
+ * Otherwise, it moves to the idle state again.
+ *
+ * It's not clear whether this is a thread-related bug, but since we've only
+ * seen this with threads, this workaround is used only when enabling threads.
+ */
+
+typedef enum { poll_idle, poll_active, poll_checking } pollstate_t;
+
+#ifndef ISC_SOCKET_POLLWATCH_TIMEOUT
+#define ISC_SOCKET_POLLWATCH_TIMEOUT 10
+#endif /* ISC_SOCKET_POLLWATCH_TIMEOUT */
+#endif /* ISC_SOCKET_USE_POLLWATCH */
+
/*
* Size of per-FD lock buckets.
*/
int maxfd;
#endif
char strbuf[ISC_STRERRORSIZE];
+#ifdef ISC_SOCKET_USE_POLLWATCH
+ pollstate_t pollstate = poll_idle;
+#endif
/*
* Get the control fd here. This will never change.
#elif defined(USE_DEVPOLL)
dvp.dp_fds = manager->events;
dvp.dp_nfds = manager->nevents;
+#ifndef ISC_SOCKET_USE_POLLWATCH
dvp.dp_timeout = -1;
+#else
+ if (pollstate == poll_idle)
+ dvp.dp_timeout = -1;
+ else
+ dvp.dp_timeout = ISC_SOCKET_POLLWATCH_TIMEOUT;
+#endif /* ISC_SOCKET_USE_POLLWATCH */
cc = ioctl(manager->devpoll_fd, DP_POLL, &dvp);
#elif defined(USE_SELECT)
LOCK(&manager->lock);
ISC_MSG_FAILED,
"failed"), strbuf);
}
+
+#if defined(USE_DEVPOLL) && defined(ISC_SOCKET_USE_POLLWATCH)
+ if (cc == 0) {
+ if (pollstate == poll_active)
+ pollstate = poll_checking;
+ else if (pollstate == poll_checking)
+ pollstate = poll_idle;
+ } else if (cc > 0) {
+ if (pollstate == poll_checking) {
+ /*
+ * XXX: We'd like to use a more
+ * verbose log level as it's actually an
+ * unexpected event, but the kernel bug
+ * reportedly happens pretty frequently
+ * (and it can also be a false positive)
+ * so it would be just too noisy.
+ */
+ manager_log(manager,
+ ISC_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_SOCKET,
+ ISC_LOG_DEBUG(1),
+ ISC_LOG_INFO,
+ "unexpected POLL timeout");
+ }
+ pollstate = poll_active;
+ }
+#endif
} while (cc < 0);
#if defined(USE_KQUEUE) || defined (USE_EPOLL) || defined (USE_DEVPOLL)