mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 14:24:46 +02:00
8322829: Refactor nioBlocker to avoid blocking while holding Thread's interrupt lock
Reviewed-by: jpai
This commit is contained in:
parent
07fce8eff2
commit
7286f5291d
6 changed files with 118 additions and 54 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -32,9 +32,9 @@ import java.nio.channels.ClosedByInterruptException;
|
|||
import java.nio.channels.InterruptibleChannel;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.nio.ch.Interruptible;
|
||||
|
||||
|
||||
/**
|
||||
* Base implementation class for interruptible channels.
|
||||
*
|
||||
|
@ -89,10 +89,26 @@ public abstract class AbstractInterruptibleChannel
|
|||
private final Object closeLock = new Object();
|
||||
private volatile boolean closed;
|
||||
|
||||
// invoked if a Thread is interrupted when blocked in an I/O op
|
||||
private final Interruptible interruptor;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*/
|
||||
protected AbstractInterruptibleChannel() { }
|
||||
protected AbstractInterruptibleChannel() {
|
||||
this.interruptor = new Interruptible() {
|
||||
@Override
|
||||
public void interrupt(Thread target) {
|
||||
AbstractInterruptibleChannel.this.trySetTarget(target);
|
||||
}
|
||||
@Override
|
||||
public void postInterrupt() {
|
||||
try {
|
||||
AbstractInterruptibleChannel.this.close();
|
||||
} catch (IOException x) { }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this channel.
|
||||
|
@ -139,8 +155,15 @@ public abstract class AbstractInterruptibleChannel
|
|||
|
||||
// -- Interruption machinery --
|
||||
|
||||
private Interruptible interruptor;
|
||||
private volatile Thread interrupted;
|
||||
private static final Unsafe U = Unsafe.getUnsafe();
|
||||
private static final long INTERRUPTED_TARGET =
|
||||
U.objectFieldOffset(AbstractInterruptibleChannel.class, "interruptedTarget");
|
||||
private volatile Object interruptedTarget; // Thread or placeholder object
|
||||
|
||||
private void trySetTarget(Thread target) {
|
||||
// can't use VarHandle here as CAS may park on first usage
|
||||
U.compareAndSetReference(this, INTERRUPTED_TARGET, null, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the beginning of an I/O operation that might block indefinitely.
|
||||
|
@ -151,24 +174,12 @@ public abstract class AbstractInterruptibleChannel
|
|||
* closing and interruption for this channel. </p>
|
||||
*/
|
||||
protected final void begin() {
|
||||
if (interruptor == null) {
|
||||
interruptor = new Interruptible() {
|
||||
public void interrupt(Thread target) {
|
||||
synchronized (closeLock) {
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
interrupted = target;
|
||||
try {
|
||||
AbstractInterruptibleChannel.this.implCloseChannel();
|
||||
} catch (IOException x) { }
|
||||
}
|
||||
}};
|
||||
}
|
||||
blockedOn(interruptor);
|
||||
Thread me = Thread.currentThread();
|
||||
if (me.isInterrupted())
|
||||
if (me.isInterrupted()) {
|
||||
interruptor.interrupt(me);
|
||||
interruptor.postInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,10 +205,14 @@ public abstract class AbstractInterruptibleChannel
|
|||
throws AsynchronousCloseException
|
||||
{
|
||||
blockedOn(null);
|
||||
Thread interrupted = this.interrupted;
|
||||
if (interrupted != null && interrupted == Thread.currentThread()) {
|
||||
this.interrupted = null;
|
||||
throw new ClosedByInterruptException();
|
||||
Object interruptedTarget = this.interruptedTarget;
|
||||
if (interruptedTarget != null) {
|
||||
interruptor.postInterrupt();
|
||||
if (interruptedTarget == Thread.currentThread()) {
|
||||
// replace with dummy object to avoid retaining reference to this thread
|
||||
this.interruptedTarget = new Object();
|
||||
throw new ClosedByInterruptException();
|
||||
}
|
||||
}
|
||||
if (!completed && closed)
|
||||
throw new AsynchronousCloseException();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
|
@ -89,6 +89,9 @@ public abstract class AbstractSelector
|
|||
// cancelled-key, not used by the JDK Selector implementations
|
||||
private final Set<SelectionKey> cancelledKeys;
|
||||
|
||||
// invoked if a Thread is interrupted when blocked on a selection op
|
||||
private final Interruptible interruptor;
|
||||
|
||||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*
|
||||
|
@ -103,6 +106,15 @@ public abstract class AbstractSelector
|
|||
} else {
|
||||
this.cancelledKeys = new HashSet<>();
|
||||
}
|
||||
this.interruptor = new Interruptible() {
|
||||
@Override
|
||||
public void interrupt(Thread ignore) {
|
||||
}
|
||||
@Override
|
||||
public void postInterrupt() {
|
||||
AbstractSelector.this.wakeup();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void cancel(SelectionKey k) { // package-private
|
||||
|
@ -209,8 +221,6 @@ public abstract class AbstractSelector
|
|||
|
||||
// -- Interruption machinery --
|
||||
|
||||
private Interruptible interruptor = null;
|
||||
|
||||
/**
|
||||
* Marks the beginning of an I/O operation that might block indefinitely.
|
||||
*
|
||||
|
@ -225,16 +235,11 @@ public abstract class AbstractSelector
|
|||
* blocked in an I/O operation upon the selector. </p>
|
||||
*/
|
||||
protected final void begin() {
|
||||
if (interruptor == null) {
|
||||
interruptor = new Interruptible() {
|
||||
public void interrupt(Thread ignore) {
|
||||
AbstractSelector.this.wakeup();
|
||||
}};
|
||||
}
|
||||
AbstractInterruptibleChannel.blockedOn(interruptor);
|
||||
Thread me = Thread.currentThread();
|
||||
if (me.isInterrupted())
|
||||
interruptor.interrupt(me);
|
||||
if (me.isInterrupted()) {
|
||||
interruptor.postInterrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -248,5 +253,4 @@ public abstract class AbstractSelector
|
|||
protected final void end() {
|
||||
AbstractInterruptibleChannel.blockedOn(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue