| /* | |
| * Copyright (c) 2006, 2025, 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 | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* @test | |
| * @bug 6285901 6501089 | |
| * @summary Check no data is written to wrong socket channel during async closing. | |
| * @requires (os.family != "windows") | |
| */ | |
| import java.io.IOException; | |
| import java.net.InetAddress; | |
| import java.net.InetSocketAddress; | |
| import java.net.ServerSocket; | |
| import java.net.Socket; | |
| import java.nio.ByteBuffer; | |
| import java.nio.channels.ClosedChannelException; | |
| import java.nio.channels.SocketChannel; | |
| public class AsyncCloseChannel { | |
| static volatile boolean failed = false; | |
| static volatile boolean keepGoing = true; | |
| static int maxAcceptCount = 100; | |
| static volatile int acceptCount = 0; | |
| static int sensorPort; | |
| static int targetPort; | |
| public static void main(String args[]) throws Exception { | |
| Thread ss = new SensorServer(); ss.start(); | |
| Thread ts = new TargetServer(); ts.start(); | |
| sensorPort = ((ServerThread)ss).server.getLocalPort(); | |
| targetPort = ((ServerThread)ts).server.getLocalPort(); | |
| Thread sc = new SensorClient(); sc.start(); | |
| Thread tc = new TargetClient(); tc.start(); | |
| while(acceptCount < maxAcceptCount && !failed) { | |
| Thread.sleep(10); | |
| } | |
| keepGoing = false; | |
| try { | |
| ss.interrupt(); | |
| ts.interrupt(); | |
| sc.interrupt(); | |
| tc.interrupt(); | |
| } catch (Exception e) {} | |
| if (failed) | |
| throw new RuntimeException("AsyncCloseChannel2 failed after <" | |
| + acceptCount + "> times of accept!"); | |
| } | |
| static class SensorServer extends ServerThread { | |
| public void runEx() throws Exception { | |
| while(keepGoing) { | |
| try { | |
| final Socket s = server.accept(); | |
| new Thread() { | |
| public void run() { | |
| try { | |
| int c = s.getInputStream().read(); | |
| if(c != -1) { | |
| // No data is ever written to the peer's socket! | |
| System.err.println("Oops: read a character: " | |
| + (char) c); | |
| failed = true; | |
| } | |
| } catch (IOException ex) { | |
| ex.printStackTrace(); | |
| } finally { | |
| closeIt(s); | |
| } | |
| } | |
| }.start(); | |
| } catch (IOException ex) { | |
| System.err.println("Exception on sensor server " + ex.getMessage()); | |
| } | |
| } | |
| } | |
| } | |
| static class TargetServer extends ServerThread { | |
| public void runEx() throws Exception { | |
| while (keepGoing) { | |
| try { | |
| final Socket s = server.accept(); | |
| acceptCount++; | |
| new Thread() { | |
| public void run() { | |
| boolean empty = true; | |
| try { | |
| while (keepGoing) { | |
| int c = s.getInputStream().read(); | |
| if(c == -1) { | |
| if(!empty) | |
| break; | |
| } | |
| empty = false; | |
| } | |
| } catch (IOException ex) { | |
| ex.printStackTrace(); | |
| } finally { | |
| closeIt(s); | |
| } | |
| } | |
| }.start(); | |
| } catch (IOException ex) { | |
| System.err.println("Exception on target server " + ex.getMessage()); | |
| } | |
| } | |
| } | |
| } | |
| static class SensorClient extends Thread { | |
| private static boolean wake; | |
| private static SensorClient theClient; | |
| public void run() { | |
| while (keepGoing) { | |
| Socket s = null; | |
| try { | |
| s = new Socket(); | |
| synchronized(this) { | |
| while(!wake && keepGoing) { | |
| try { | |
| wait(); | |
| } catch (InterruptedException ex) { } | |
| } | |
| wake = false; | |
| } | |
| s.connect(new InetSocketAddress(InetAddress.getLoopbackAddress(), sensorPort)); | |
| try { | |
| Thread.sleep(10); | |
| } catch (InterruptedException ex) { } | |
| } catch (IOException ex) { | |
| System.err.println("Exception on sensor client " + ex.getMessage()); | |
| } finally { | |
| if(s != null) { | |
| try { | |
| s.close(); | |
| } catch(IOException ex) { ex.printStackTrace();} | |
| } | |
| } | |
| } | |
| } | |
| public SensorClient() { | |
| theClient = this; | |
| } | |
| public static void wakeMe() { | |
| synchronized(theClient) { | |
| wake = true; | |
| theClient.notify(); | |
| } | |
| } | |
| } | |
| static class TargetClient extends Thread { | |
| volatile boolean ready = false; | |
| public void run() { | |
| while(keepGoing) { | |
| try { | |
| final SocketChannel s = SocketChannel.open( | |
| new InetSocketAddress(InetAddress.getLoopbackAddress(), targetPort)); | |
| s.finishConnect(); | |
| s.socket().setSoLinger(false, 0); | |
| ready = false; | |
| Thread t = new Thread() { | |
| public void run() { | |
| ByteBuffer b = ByteBuffer.allocate(1); | |
| try { | |
| for(;;) { | |
| b.clear(); | |
| b.put((byte) 'A'); | |
| b.flip(); | |
| s.write(b); | |
| ready = true; | |
| } | |
| } catch (IOException ex) { | |
| if(!(ex instanceof ClosedChannelException)) | |
| System.err.println("Exception in target client child " | |
| + ex.toString()); | |
| } | |
| } | |
| }; | |
| t.start(); | |
| while(!ready && keepGoing) { | |
| try { | |
| Thread.sleep(10); | |
| } catch (InterruptedException ex) {} | |
| } | |
| s.close(); | |
| SensorClient.wakeMe(); | |
| t.join(); | |
| } catch (IOException ex) { | |
| System.err.println("Exception in target client parent " | |
| + ex.getMessage()); | |
| } catch (InterruptedException ex) {} | |
| } | |
| } | |
| } | |
| static abstract class ServerThread extends Thread { | |
| ServerSocket server; | |
| public ServerThread() { | |
| super(); | |
| try { | |
| server = new ServerSocket(0); | |
| } catch (IOException ex) { | |
| ex.printStackTrace(); | |
| } | |
| } | |
| public void interrupt() { | |
| super.interrupt(); | |
| if (server != null) { | |
| try { | |
| server.close(); | |
| } catch (IOException ex) { | |
| ex.printStackTrace(); | |
| } | |
| } | |
| } | |
| public void run() { | |
| try { | |
| runEx(); | |
| } catch (Exception ex) { | |
| ex.printStackTrace(); | |
| } | |
| } | |
| abstract void runEx() throws Exception; | |
| } | |
| public static void closeIt(Socket s) { | |
| try { | |
| if(s != null) | |
| s.close(); | |
| } catch (IOException ex) { } | |
| } | |
| } |