diff --git a/make/java/nio/mapfile-bsd b/make/java/nio/mapfile-bsd index 070646b708e773ec1259062c0fb2e8cf84acf505..45b23ab8652478120b3cc76fbb48fcb8064ccae1 100644 --- a/make/java/nio/mapfile-bsd +++ b/make/java/nio/mapfile-bsd @@ -108,6 +108,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_Net_setInterface6; Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; + Java_sun_nio_ch_Net_poll; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 4cc3b2910c1c581e75a41054c50024d42a5e7368..37999cb7e3bb3dc297c9f7bf5b27ab1aab362c63 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -116,6 +116,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_Net_setInterface6; Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; + Java_sun_nio_ch_Net_poll; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index e05ea725fe1167a8f22c00f70dfc4dde9ee7dad3..beb13e25d6fd182f770e3131eb0befb2256f11d4 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -104,6 +104,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_Net_setInterface6; Java_sun_nio_ch_Net_getInterface6; Java_sun_nio_ch_Net_shutdown; + Java_sun_nio_ch_Net_poll; Java_sun_nio_ch_PollArrayWrapper_interrupt; Java_sun_nio_ch_PollArrayWrapper_poll0; Java_sun_nio_ch_ServerSocketChannelImpl_accept0; diff --git a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java index cd65cb99a4b239521c555dbc9b0b87e85eb0cdfa..3d60a38d74ccf9af405330507ef354ca6737be1a 100644 --- a/src/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/src/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -1059,6 +1059,28 @@ class DatagramChannelImpl return translateReadyOps(ops, 0, sk); } + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (readLock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + readerThread = 0; + end(n > 0); + } + return n; + } + } + /** * Translates an interest operation set into a native poll event set */ diff --git a/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java b/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java index dcd507a2e9f3d7e2b8d12ebeca0e0731eb155973..92ea5870ca410228b966ffb96d7d643a0b255ebc 100644 --- a/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/DatagramSocketAdaptor.java @@ -176,40 +176,31 @@ public class DatagramSocketAdaptor return dc.receive(bb); } - // Implement timeout with a selector - SelectionKey sk = null; - Selector sel = null; dc.configureBlocking(false); try { int n; SocketAddress sender; if ((sender = dc.receive(bb)) != null) return sender; - sel = Util.getTemporarySelector(dc); - sk = dc.register(sel, SelectionKey.OP_READ); long to = timeout; for (;;) { if (!dc.isOpen()) throw new ClosedChannelException(); long st = System.currentTimeMillis(); - int ns = sel.select(to); - if (ns > 0 && sk.isReadable()) { + int result = dc.poll(PollArrayWrapper.POLLIN, to); + if (result > 0 && + ((result & PollArrayWrapper.POLLIN) != 0)) { if ((sender = dc.receive(bb)) != null) return sender; } - sel.selectedKeys().remove(sk); to -= System.currentTimeMillis() - st; if (to <= 0) throw new SocketTimeoutException(); } } finally { - if (sk != null) - sk.cancel(); if (dc.isOpen()) dc.configureBlocking(true); - if (sel != null) - Util.releaseTemporarySelector(sel); } } diff --git a/src/share/classes/sun/nio/ch/Net.java b/src/share/classes/sun/nio/ch/Net.java index 9f2d1f01776f3085b807310ace5390e6a93ca1d7..48c971bc7105cf7b11224d43cd62333a70604476 100644 --- a/src/share/classes/sun/nio/ch/Net.java +++ b/src/share/classes/sun/nio/ch/Net.java @@ -409,6 +409,9 @@ public class Net { int level, int opt, int arg) throws IOException; + static native int poll(FileDescriptor fd, int events, long timeout) + throws IOException; + // -- Multicast support -- diff --git a/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java b/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java index f897d3460db0ea7a6fda2eca959e3f4cb833adb1..a3a88ce2ca29d8402d8c988d682cfb847c004bc4 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/ServerSocketAdaptor.java @@ -102,37 +102,26 @@ public class ServerSocketAdaptor // package-private return sc.socket(); } - // Implement timeout with a selector - SelectionKey sk = null; - Selector sel = null; ssc.configureBlocking(false); try { SocketChannel sc; if ((sc = ssc.accept()) != null) return sc.socket(); - sel = Util.getTemporarySelector(ssc); - sk = ssc.register(sel, SelectionKey.OP_ACCEPT); long to = timeout; for (;;) { if (!ssc.isOpen()) throw new ClosedChannelException(); long st = System.currentTimeMillis(); - int ns = sel.select(to); - if (ns > 0 && - sk.isAcceptable() && ((sc = ssc.accept()) != null)) + int result = ssc.poll(PollArrayWrapper.POLLIN, to); + if (result > 0 && ((sc = ssc.accept()) != null)) return sc.socket(); - sel.selectedKeys().remove(sk); to -= System.currentTimeMillis() - st; if (to <= 0) throw new SocketTimeoutException(); } } finally { - if (sk != null) - sk.cancel(); if (ssc.isOpen()) ssc.configureBlocking(true); - if (sel != null) - Util.releaseTemporarySelector(sel); } } catch (Exception x) { diff --git a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java index a7f90a9660c95c3f4908a300d8e55e6137873005..728d865b8784b901f07f788a3617404225dd0e3d 100644 --- a/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/ServerSocketChannelImpl.java @@ -324,6 +324,28 @@ class ServerSocketChannelImpl return translateReadyOps(ops, 0, sk); } + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (lock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + thread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + thread = 0; + end(n > 0); + } + return n; + } + } + /** * Translates an interest operation set into a native poll event set */ diff --git a/src/share/classes/sun/nio/ch/SocketAdaptor.java b/src/share/classes/sun/nio/ch/SocketAdaptor.java index 2fbc517193c5299b89acd9dd5acf15d77ec76f85..ab3dbcf379b21d67d2ee77819c6d15e14d10609d 100644 --- a/src/share/classes/sun/nio/ch/SocketAdaptor.java +++ b/src/share/classes/sun/nio/ch/SocketAdaptor.java @@ -97,25 +97,19 @@ public class SocketAdaptor return; } - // Implement timeout with a selector - SelectionKey sk = null; - Selector sel = null; sc.configureBlocking(false); try { if (sc.connect(remote)) return; - sel = Util.getTemporarySelector(sc); - sk = sc.register(sel, SelectionKey.OP_CONNECT); long to = timeout; for (;;) { if (!sc.isOpen()) throw new ClosedChannelException(); long st = System.currentTimeMillis(); - int ns = sel.select(to); - if (ns > 0 && - sk.isConnectable() && sc.finishConnect()) + + int result = sc.poll(PollArrayWrapper.POLLCONN, to); + if (result > 0 && sc.finishConnect()) break; - sel.selectedKeys().remove(sk); to -= System.currentTimeMillis() - st; if (to <= 0) { try { @@ -125,12 +119,8 @@ public class SocketAdaptor } } } finally { - if (sk != null) - sk.cancel(); if (sc.isOpen()) sc.configureBlocking(true); - if (sel != null) - Util.releaseTemporarySelector(sel); } } catch (Exception x) { @@ -199,39 +189,29 @@ public class SocketAdaptor throw new IllegalBlockingModeException(); if (timeout == 0) return sc.read(bb); - - // Implement timeout with a selector - SelectionKey sk = null; - Selector sel = null; sc.configureBlocking(false); + try { int n; if ((n = sc.read(bb)) != 0) return n; - sel = Util.getTemporarySelector(sc); - sk = sc.register(sel, SelectionKey.OP_READ); long to = timeout; for (;;) { if (!sc.isOpen()) throw new ClosedChannelException(); long st = System.currentTimeMillis(); - int ns = sel.select(to); - if (ns > 0 && sk.isReadable()) { + int result = sc.poll(PollArrayWrapper.POLLIN, to); + if (result > 0) { if ((n = sc.read(bb)) != 0) return n; } - sel.selectedKeys().remove(sk); to -= System.currentTimeMillis() - st; if (to <= 0) throw new SocketTimeoutException(); } } finally { - if (sk != null) - sk.cancel(); if (sc.isOpen()) sc.configureBlocking(true); - if (sel != null) - Util.releaseTemporarySelector(sel); } } diff --git a/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/share/classes/sun/nio/ch/SocketChannelImpl.java index f0f7255161411d7c10b48a667cc0ee6058724806..9696038ecbf29d2cef5f216b48fe321f5ad13cc6 100644 --- a/src/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -914,6 +914,28 @@ class SocketChannelImpl return translateReadyOps(ops, 0, sk); } + // package-private + int poll(int events, long timeout) throws IOException { + assert Thread.holdsLock(blockingLock()) && !isBlocking(); + + synchronized (readLock) { + int n = 0; + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) + return 0; + readerThread = NativeThread.current(); + } + n = Net.poll(fd, events, timeout); + } finally { + readerCleanup(); + end(n > 0); + } + return n; + } + } + /** * Translates an interest operation set into a native poll event set */ diff --git a/src/share/classes/sun/nio/ch/Util.java b/src/share/classes/sun/nio/ch/Util.java index 1d8f6000c489d4bb62a8129226c74acf34cdf933..73f71898a730c67e7ecf9e3b16899c1afcfc358e 100644 --- a/src/share/classes/sun/nio/ch/Util.java +++ b/src/share/classes/sun/nio/ch/Util.java @@ -218,66 +218,6 @@ public class Util { ((DirectBuffer)buf).cleaner().clean(); } - private static class SelectorWrapper { - private Selector sel; - private SelectorWrapper (Selector sel) { - this.sel = sel; - Cleaner.create(this, new Closer(sel)); - } - private static class Closer implements Runnable { - private Selector sel; - private Closer (Selector sel) { - this.sel = sel; - } - public void run () { - try { - sel.close(); - } catch (Throwable th) { - throw new Error(th); - } - } - } - public Selector get() { return sel;} - } - - // Per-thread cached selector - private static ThreadLocal> localSelector - = new ThreadLocal>(); - // Hold a reference to the selWrapper object to prevent it from - // being cleaned when the temporary selector wrapped is on lease. - private static ThreadLocal localSelectorWrapper - = new ThreadLocal(); - - // When finished, invoker must ensure that selector is empty - // by cancelling any related keys and explicitly releasing - // the selector by invoking releaseTemporarySelector() - static Selector getTemporarySelector(SelectableChannel sc) - throws IOException - { - SoftReference ref = localSelector.get(); - SelectorWrapper selWrapper = null; - Selector sel = null; - if (ref == null - || ((selWrapper = ref.get()) == null) - || ((sel = selWrapper.get()) == null) - || (sel.provider() != sc.provider())) { - sel = sc.provider().openSelector(); - selWrapper = new SelectorWrapper(sel); - localSelector.set(new SoftReference(selWrapper)); - } - localSelectorWrapper.set(selWrapper); - return sel; - } - - static void releaseTemporarySelector(Selector sel) - throws IOException - { - // Selector should be empty - sel.selectNow(); // Flush cancelled keys - assert sel.keys().isEmpty() : "Temporary selector not empty"; - localSelectorWrapper.set(null); - } - // -- Random stuff -- diff --git a/src/solaris/native/sun/nio/ch/Net.c b/src/solaris/native/sun/nio/ch/Net.c index 2f01af33efa68cd1cb041ed63b83751af6104ea6..907baff1ba89758b811637e971a09b64682eadcf 100644 --- a/src/solaris/native/sun/nio/ch/Net.c +++ b/src/solaris/native/sun/nio/ch/Net.c @@ -38,6 +38,7 @@ #include "net_util_md.h" #include "nio_util.h" #include "nio.h" +#include "sun_nio_ch_PollArrayWrapper.h" #ifdef _ALLBSD_SOURCE @@ -627,6 +628,26 @@ Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) handleSocketError(env, errno); } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout) +{ + struct pollfd pfd; + int rv; + pfd.fd = fdval(env, fdo); + pfd.events = events; + rv = poll(&pfd, 1, timeout); + + if (rv >= 0) { + return pfd.revents; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else if (rv < 0) { + handleSocketError(env, errno); + return IOS_THROWN; + } +} + + /* Declared in nio_util.h */ jint diff --git a/src/windows/native/sun/nio/ch/Net.c b/src/windows/native/sun/nio/ch/Net.c index 55c03a28b5b40fa9ebe20b34e9fc7e5d1cfa0274..36a5d2b302c64fc866d2c7db98af65c6b961ef61 100644 --- a/src/windows/native/sun/nio/ch/Net.c +++ b/src/windows/native/sun/nio/ch/Net.c @@ -35,6 +35,7 @@ #include "net_util.h" #include "sun_nio_ch_Net.h" +#include "sun_nio_ch_PollArrayWrapper.h" /** * Definitions to allow for building with older SDK include files. @@ -524,3 +525,49 @@ Java_sun_nio_ch_Net_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow) { NET_ThrowNew(env, WSAGetLastError(), "shutdown"); } } + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_Net_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout) +{ + int rv; + int revents = 0; + struct timeval t; + int lastError = 0; + fd_set rd, wr, ex; + jint fd = fdval(env, fdo); + + t.tv_sec = timeout / 1000; + t.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + if (events & sun_nio_ch_PollArrayWrapper_POLLIN) { + FD_SET(fd, &rd); + } + if (events & sun_nio_ch_PollArrayWrapper_POLLOUT || + events & sun_nio_ch_PollArrayWrapper_POLLCONN) { + FD_SET(fd, &wr); + } + FD_SET(fd, &ex); + + rv = select(fd+1, &rd, &wr, &ex, &t); + + /* save last winsock error */ + if (rv == SOCKET_ERROR) { + handleSocketError(env, lastError); + return IOS_THROWN; + } else if (rv >= 0) { + rv = 0; + if (FD_ISSET(fd, &rd)) { + rv |= sun_nio_ch_PollArrayWrapper_POLLIN; + } + if (FD_ISSET(fd, &wr)) { + rv |= sun_nio_ch_PollArrayWrapper_POLLOUT; + } + if (FD_ISSET(fd, &ex)) { + rv |= sun_nio_ch_PollArrayWrapper_POLLERR; + } + } + return rv; +} diff --git a/test/java/nio/channels/etc/AdaptorCloseAndInterrupt.java b/test/java/nio/channels/etc/AdaptorCloseAndInterrupt.java new file mode 100644 index 0000000000000000000000000000000000000000..8d99fde7a411f3f0e938cb56d22c1f66a6e1faa9 --- /dev/null +++ b/test/java/nio/channels/etc/AdaptorCloseAndInterrupt.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2012, 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 7184932 + * @summary Test asynchronous close and interrupt of timed socket adapter methods + */ + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.net.*; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.Random; + + +public class AdaptorCloseAndInterrupt { + private static final ScheduledExecutorService pool = + Executors.newScheduledThreadPool(1); + final ServerSocketChannel listener; + final DatagramChannel peer; + final int port; + + public AdaptorCloseAndInterrupt() { + listener = null; + peer = null; + port = -1; + } + + public AdaptorCloseAndInterrupt(ServerSocketChannel listener) { + this.listener = listener; + this.port = listener.socket().getLocalPort(); + this.peer = null; + } + + public AdaptorCloseAndInterrupt(DatagramChannel listener) { + this.peer = listener; + this.port = peer.socket().getLocalPort(); + this.listener = null; + } + + public static void main(String args[]) throws Exception { + try { + try (ServerSocketChannel listener = ServerSocketChannel.open()) { + listener.socket().bind(null); + new AdaptorCloseAndInterrupt(listener).scReadAsyncClose(); + new AdaptorCloseAndInterrupt(listener).scReadAsyncInterrupt(); + } + + try (DatagramChannel peer = DatagramChannel.open()) { + peer.socket().bind(null); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncClose(); + new AdaptorCloseAndInterrupt(peer).dcReceiveAsyncInterrupt(); + } + + new AdaptorCloseAndInterrupt().ssAcceptAsyncClose(); + new AdaptorCloseAndInterrupt().ssAcceptAsyncInterrupt(); + } finally { + pool.shutdown(); + } + System.out.println("Test Passed"); + } + + void scReadAsyncClose() throws IOException { + try { + SocketChannel sc = SocketChannel.open(new InetSocketAddress( + "127.0.0.1", port)); + sc.socket().setSoTimeout(30*1000); + + doAsyncClose(sc); + + try { + sc.socket().getInputStream().read(new byte[100]); + throw new RuntimeException("read should not have completed"); + } catch (ClosedChannelException expected) {} + + if (!sc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } finally { + // accept connection and close it. + listener.accept().close(); + } + } + + void scReadAsyncInterrupt() throws IOException { + try { + final SocketChannel sc = SocketChannel.open(new InetSocketAddress( + "127.0.0.1", port)); + sc.socket().setSoTimeout(30*1000); + + doAsyncInterrupt(); + + try { + sc.socket().getInputStream().read(new byte[100]); + throw new RuntimeException("read should not have completed"); + } catch (ClosedByInterruptException expected) { + Thread.currentThread().interrupted(); + } + + if (!sc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } finally { + // accept connection and close it. + listener.accept().close(); + } + } + + void dcReceiveAsyncClose() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + dc.connect(new InetSocketAddress( + "127.0.0.1", port)); + dc.socket().setSoTimeout(30*1000); + + doAsyncClose(dc); + + try { + dc.socket().receive(new DatagramPacket(new byte[100], 100)); + throw new RuntimeException("receive should not have completed"); + } catch (ClosedChannelException expected) {} + + if (!dc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } + + void dcReceiveAsyncInterrupt() throws IOException { + DatagramChannel dc = DatagramChannel.open(); + dc.connect(new InetSocketAddress( + "127.0.0.1", port)); + dc.socket().setSoTimeout(30*1000); + + doAsyncInterrupt(); + + try { + dc.socket().receive(new DatagramPacket(new byte[100], 100)); + throw new RuntimeException("receive should not have completed"); + } catch (ClosedByInterruptException expected) { + Thread.currentThread().interrupted(); + } + + if (!dc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } + + void ssAcceptAsyncClose() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.socket().bind(null); + ssc.socket().setSoTimeout(30*1000); + + doAsyncClose(ssc); + + try { + ssc.socket().accept(); + throw new RuntimeException("accept should not have completed"); + } catch (ClosedChannelException expected) {} + + if (!ssc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } + + void ssAcceptAsyncInterrupt() throws IOException { + ServerSocketChannel ssc = ServerSocketChannel.open(); + ssc.socket().bind(null); + ssc.socket().setSoTimeout(30*1000); + + doAsyncInterrupt(); + + try { + ssc.socket().accept(); + throw new RuntimeException("accept should not have completed"); + } catch (ClosedByInterruptException expected) { + Thread.currentThread().interrupted(); + } + + if (!ssc.socket().isClosed()) + throw new RuntimeException("socket is not closed"); + } + + void doAsyncClose(final AbstractSelectableChannel sc) { + AdaptorCloseAndInterrupt.pool.schedule(new Callable() { + public Void call() throws Exception { + sc.close(); + return null; + } + }, new Random().nextInt(1000), TimeUnit.MILLISECONDS); + } + + void doAsyncInterrupt() { + final Thread current = Thread.currentThread(); + AdaptorCloseAndInterrupt.pool.schedule(new Callable() { + public Void call() throws Exception { + current.interrupt(); + return null; + } + }, new Random().nextInt(1000), TimeUnit.MILLISECONDS); + } + +}