RE: Future.isCancelled and thread interruption

2022-02-28 Thread Pushkar N Kulkarni
Hi David,

Thank you. So,  I had the wrong notion of Thread.interrupt() also causing the 
signalling of the underlying OS thread. With that corrected, things have 
started making sense. Appreciate you taking the time to respond!

On 01/03/22, 7:59 AM, "David Holmes"  wrote:

Hi,

On 1/03/2022 4:17 am, Pushkar N Kulkarni wrote:
> "Future.cancel(true)" cancels the receiver Future and also attempts to 
interrupt the executor thread that is running this task. However, not all 
threads may be interrupted. An exception are threads that executing one of the 
"restart-able blocking system calls" from libnet.
> Such threads will ignore the thread interrupt(EINTR) and restart the 
blocking system call that was interrupted. See the system calls wrapped in the 
BLOCKING_IO_RETURN_INT macro: 
https://github.com/openjdk/jdk/blob/master/src/java.base/linux/native/libnet/linux_close.c#L352
 

You are mixing up two completely different notions of "interrupt". 
Thread.interrupt does not "interrupt" system calls - that only happens 
with signals. Thread.interrupt will not unblock low-level blocking I/O 
calls - like a socket connect. Only InterruptibleChannels provide some 
support for Thread.interrupt to break out of the I/O operation.

David
-

