Jesse Wilson (JIRA) wrote:
[ https://issues.apache.org/jira/browse/HARMONY-6312?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12745221#action_12745221 ]
Jesse Wilson commented on HARMONY-6312:
---------------------------------------
The patch includes a test fails on the original code, but passes on the RI and
on the fixed code.
The patch makes some major surgery to SelectionKeyImpl. The fundamental change is that
instead of adjusting readableFDs and writableFDs as the keys and interest ops change,
this adjusts them before each select() operation. Select is already an O(N) operation (in
terms of the number of keys), so constructing this array will not hurt performance. It
also guarantees that the two arrays are "resized" the minimum number of times
possible. The old code may grow the arrays more than necessary, only to shrink them
before the native call.
That file also received a small bit of documentation and style cleanup. Once
this patch is live, I volunteer to submit a follow up that will improve
variable names.
Concurrency problems in NIO
---------------------------
Key: HARMONY-6312
URL: https://issues.apache.org/jira/browse/HARMONY-6312
Project: Harmony
Issue Type: Bug
Components: Classlib
Environment: SVN Revision: 801399
Reporter: Jesse Wilson
Attachments: NIO_Concurrency_issues.patch
Original Estimate: 72h
Remaining Estimate: 72h
There's several potential concurrency problems in our NIO implementation...
- Charset#isSupported isn't synchronized, but it accesses mutable member
cachedCharsetTable.
- In SelectionKeyImpl, stHash++ is a non-atomic operation so multiple
SelectionKey instances may receive the same hash code. (Why not use the default
hash code?)
- In SelectorImpl, the unmodifiableKeys member is never synchronized on. But
the documentation specifies that clients can synchronize on that set to guard
against changes
These are the obvious problems; I suspect there are more subtle concurrency
defects at play here. I'll prepare a patch to address the major issues, and we
should consider a rigorous approach (Findbugs?) to discover concurrency
problems.
#Android
Jesse, thanks for this patch, I believe it make selector code easier understand
and maintain, but I still have concerns with iterating keySet in prepareChannels.
The more registered channels, the slower prepareChannels, even more, we have to
travel the whole set more than once! Current implementation maintains several
mapping to avoid this. So do you have any data to prove this patch is good as
old one when large number of channels are registered?
For arrays "resize" in prepareChannels, it can be simple avoid by patch [1]. in
[1] I added a new interface to accept count of readable/writable FDs, so
readableFDs and writableFDs don't need to be resized. After patch [1] nothing to
be done before select.
Index:
modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
=====================================================================
---
modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
+++
modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
@@ -157,6 +157,9 @@ public interface INetworkSystem {
FileDescriptor[] writeFDs, long timeout)
throws SocketException;
+ public int[] select(FileDescriptor[] readFDs,
+ FileDescriptor[] writeFDs, int countRead, int countWrite, long
timeout)
+ throws SocketException;
/*
* Query the IP stack for the local port to which this socket is bound.
*
Index:
modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
=====================================================================
---
modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
+++
modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
@@ -376,6 +376,31 @@ final class OSNetworkSystem implements INetworkSystem {
throw new SocketException();
}
+ public int[] select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
int countRead, int countWrite,
+ long timeout) throws SocketException {
+ int result = 0;
+ if (0 == countRead + countWrite) {
+ return (new int[0]);
+ }
+ int[] flags = new int[countRead + countWrite];
+
+ assert validateFDs(readFDs, writeFDs, countRead, countWrite) : "Invalid
file descriptor arrays"; //$NON-NLS-1$
+
+ // handle timeout in native
+ result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags,
+ timeout);
+
+ if (0 <= result) {
+ return flags;
+ }
+ if (ERRORCODE_SOCKET_TIMEOUT == result ||
+ ERRORCODE_SOCKET_INTERRUPTED == result) {
+ return new int[0];
+ }
+ throw new SocketException();
+ }
+
+
private native int selectImpl(FileDescriptor[] readfd,
FileDescriptor[] writefd, int cread, int cwirte, int[] flags,
long timeout);
@@ -497,6 +522,22 @@ final class OSNetworkSystem implements INetworkSystem {
return true;
}
+ private boolean validateFDs(FileDescriptor[] readFDs,
+ FileDescriptor[] writeFDs, int countRead, int countWrite) {
+ for (int i = 0; i < countRead; ++i) {
+ // Also checks fd not null
+ if (!readFDs[i].valid()) {
+ return false;
+ }
+ }
+ for (int i = 0; i < countWrite; ++i) {
+ if (!writeFDs[i].valid()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Write bytes from a byte array to a socket.
*
Index:
modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
=====================================================================
---
modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
+++
modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
@@ -469,13 +469,12 @@ final class SelectorImpl extends AbstractSelector {
doCancel();
int[] readyChannels = null;
boolean isBlock = (SELECT_NOW != timeout);
- prepareChannels();
try {
if (isBlock) {
begin();
}
readyChannels = Platform.getNetworkSystem().select(
- readableFDs, writableFDs, timeout);
+ readableFDs, writableFDs, readableKeysCount,
writableKeysCount, timeout);
} finally {
if (isBlock) {
end();
--
Best Regards,
Regis.