8201315: (se) Allow SelectableChannel.register to be invoked while selection operation is in progress

Reviewed-by: bpb
This commit is contained in:
Alan Bateman 2018-04-18 10:20:09 +01:00
parent 76590dc009
commit 33b921f25d
6 changed files with 244 additions and 142 deletions

View file

@ -108,7 +108,7 @@ public abstract class AbstractSelectableChannel
if (keys[i] == null)
break;
} else if (keys == null) {
keys = new SelectionKey[3];
keys = new SelectionKey[2];
} else {
// Grow key array
int n = keys.length * 2;
@ -123,14 +123,14 @@ public abstract class AbstractSelectableChannel
}
private SelectionKey findKey(Selector sel) {
synchronized (keyLock) {
if (keys == null)
return null;
for (int i = 0; i < keys.length; i++)
if ((keys[i] != null) && (keys[i].selector() == sel))
return keys[i];
assert Thread.holdsLock(keyLock);
if (keys == null)
return null;
}
for (int i = 0; i < keys.length; i++)
if ((keys[i] != null) && (keys[i].selector() == sel))
return keys[i];
return null;
}
void removeKey(SelectionKey k) { // package-private
@ -166,7 +166,9 @@ public abstract class AbstractSelectableChannel
}
public final SelectionKey keyFor(Selector sel) {
return findKey(sel);
synchronized (keyLock) {
return findKey(sel);
}
}
/**
@ -195,32 +197,31 @@ public abstract class AbstractSelectableChannel
*
* @throws IllegalArgumentException {@inheritDoc}
*/
public final SelectionKey register(Selector sel, int ops,
Object att)
public final SelectionKey register(Selector sel, int ops, Object att)
throws ClosedChannelException
{
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (!isOpen())
throw new ClosedChannelException();
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (isBlocking())
throw new IllegalBlockingModeException();
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
synchronized (keyLock) {
// re-check if channel has been closed
if (!isOpen())
throw new ClosedChannelException();
SelectionKey k = findKey(sel);
if (k != null) {
k.attach(att);
k.interestOps(ops);
} else {
// New registration
k = ((AbstractSelector)sel).register(this, ops, att);
addKey(k);
}
return k;
}
return k;
}
}
@ -239,12 +240,20 @@ public abstract class AbstractSelectableChannel
*/
protected final void implCloseChannel() throws IOException {
implCloseSelectableChannel();
// clone keys to avoid calling cancel when holding keyLock
SelectionKey[] copyOfKeys = null;
synchronized (keyLock) {
int count = (keys == null) ? 0 : keys.length;
for (int i = 0; i < count; i++) {
SelectionKey k = keys[i];
if (k != null)
k.cancel();
if (keys != null) {
copyOfKeys = keys.clone();
}
}
if (copyOfKeys != null) {
for (SelectionKey k : copyOfKeys) {
if (k != null) {
k.cancel(); // invalidate and adds key to cancelledKey set
}
}
}
}