7169050: (se) Selector.select slow on Solaris due to insertion of POLLREMOVE and 0 events
Reviewed-by: chegar, coffeys
--- a/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Wed May 23 10:41:45 2012 -0700
+++ b/jdk/src/solaris/classes/sun/nio/ch/DevPollArrayWrapper.java Thu May 24 10:57:21 2012 +0100
@@ -25,9 +25,7 @@
package sun.nio.ch;
-import sun.misc.*;
import java.io.IOException;
-import java.util.LinkedList;
/**
@@ -66,6 +64,9 @@
static final short EVENT_OFFSET = 4;
static final short REVENT_OFFSET = 6;
+ // Special value to indicate that an update should be ignored
+ static final byte CANCELLED = (byte)-1;
+
// Maximum number of open file descriptors
static final int OPEN_MAX = fdLimit();
@@ -74,13 +75,16 @@
static final int NUM_POLLFDS = Math.min(OPEN_MAX-1, 8192);
// Base address of the native pollArray
- private long pollArrayAddress;
+ private final long pollArrayAddress;
// Array of pollfd structs used for driver updates
- private AllocatedNativeObject updatePollArray;
+ private final AllocatedNativeObject updatePollArray;
// Maximum number of POLL_FD structs to update at once
- private int MAX_UPDATE_SIZE = Math.min(OPEN_MAX, 10000);
+ private final int MAX_UPDATE_SIZE = Math.min(OPEN_MAX, 512);
+
+ // Initial size of arrays for fd registration changes
+ private final int INITIAL_PENDING_UPDATE_SIZE = 64;
DevPollArrayWrapper() {
int allocationSize = NUM_POLLFDS * SIZE_POLLFD;
@@ -91,19 +95,6 @@
wfd = init();
}
- // Machinery for remembering fd registration changes
- // A hashmap could be used but the number of changes pending
- // is expected to be small
- private static class Updator {
- int fd;
- int mask;
- Updator(int fd, int mask) {
- this.fd = fd;
- this.mask = mask;
- }
- }
- private LinkedList<Updator> updateList = new LinkedList<Updator>();
-
// The pollfd array for results from devpoll driver
private AllocatedNativeObject pollArray;
@@ -122,6 +113,20 @@
// Number of updated pollfd entries
int updated;
+ // object to synchronize fd registration changes
+ private final Object updateLock = new Object();
+
+ // number of file descriptors with registration changes pending
+ private int updateCount;
+
+ // file descriptors with registration changes pending
+ private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
+
+ // events for file descriptors with registration changes pending, indexed
+ // by file descriptor and stored as bytes for efficiency reasons.
+ private byte[] updateEvents = new byte[OPEN_MAX];
+
+
void initInterrupt(int fd0, int fd1) {
outgoingInterruptFD = fd1;
incomingInterruptFD = fd0;
@@ -149,14 +154,32 @@
}
void setInterest(int fd, int mask) {
- synchronized (updateList) {
- updateList.add(new Updator(fd, mask));
+ synchronized (updateLock) {
+ // record the file descriptor and events, expanding the
+ // respective arrays first if necessary.
+ int oldCapacity = updateDescriptors.length;
+ if (updateCount >= oldCapacity) {
+ int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
+ int[] newDescriptors = new int[newCapacity];
+ System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
+ updateDescriptors = newDescriptors;
+ }
+ updateDescriptors[updateCount++] = fd;
+
+ // events are stored as bytes for efficiency reasons
+ byte b = (byte)mask;
+ assert (b == mask) && (b != CANCELLED);
+ updateEvents[fd] = b;
}
}
void release(int fd) {
- synchronized (updateList) {
- updateList.add(new Updator(fd, POLLREMOVE));
+ synchronized (updateLock) {
+ // cancel any pending update for this file descriptor
+ updateEvents[fd] = CANCELLED;
+
+ // remove from /dev/poll
+ register(wfd, fd, POLLREMOVE);
}
}
@@ -181,32 +204,37 @@
void updateRegistrations() throws IOException {
// Populate pollfd array with updated masks
- synchronized (updateList) {
- while (updateList.size() > 0) {
- // We have to insert a dummy node in between each
- // real update to use POLLREMOVE on the fd first because
- // otherwise the changes are simply OR'd together
- int index = 0;
- Updator u = null;
- while ((u = updateList.poll()) != null) {
- // First add pollfd struct to clear out this fd
- putPollFD(updatePollArray, index, u.fd, POLLREMOVE);
+ synchronized (updateLock) {
+
+ int j = 0;
+ int index = 0;
+ while (j < updateCount) {
+ int fd = updateDescriptors[j];
+ short events = updateEvents[fd];
+
+ // skip update if key has been cancelled
+ if (events != CANCELLED) {
+ // remove from /dev/poll when the interest ops changes to 0
+ if (events == 0)
+ events = POLLREMOVE;
+
+ // populate pollfd array with updated event
+ putPollFD(updatePollArray, index, fd, events);
index++;
- // Now add pollfd to update this fd, if necessary
- if (u.mask != POLLREMOVE) {
- putPollFD(updatePollArray, index, u.fd, (short)u.mask);
- index++;
+ if (index >= MAX_UPDATE_SIZE) {
+ registerMultiple(wfd, updatePollArray.address(), index);
+ index = 0;
}
+ }
+ j++;
- // Check against the max update size; these are
- // all we will process. Valid index ranges from 0 to
- // (MAX_UPDATE_SIZE - 1) and we can use up to 2 per loop
- if (index > MAX_UPDATE_SIZE - 2)
- break;
- }
- // Register the changes with /dev/poll
+ }
+
+ // write any remaining updates
+ if (index > 0)
registerMultiple(wfd, updatePollArray.address(), index);
- }
+
+ updateCount = 0;
}
}
--- a/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c Wed May 23 10:41:45 2012 -0700
+++ b/jdk/src/solaris/native/sun/nio/ch/DevPollArrayWrapper.c Thu May 24 10:57:21 2012 +0100
@@ -118,27 +118,20 @@
Java_sun_nio_ch_DevPollArrayWrapper_register(JNIEnv *env, jobject this,
jint wfd, jint fd, jint mask)
{
- struct pollfd a[2];
- unsigned char *pollBytes = (unsigned char *)&a[0];
- unsigned char *pollEnd = pollBytes + sizeof(struct pollfd) * 2;
+ struct pollfd a[1];
+ int n;
- /* We clear it first, otherwise any entries between poll invocations
- get OR'd together */
a[0].fd = fd;
- a[0].events = POLLREMOVE;
+ a[0].events = mask;
a[0].revents = 0;
- a[1].fd = fd;
- a[1].events = mask;
- a[1].revents = 0;
-
- while (pollBytes < pollEnd) {
- int bytesWritten = write(wfd, pollBytes, (int)(pollEnd - pollBytes));
- if (bytesWritten < 0) {
+ n = write(wfd, &a[0], sizeof(a));
+ if (n != sizeof(a)) {
+ if (n < 0) {
JNU_ThrowIOExceptionWithLastError(env, "Error writing pollfds");
- return;
+ } else {
+ JNU_ThrowIOException(env, "Unexpected number of bytes written");
}
- pollBytes += bytesWritten;
}
}