> 
> The javadoc for "java.util.concurrent.Future.cancel(boolean 
mayInterruptIfRunning)" DOES clarify that this method is an "attempt" to cancel 
the Future, and that it has no effect if the "task is already completed or 
cancelled, or could not be cancelled for *some other reason*". It is also made 
clear that "the return value from this method does not necessarily indicate 
whether the task is now cancelled". This is good, in the context of this note.
> 
> The javadoc also presents "Future.isCancelled()" as a definitive way to 
test if a Future was cancelled. However, it does not comment on the thread 
interruption attempted by Future.cancel(true). This might lead users to assume 
that if "Future.isCancelled()" returns true, the related executor thread was 
also successfully interrupted. This assumption would be invalid if the related 
executor thread was blocked in one of libnet's restart-able system calls 
(connect() could block for a couple of minutes).
> 
> I am attaching a test program that reproduces the mentioned behaviour. 
The executor thread held a lock and it was assumed that when 
"Future.isCancelled()" returned true, the executor had been interrupted and the 
lock released. In reality, the lock was held for a longer time and it blocked 
the main thread where the invalid assumption was made.
> 
> I am curious to know what others think of this matter! Any 
help/corrections/opinions will be appreciated. Thank you!
> 
> --
> import java.net.*;
> import java.util.concurrent.*;
> 
> public class ConnectionTest {
>  private  synchronized Socket connect(String host, int port) throws 
Exception {
>  InetSocketAddress address = new InetSocketAddress(host, port);
>  Socket s = new Socket();
>  s.connect(address); // HERE: s.connect(address, T), with any 
T>0, would resolve the hang!
>  return s;
>  }
> 
>  private Socket connectToMain() throws Exception {
>  System.out.println("Connecting to main...");
>  return connect("www.google.com", 81);
>  }
> 
>  private Socket connectToAlternate() throws Exception {
>  System.out.println("Connecting to alternate...");
>  return connect("www.example.com", 80);
>  }
> 
>  public void test() throws Exception {
>  ExecutorService es = Executors.newFixedThreadPool(1);
>  Future f = es.submit(new Callable() {
>  public Socket call() throws Exception {
>  return connectToMain();
>  }
>  });
>  try {
>  f.get(2000, TimeUnit.MILLISECONDS);
>  System.out.println("Connected to main!");
>  return;
>  } catch (TimeoutException e) {
>  System.out.println("Connection to main timed out, cancelling 
the Future with mayInterruptIfRunning = true");
>  boolean ret = f.cancel(true);
>  System.out.println("Future.cancel(true) returned " + ret);
>  }
>  System.out.println("Is Future canceled? ..." + f.isCancelled());
>  if (f.isCancelled()) {
>  connectToAlternate();
>  System.out.println("Connected to alternate!");
>  }
>  }
> 
>  public static void main(String [] args) throws Exception {
>  new ConnectionTest().test();
>  }
> }
> --
> 
> -Pushkar
> 
> 
> 



Re: Future.isCancelled and thread interruption

2022-02-28 Thread David Holmes

Hi,

On 1/03/2022 4:17 am, Pushkar N Kulkarni wrote:

"Future.cancel(true)" cancels the receiver Future and also attempts to interrupt the 
executor thread that is running this task. However, not all threads may be interrupted. An 
exception are threads that executing one of the "restart-able blocking system calls" from 
libnet.
Such threads will ignore the thread interrupt(EINTR) and restart the blocking 
system call that was interrupted. See the system calls wrapped in the 
BLOCKING_IO_RETURN_INT macro: 
https://github.com/openjdk/jdk/blob/master/src/java.base/linux/native/libnet/linux_close.c#L352


You are mixing up two completely different notions of "interrupt". 
Thread.interrupt does not "interrupt" system calls - that only happens 
with signals. Thread.interrupt will not unblock low-level blocking I/O 
calls - like a socket connect. Only InterruptibleChannels provide some 
support for Thread.interrupt to break out of the I/O operation.


David
-



The javadoc for "java.util.concurrent.Future.cancel(boolean mayInterruptIfRunning)" DOES clarify that this 
method is an "attempt" to cancel the Future, and that it has no effect if the "task is already completed 
or cancelled, or could not be cancelled for *some other reason*". It is also made clear that "the return 
value from this method does not necessarily indicate whether the task is now cancelled". This is good, in the 
context of this note.

The javadoc also presents "Future.isCancelled()" as a definitive way to test if a Future 
was cancelled. However, it does not comment on the thread interruption attempted by 
Future.cancel(true). This might lead users to assume that if "Future.isCancelled()" 
returns true, the related executor thread was also successfully interrupted. This assumption would 
be invalid if the related executor thread was blocked in one of libnet's restart-able system calls 
(connect() could block for a couple of minutes).

I am attaching a test program that reproduces the mentioned behaviour. The executor 
thread held a lock and it was assumed that when "Future.isCancelled()" returned 
true, the executor had been interrupted and the lock released. In reality, the lock was 
held for a longer time and it blocked the main thread where the invalid assumption was 
made.

I am curious to know what others think of this matter! Any 
help/corrections/opinions will be appreciated. Thank you!

--
import java.net.*;
import java.util.concurrent.*;

public class ConnectionTest {
 private  synchronized Socket connect(String host, int port) throws 
Exception {
 InetSocketAddress address = new InetSocketAddress(host, port);
 Socket s = new Socket();
 s.connect(address); // HERE: s.connect(address, T), with any T>0, 
would resolve the hang!
 return s;
 }

 private Socket connectToMain() throws Exception {
 System.out.println("Connecting to main...");
 return connect("www.google.com", 81);
 }

 private Socket connectToAlternate() throws Exception {
 System.out.println("Connecting to alternate...");
 return connect("www.example.com", 80);
 }

 public void test() throws Exception {
 ExecutorService es = Executors.newFixedThreadPool(1);
 Future f = es.submit(new Callable() {
 public Socket call() throws Exception {
 return connectToMain();
 }
 });
 try {
 f.get(2000, TimeUnit.MILLISECONDS);
 System.out.println("Connected to main!");
 return;
 } catch (TimeoutException e) {
 System.out.println("Connection to main timed out, cancelling the Future 
with mayInterruptIfRunning = true");
 boolean ret = f.cancel(true);
 System.out.println("Future.cancel(true) returned " + ret);
 }
 System.out.println("Is Future canceled? ..." + f.isCancelled());
 if (f.isCancelled()) {
 connectToAlternate();
 System.out.println("Connected to alternate!");
 }
 }

 public static void main(String [] args) throws Exception {
 new ConnectionTest().test();
 }
}
--

-Pushkar





Future.isCancelled and thread interruption

2022-02-28 Thread Pushkar N Kulkarni
"Future.cancel(true)" cancels the receiver Future and also attempts to 
interrupt the executor thread that is running this task. However, not all 
threads may be interrupted. An exception are threads that executing one of the 
"restart-able blocking system calls" from libnet.  
Such threads will ignore the thread interrupt(EINTR) and restart the blocking 
system call that was interrupted. See the system calls wrapped in the 
BLOCKING_IO_RETURN_INT macro: 
https://github.com/openjdk/jdk/blob/master/src/java.base/linux/native/libnet/linux_close.c#L352


The javadoc for "java.util.concurrent.Future.cancel(boolean 
mayInterruptIfRunning)" DOES clarify that this method is an "attempt" to cancel 
the Future, and that it has no effect if the "task is already completed or 
cancelled, or could not be cancelled for *some other reason*". It is also made 
clear that "the return value from this method does not necessarily indicate 
whether the task is now cancelled". This is good, in the context of this note.

The javadoc also presents "Future.isCancelled()" as a definitive way to test if 
a Future was cancelled. However, it does not comment on the thread interruption 
attempted by Future.cancel(true). This might lead users to assume that if 
"Future.isCancelled()" returns true, the related executor thread was also 
successfully interrupted. This assumption would be invalid if the related 
executor thread was blocked in one of libnet's restart-able system calls 
(connect() could block for a couple of minutes). 

I am attaching a test program that reproduces the mentioned behaviour. The 
executor thread held a lock and it was assumed that when "Future.isCancelled()" 
returned true, the executor had been interrupted and the lock released. In 
reality, the lock was held for a longer time and it blocked the main thread 
where the invalid assumption was made. 

I am curious to know what others think of this matter! Any 
help/corrections/opinions will be appreciated. Thank you!

--
import java.net.*;
import java.util.concurrent.*;

public class ConnectionTest {
private  synchronized Socket connect(String host, int port) throws 
Exception {
InetSocketAddress address = new InetSocketAddress(host, port);
Socket s = new Socket();
s.connect(address); // HERE: s.connect(address, T), with any T>0, would 
resolve the hang!
return s;
}

private Socket connectToMain() throws Exception {
System.out.println("Connecting to main...");
return connect("www.google.com", 81);
}

private Socket connectToAlternate() throws Exception {
System.out.println("Connecting to alternate...");
return connect("www.example.com", 80);
}

public void test() throws Exception {
ExecutorService es = Executors.newFixedThreadPool(1);
Future f = es.submit(new Callable() {
public Socket call() throws Exception {
return connectToMain();
}
}); 
try {
f.get(2000, TimeUnit.MILLISECONDS);
System.out.println("Connected to main!");
return;
} catch (TimeoutException e) {
System.out.println("Connection to main timed out, cancelling the 
Future with mayInterruptIfRunning = true");
boolean ret = f.cancel(true);
System.out.println("Future.cancel(true) returned " + ret);
}
System.out.println("Is Future canceled? ..." + f.isCancelled());
if (f.isCancelled()) {
connectToAlternate();
System.out.println("Connected to alternate!");
}
}

public static void main(String [] args) throws Exception {
new ConnectionTest().test();
}
}
--

-Pushkar