test/jdk/java/net/Socket/Timeouts.java
changeset 55102 59567035d279
parent 54289 6183f835b9b6
child 58679 9c3209ff7550
equal deleted inserted replaced
55101:c41783eb76eb 55102:59567035d279
    21  * questions.
    21  * questions.
    22  */
    22  */
    23 
    23 
    24 /*
    24 /*
    25  * @test
    25  * @test
       
    26  * @bug 8221481
    26  * @library /test/lib
    27  * @library /test/lib
    27  * @build jdk.test.lib.Utils
    28  * @build jdk.test.lib.Utils
    28  * @run testng Timeouts
    29  * @run testng/timeout=180 Timeouts
    29  * @summary Test Socket timeouts
    30  * @summary Test Socket timeouts
    30  */
    31  */
    31 
    32 
    32 import java.io.Closeable;
    33 import java.io.Closeable;
    33 import java.io.IOException;
    34 import java.io.IOException;
    34 import java.io.InputStream;
    35 import java.io.InputStream;
    35 import java.io.OutputStream;
    36 import java.io.OutputStream;
    36 import java.net.ConnectException;
    37 import java.net.ConnectException;
       
    38 import java.net.InetAddress;
       
    39 import java.net.InetSocketAddress;
    37 import java.net.ServerSocket;
    40 import java.net.ServerSocket;
    38 import java.net.Socket;
    41 import java.net.Socket;
    39 import java.net.SocketAddress;
    42 import java.net.SocketAddress;
    40 import java.net.SocketException;
    43 import java.net.SocketException;
    41 import java.net.SocketTimeoutException;
    44 import java.net.SocketTimeoutException;
    42 import java.util.concurrent.Executors;
    45 import java.util.concurrent.Executors;
       
    46 import java.util.concurrent.ExecutionException;
       
    47 import java.util.concurrent.ExecutorService;
       
    48 import java.util.concurrent.Future;
    43 import java.util.concurrent.ScheduledExecutorService;
    49 import java.util.concurrent.ScheduledExecutorService;
    44 import java.util.concurrent.TimeUnit;
    50 import java.util.concurrent.TimeUnit;
    45 
    51 
    46 import org.testng.annotations.Test;
    52 import org.testng.annotations.Test;
    47 import static org.testng.Assert.*;
    53 import static org.testng.Assert.*;
    52 
    58 
    53     /**
    59     /**
    54      * Test timed connect where connection is established
    60      * Test timed connect where connection is established
    55      */
    61      */
    56     public void testTimedConnect1() throws IOException {
    62     public void testTimedConnect1() throws IOException {
    57         try (ServerSocket ss = new ServerSocket(0)) {
    63         try (ServerSocket ss = boundServerSocket()) {
    58             try (Socket s = new Socket()) {
    64             try (Socket s = new Socket()) {
    59                 s.connect(ss.getLocalSocketAddress(), 2000);
    65                 s.connect(ss.getLocalSocketAddress(), 2000);
    60             }
    66             }
    61         }
    67         }
    62     }
    68     }
    75 
    81 
    76     /**
    82     /**
    77      * Test connect with a timeout of Integer.MAX_VALUE
    83      * Test connect with a timeout of Integer.MAX_VALUE
    78      */
    84      */
    79     public void testTimedConnect3() throws IOException {
    85     public void testTimedConnect3() throws IOException {
    80         try (ServerSocket ss = new ServerSocket(0)) {
    86         try (ServerSocket ss = boundServerSocket()) {
    81             try (Socket s = new Socket()) {
    87             try (Socket s = new Socket()) {
    82                 s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE);
    88                 s.connect(ss.getLocalSocketAddress(), Integer.MAX_VALUE);
    83             }
    89             }
    84         }
    90         }
    85     }
    91     }
    86 
    92 
    87     /**
    93     /**
    88      * Test connect with a negative timeout.
    94      * Test connect with a negative timeout.
    89      */
    95      */
    90     public void testTimedConnect4() throws IOException {
    96     public void testTimedConnect4() throws IOException {
    91         try (ServerSocket ss = new ServerSocket(0)) {
    97         try (ServerSocket ss = boundServerSocket()) {
    92             try (Socket s = new Socket()) {
    98             try (Socket s = new Socket()) {
    93                 try {
    99                 expectThrows(IllegalArgumentException.class,
    94                     s.connect(ss.getLocalSocketAddress(), -1);
   100                              () -> s.connect(ss.getLocalSocketAddress(), -1));
    95                     assertTrue(false);
       
    96                 } catch (IllegalArgumentException expected) { }
       
    97             }
   101             }
    98         }
   102         }
    99     }
   103     }
   100 
   104 
   101     /**
   105     /**
   126      * Test timed read where the read times out
   130      * Test timed read where the read times out
   127      */
   131      */
   128     public void testTimedRead3() throws IOException {
   132     public void testTimedRead3() throws IOException {
   129         withConnection((s1, s2) -> {
   133         withConnection((s1, s2) -> {
   130             s2.setSoTimeout(2000);
   134             s2.setSoTimeout(2000);
   131             try {
   135             long startMillis = millisTime();
   132                 s2.getInputStream().read();
   136             expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
   133                 assertTrue(false);
   137             int timeout = s2.getSoTimeout();
   134             } catch (SocketTimeoutException expected) { }
   138             checkDuration(startMillis, timeout-100, timeout+2000);
   135         });
   139         });
   136     }
   140     }
   137 
   141 
   138     /**
   142     /**
   139      * Test timed read that succeeds after a previous read has timed out
   143      * Test timed read that succeeds after a previous read has timed out
   140      */
   144      */
   141     public void testTimedRead4() throws IOException {
   145     public void testTimedRead4() throws IOException {
   142         withConnection((s1, s2) -> {
   146         withConnection((s1, s2) -> {
   143             s2.setSoTimeout(2000);
   147             s2.setSoTimeout(2000);
   144             try {
   148             expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
   145                 s2.getInputStream().read();
       
   146                 assertTrue(false);
       
   147             } catch (SocketTimeoutException e) { }
       
   148             s1.getOutputStream().write(99);
   149             s1.getOutputStream().write(99);
   149             int b = s2.getInputStream().read();
   150             int b = s2.getInputStream().read();
   150             assertTrue(b == 99);
   151             assertTrue(b == 99);
   151         });
   152         });
   152     }
   153     }
   156      * after a short delay
   157      * after a short delay
   157      */
   158      */
   158     public void testTimedRead5() throws IOException {
   159     public void testTimedRead5() throws IOException {
   159         withConnection((s1, s2) -> {
   160         withConnection((s1, s2) -> {
   160             s2.setSoTimeout(2000);
   161             s2.setSoTimeout(2000);
   161             try {
   162             expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
   162                 s2.getInputStream().read();
       
   163                 assertTrue(false);
       
   164             } catch (SocketTimeoutException e) { }
       
   165             s2.setSoTimeout(30*3000);
   163             s2.setSoTimeout(30*3000);
   166             scheduleWrite(s1.getOutputStream(), 99, 2000);
   164             scheduleWrite(s1.getOutputStream(), 99, 2000);
   167             int b = s2.getInputStream().read();
   165             int b = s2.getInputStream().read();
   168             assertTrue(b == 99);
   166             assertTrue(b == 99);
   169         });
   167         });
   173      * Test untimed read that succeeds after a previous read has timed out
   171      * Test untimed read that succeeds after a previous read has timed out
   174      */
   172      */
   175     public void testTimedRead6() throws IOException {
   173     public void testTimedRead6() throws IOException {
   176         withConnection((s1, s2) -> {
   174         withConnection((s1, s2) -> {
   177             s2.setSoTimeout(2000);
   175             s2.setSoTimeout(2000);
   178             try {
   176             expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
   179                 s2.getInputStream().read();
       
   180                 assertTrue(false);
       
   181             } catch (SocketTimeoutException e) { }
       
   182             s1.getOutputStream().write(99);
   177             s1.getOutputStream().write(99);
   183             s2.setSoTimeout(0);
   178             s2.setSoTimeout(0);
   184             int b = s2.getInputStream().read();
   179             int b = s2.getInputStream().read();
   185             assertTrue(b == 99);
   180             assertTrue(b == 99);
   186         });
   181         });
   191      * after a short delay
   186      * after a short delay
   192      */
   187      */
   193     public void testTimedRead7() throws IOException {
   188     public void testTimedRead7() throws IOException {
   194         withConnection((s1, s2) -> {
   189         withConnection((s1, s2) -> {
   195             s2.setSoTimeout(2000);
   190             s2.setSoTimeout(2000);
   196             try {
   191             expectThrows(SocketTimeoutException.class, () -> s2.getInputStream().read());
   197                 s2.getInputStream().read();
       
   198                 assertTrue(false);
       
   199             } catch (SocketTimeoutException e) { }
       
   200             scheduleWrite(s1.getOutputStream(), 99, 2000);
   192             scheduleWrite(s1.getOutputStream(), 99, 2000);
   201             s2.setSoTimeout(0);
   193             s2.setSoTimeout(0);
   202             int b = s2.getInputStream().read();
   194             int b = s2.getInputStream().read();
   203             assertTrue(b == 99);
   195             assertTrue(b == 99);
   204         });
   196         });
   209      */
   201      */
   210     public void testTimedRead8() throws IOException {
   202     public void testTimedRead8() throws IOException {
   211         withConnection((s1, s2) -> {
   203         withConnection((s1, s2) -> {
   212             s2.setSoTimeout(30*1000);
   204             s2.setSoTimeout(30*1000);
   213             scheduleClose(s2, 2000);
   205             scheduleClose(s2, 2000);
   214             try {
   206             expectThrows(SocketException.class, () -> s2.getInputStream().read());
   215                 s2.getInputStream().read();
       
   216                 assertTrue(false);
       
   217             } catch (SocketException expected) { }
       
   218         });
   207         });
   219     }
   208     }
   220 
   209 
   221     /**
   210     /**
   222      * Test read with a timeout of Integer.MAX_VALUE
   211      * Test read with a timeout of Integer.MAX_VALUE
   278      * Test timed accept where a connection is established immediately
   267      * Test timed accept where a connection is established immediately
   279      */
   268      */
   280     public void testTimedAccept1() throws IOException {
   269     public void testTimedAccept1() throws IOException {
   281         Socket s1 = null;
   270         Socket s1 = null;
   282         Socket s2 = null;
   271         Socket s2 = null;
   283         try (ServerSocket ss = new ServerSocket(0)) {
   272         try (ServerSocket ss = boundServerSocket()) {
   284             s1 = new Socket();
   273             s1 = new Socket();
   285             s1.connect(ss.getLocalSocketAddress());
   274             s1.connect(ss.getLocalSocketAddress());
   286             ss.setSoTimeout(30*1000);
   275             ss.setSoTimeout(30*1000);
   287             s2 = ss.accept();
   276             s2 = ss.accept();
   288         } finally {
   277         } finally {
   293 
   282 
   294     /**
   283     /**
   295      * Test timed accept where a connection is established after a short delay
   284      * Test timed accept where a connection is established after a short delay
   296      */
   285      */
   297     public void testTimedAccept2() throws IOException {
   286     public void testTimedAccept2() throws IOException {
   298         try (ServerSocket ss = new ServerSocket(0)) {
   287         try (ServerSocket ss = boundServerSocket()) {
   299             ss.setSoTimeout(30*1000);
   288             ss.setSoTimeout(30*1000);
   300             scheduleConnect(ss.getLocalSocketAddress(), 2000);
   289             scheduleConnect(ss.getLocalSocketAddress(), 2000);
   301             Socket s = ss.accept();
   290             Socket s = ss.accept();
   302             s.close();
   291             s.close();
   303         }
   292         }
   305 
   294 
   306     /**
   295     /**
   307      * Test timed accept where the accept times out
   296      * Test timed accept where the accept times out
   308      */
   297      */
   309     public void testTimedAccept3() throws IOException {
   298     public void testTimedAccept3() throws IOException {
   310         try (ServerSocket ss = new ServerSocket(0)) {
   299         try (ServerSocket ss = boundServerSocket()) {
   311             ss.setSoTimeout(2000);
   300             ss.setSoTimeout(2000);
       
   301             long startMillis = millisTime();
   312             try {
   302             try {
   313                 Socket s = ss.accept();
   303                 Socket s = ss.accept();
   314                 s.close();
   304                 s.close();
   315                 assertTrue(false);
   305                 fail();
   316             } catch (SocketTimeoutException expected) { }
   306             } catch (SocketTimeoutException expected) {
       
   307                 int timeout = ss.getSoTimeout();
       
   308                 checkDuration(startMillis, timeout-100, timeout+2000);
       
   309             }
   317         }
   310         }
   318     }
   311     }
   319 
   312 
   320     /**
   313     /**
   321      * Test timed accept where a connection is established immediately after a
   314      * Test timed accept where a connection is established immediately after a
   322      * previous accept timed out.
   315      * previous accept timed out.
   323      */
   316      */
   324     public void testTimedAccept4() throws IOException {
   317     public void testTimedAccept4() throws IOException {
   325         try (ServerSocket ss = new ServerSocket(0)) {
   318         try (ServerSocket ss = boundServerSocket()) {
   326             ss.setSoTimeout(2000);
   319             ss.setSoTimeout(2000);
   327             try {
   320             try {
   328                 Socket s = ss.accept();
   321                 Socket s = ss.accept();
   329                 s.close();
   322                 s.close();
   330                 assertTrue(false);
   323                 fail();
   331             } catch (SocketTimeoutException expected) { }
   324             } catch (SocketTimeoutException expected) { }
   332             try (Socket s1 = new Socket()) {
   325             try (Socket s1 = new Socket()) {
   333                 s1.connect(ss.getLocalSocketAddress());
   326                 s1.connect(ss.getLocalSocketAddress());
   334                 Socket s2 = ss.accept();
   327                 Socket s2 = ss.accept();
   335                 s2.close();
   328                 s2.close();
   340     /**
   333     /**
   341      * Test untimed accept where a connection is established after a previous
   334      * Test untimed accept where a connection is established after a previous
   342      * accept timed out
   335      * accept timed out
   343      */
   336      */
   344     public void testTimedAccept5() throws IOException {
   337     public void testTimedAccept5() throws IOException {
   345         try (ServerSocket ss = new ServerSocket(0)) {
   338         try (ServerSocket ss = boundServerSocket()) {
   346             ss.setSoTimeout(2000);
   339             ss.setSoTimeout(2000);
   347             try {
   340             try {
   348                 Socket s = ss.accept();
   341                 Socket s = ss.accept();
   349                 s.close();
   342                 s.close();
   350                 assertTrue(false);
   343                 fail();
   351             } catch (SocketTimeoutException expected) { }
   344             } catch (SocketTimeoutException expected) { }
   352             ss.setSoTimeout(0);
   345             ss.setSoTimeout(0);
   353             try (Socket s1 = new Socket()) {
   346             try (Socket s1 = new Socket()) {
   354                 s1.connect(ss.getLocalSocketAddress());
   347                 s1.connect(ss.getLocalSocketAddress());
   355                 Socket s2 = ss.accept();
   348                 Socket s2 = ss.accept();
   361     /**
   354     /**
   362      * Test untimed accept where a connection is established after a previous
   355      * Test untimed accept where a connection is established after a previous
   363      * accept timed out and after a short delay
   356      * accept timed out and after a short delay
   364      */
   357      */
   365     public void testTimedAccept6() throws IOException {
   358     public void testTimedAccept6() throws IOException {
   366         try (ServerSocket ss = new ServerSocket(0)) {
   359         try (ServerSocket ss = boundServerSocket()) {
   367             ss.setSoTimeout(2000);
   360             ss.setSoTimeout(2000);
   368             try {
   361             try {
   369                 Socket s = ss.accept();
   362                 Socket s = ss.accept();
   370                 s.close();
   363                 s.close();
   371                 assertTrue(false);
   364                 fail();
   372             } catch (SocketTimeoutException expected) { }
   365             } catch (SocketTimeoutException expected) { }
   373             ss.setSoTimeout(0);
   366             ss.setSoTimeout(0);
   374             scheduleConnect(ss.getLocalSocketAddress(), 2000);
   367             scheduleConnect(ss.getLocalSocketAddress(), 2000);
   375             Socket s = ss.accept();
   368             Socket s = ss.accept();
   376             s.close();
   369             s.close();
   379 
   372 
   380     /**
   373     /**
   381      * Test async close of a timed accept
   374      * Test async close of a timed accept
   382      */
   375      */
   383     public void testTimedAccept7() throws IOException {
   376     public void testTimedAccept7() throws IOException {
   384         try (ServerSocket ss = new ServerSocket(0)) {
   377         try (ServerSocket ss = boundServerSocket()) {
   385             ss.setSoTimeout(30*1000);
   378             ss.setSoTimeout(30*1000);
   386             scheduleClose(ss, 2000);
   379             long delay = 2000;
       
   380             scheduleClose(ss, delay);
       
   381             long startMillis = millisTime();
   387             try {
   382             try {
   388                 ss.accept().close();
   383                 ss.accept().close();
   389                 assertTrue(false);
   384                 fail();
   390             } catch (SocketException expected) { }
   385             } catch (SocketException expected) {
       
   386                 checkDuration(startMillis, delay-100, delay+2000);
       
   387             }
       
   388         }
       
   389     }
       
   390 
       
   391     /**
       
   392      * Test timed accept with the thread interrupt status set.
       
   393      */
       
   394     public void testTimedAccept8() throws IOException {
       
   395         try (ServerSocket ss = boundServerSocket()) {
       
   396             ss.setSoTimeout(2000);
       
   397             Thread.currentThread().interrupt();
       
   398             long startMillis = millisTime();
       
   399             try {
       
   400                 Socket s = ss.accept();
       
   401                 s.close();
       
   402                 fail();
       
   403             } catch (SocketTimeoutException expected) {
       
   404                 // accept should have blocked for 2 seconds
       
   405                 int timeout = ss.getSoTimeout();
       
   406                 checkDuration(startMillis, timeout-100, timeout+2000);
       
   407                 assertTrue(Thread.currentThread().isInterrupted());
       
   408             } finally {
       
   409                 Thread.interrupted(); // clear interrupt status
       
   410             }
       
   411         }
       
   412     }
       
   413 
       
   414     /**
       
   415      * Test interrupt of thread blocked in timed accept.
       
   416      */
       
   417     public void testTimedAccept9() throws IOException {
       
   418         try (ServerSocket ss = boundServerSocket()) {
       
   419             ss.setSoTimeout(4000);
       
   420             // interrupt thread after 1 second
       
   421             Future<?> interrupter = scheduleInterrupt(Thread.currentThread(), 1000);
       
   422             long startMillis = millisTime();
       
   423             try {
       
   424                 Socket s = ss.accept();   // should block for 4 seconds
       
   425                 s.close();
       
   426                 fail();
       
   427             } catch (SocketTimeoutException expected) {
       
   428                 // accept should have blocked for 4 seconds
       
   429                 int timeout = ss.getSoTimeout();
       
   430                 checkDuration(startMillis, timeout-100, timeout+2000);
       
   431                 assertTrue(Thread.currentThread().isInterrupted());
       
   432             } finally {
       
   433                 interrupter.cancel(true);
       
   434                 Thread.interrupted(); // clear interrupt status
       
   435             }
       
   436         }
       
   437     }
       
   438 
       
   439     /**
       
   440      * Test two threads blocked in timed accept where no connection is established.
       
   441      */
       
   442     public void testTimedAccept10() throws Exception {
       
   443         ExecutorService pool = Executors.newFixedThreadPool(2);
       
   444         try (ServerSocket ss = boundServerSocket()) {
       
   445             ss.setSoTimeout(4000);
       
   446 
       
   447             long startMillis = millisTime();
       
   448 
       
   449             Future<Socket> result1 = pool.submit(ss::accept);
       
   450             Future<Socket> result2 = pool.submit(ss::accept);
       
   451 
       
   452             // both tasks should complete with SocketTimeoutException
       
   453             Throwable e = expectThrows(ExecutionException.class, result1::get);
       
   454             assertTrue(e.getCause() instanceof SocketTimeoutException);
       
   455             e = expectThrows(ExecutionException.class, result2::get);
       
   456             assertTrue(e.getCause() instanceof SocketTimeoutException);
       
   457 
       
   458             // should get here in 4 seconds, not 8 seconds
       
   459             int timeout = ss.getSoTimeout();
       
   460             checkDuration(startMillis, timeout-100, timeout+2000);
       
   461         } finally {
       
   462             pool.shutdown();
       
   463         }
       
   464     }
       
   465 
       
   466     /**
       
   467      * Test two threads blocked in timed accept where one connection is established.
       
   468      */
       
   469     public void testTimedAccept11() throws Exception {
       
   470         ExecutorService pool = Executors.newFixedThreadPool(2);
       
   471         try (ServerSocket ss = boundServerSocket()) {
       
   472             ss.setSoTimeout(4000);
       
   473 
       
   474             long startMillis = millisTime();
       
   475 
       
   476             Future<Socket> result1 = pool.submit(ss::accept);
       
   477             Future<Socket> result2 = pool.submit(ss::accept);
       
   478 
       
   479             // establish connection after 2 seconds
       
   480             scheduleConnect(ss.getLocalSocketAddress(), 2000);
       
   481 
       
   482             // one task should have accepted the connection, the other should
       
   483             // have completed with SocketTimeoutException
       
   484             Socket s1 = null;
       
   485             try {
       
   486                 s1 = result1.get();
       
   487                 s1.close();
       
   488             } catch (ExecutionException e) {
       
   489                 assertTrue(e.getCause() instanceof SocketTimeoutException);
       
   490             }
       
   491             Socket s2 = null;
       
   492             try {
       
   493                 s2 = result2.get();
       
   494                 s2.close();
       
   495             } catch (ExecutionException e) {
       
   496                 assertTrue(e.getCause() instanceof SocketTimeoutException);
       
   497             }
       
   498             assertTrue((s1 != null) ^ (s2 != null));
       
   499 
       
   500             // should get here in 4 seconds, not 8 seconds
       
   501             int timeout = ss.getSoTimeout();
       
   502             checkDuration(startMillis, timeout-100, timeout+2000);
       
   503         } finally {
       
   504             pool.shutdown();
   391         }
   505         }
   392     }
   506     }
   393 
   507 
   394     /**
   508     /**
   395      * Test Socket setSoTimeout with a negative timeout.
   509      * Test Socket setSoTimeout with a negative timeout.
   409         try (ServerSocket ss = new ServerSocket()) {
   523         try (ServerSocket ss = new ServerSocket()) {
   410             ss.setSoTimeout(-1);
   524             ss.setSoTimeout(-1);
   411         }
   525         }
   412     }
   526     }
   413 
   527 
       
   528     /**
       
   529      * Returns a ServerSocket bound to a port on the loopback address
       
   530      */
       
   531     static ServerSocket boundServerSocket() throws IOException {
       
   532         var loopback = InetAddress.getLoopbackAddress();
       
   533         ServerSocket ss = new ServerSocket();
       
   534         ss.bind(new InetSocketAddress(loopback, 0));
       
   535         return ss;
       
   536     }
       
   537 
       
   538     /**
       
   539      * An operation that accepts two arguments and may throw IOException
       
   540      */
   414     interface ThrowingBiConsumer<T, U> {
   541     interface ThrowingBiConsumer<T, U> {
   415         void accept(T t, U u) throws IOException;
   542         void accept(T t, U u) throws IOException;
   416     }
   543     }
   417 
   544 
   418     /**
   545     /**
   421     static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
   548     static void withConnection(ThrowingBiConsumer<Socket, Socket> consumer)
   422         throws IOException
   549         throws IOException
   423     {
   550     {
   424         Socket s1 = null;
   551         Socket s1 = null;
   425         Socket s2 = null;
   552         Socket s2 = null;
   426         try (ServerSocket ss = new ServerSocket(0)) {
   553         try (ServerSocket ss = boundServerSocket()) {
   427             s1 = new Socket();
   554             s1 = new Socket();
   428             s1.connect(ss.getLocalSocketAddress());
   555             s1.connect(ss.getLocalSocketAddress());
   429             s2 = ss.accept();
   556             s2 = ss.accept();
   430             consumer.accept(s1, s2);
   557             consumer.accept(s1, s2);
   431         } finally {
   558         } finally {
   444             } catch (IOException ioe) { }
   571             } catch (IOException ioe) { }
   445         }, delay);
   572         }, delay);
   446     }
   573     }
   447 
   574 
   448     /**
   575     /**
       
   576      * Schedule thread to be interrupted after a delay
       
   577      */
       
   578     static Future<?> scheduleInterrupt(Thread thread, long delay) {
       
   579         return schedule(() -> thread.interrupt(), delay);
       
   580     }
       
   581 
       
   582     /**
   449      * Schedule a thread to connect to the given end point after a delay
   583      * Schedule a thread to connect to the given end point after a delay
   450      */
   584      */
   451     static void scheduleConnect(SocketAddress remote, long delay) {
   585     static void scheduleConnect(SocketAddress remote, long delay) {
   452         schedule(() -> {
   586         schedule(() -> {
   453             try (Socket s = new Socket()) {
   587             try (Socket s = new Socket()) {
   480     }
   614     }
   481     static void scheduleWrite(OutputStream out, int b, long delay) {
   615     static void scheduleWrite(OutputStream out, int b, long delay) {
   482         scheduleWrite(out, new byte[] { (byte)b }, delay);
   616         scheduleWrite(out, new byte[] { (byte)b }, delay);
   483     }
   617     }
   484 
   618 
   485     static void schedule(Runnable task, long delay) {
   619     static Future<?> schedule(Runnable task, long delay) {
   486         ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
   620         ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
   487         try {
   621         try {
   488             executor.schedule(task, delay, TimeUnit.MILLISECONDS);
   622             return executor.schedule(task, delay, TimeUnit.MILLISECONDS);
   489         } finally {
   623         } finally {
   490             executor.shutdown();
   624             executor.shutdown();
   491         }
   625         }
   492     }
   626     }
       
   627 
       
   628     /**
       
   629      * Returns the current time in milliseconds.
       
   630      */
       
   631     private static long millisTime() {
       
   632         long now = System.nanoTime();
       
   633         return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS);
       
   634     }
       
   635 
       
   636     /**
       
   637      * Check the duration of a task
       
   638      * @param start start time, in milliseconds
       
   639      * @param min minimum expected duration, in milliseconds
       
   640      * @param max maximum expected duration, in milliseconds
       
   641      * @return the duration (now - start), in milliseconds
       
   642      */
       
   643     private static long checkDuration(long start, long min, long max) {
       
   644         long duration = millisTime() - start;
       
   645         assertTrue(duration >= min,
       
   646                 "Duration " + duration + "ms, expected >= " + min + "ms");
       
   647         assertTrue(duration <= max,
       
   648                 "Duration " + duration + "ms, expected <= " + max + "ms");
       
   649         return duration;
       
   650     }
   493 }
   651 }