Sunday, May 27, 2012

Low level interrupts

[Disclaimer: I don't make my living from writing C so the following should all be taken with a pinch of salt. I am a lowly Java programmer who is interested in what is going on in the low-level Linux world].

In my last post, I showed that Java interrupts are not achieved by Linux kernel calls even though Linux does allow such an "interrupt()".

Take this C code (SignalHandling.c):

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>



void sig_handler(int signum)
{
    fprintf(stdout, "\n=============\nReceived signal %d\n==============\n", signum);
}


void addSigAction() {
    struct sigaction sa;
    sa.sa_handler = sig_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_INTERRUPT; /* Restart functions if
                                 interrupted by handler */
    if (sigaction(SIGINT, &sa, NULL) == -1) {
        fprintf(stderr, "Could not install signal handler\n");
    } else {
        fprintf(stdout, "addSigAction: registered handler\n");
    }
}



void* runSleep(void* object)
{
    addSigAction();
    printf("runSleep: about to sleep\n");
    sleep(100);
    printf("runSleep: finished sleeping\n");
}




int main(int argc, char *argv[])
{
    pid_t pid = getpid();
  fprintf(stdout, "Main: Started. PID = %d\n", pid);


  pthread_t otherThread; 


  int error = pthread_create(&otherThread, NULL, runSleep, NULL);


  printf("Hit return...\n");
  getchar();
  pthread_kill(otherThread, SIGINT);


  pthread_join(otherThread, NULL);
  fprintf(stdout, "Finished\n");
}

Forget rogue includes for now - we'll be needing them later.

Compile it with:


gcc -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/SignalHandling.d" -MT"src/SignalHandling.d" -o"src/SignalHandling.o" "../src/SignalHandling.c"


gcc  -o"SignalHandling"  ./src/SignalHandling.o   -lpthread -lrt


(Note the -lpthread switch to use the thread library).

This code starts a new thread (pthread_create) - the equivalent of instantiating a new java.lang.Thread with a Runnable that is actually the runSleep method.

(The first NULL is the thread attributes and the second is the argument passed to runSleep - that is: nothing as runSleep doesn't take an argument).

This new thread registers a signal handler (sig_handler method) and then sleeps for 100s.

Meanwhile, the main thread that waits for the user to hit return before sending a message to the first thread (pthread_kill).

Don't be fooled by this method name. The documentation in the GLIBC sigthread.h file says it will:
Send signal SIGNO to the given thread.
Indeed, using strace to see what kernel calls are being made shows that this method delegates to tgkill. The man docs say:
tgkill() sends the signal sig to the thread with the thread ID tid in the thread group tgid. (By contrast, kill(2) can only be used to send a signal to a process (i.e., thread group) as a whole, and the signal will be delivered to an arbitrary thread within that process.)
The use of the word kill is historic. The Linux Programming Interface says:
The term kill was chosen because the default action of most of the signals that were available on early UNIX implementations was to terminate the process.
Anyway, the point is that a kernel call is made to tell the other thread when an interrupt signal (SIGINT in the GLIBC signum.h) is sent. This does not appear to be the case in the Linux Java implementation that I am using (1.7.0-ea-b125 on Linux 2.6.32.9-70.fc12.i686).

My next post should (hopefully) be about what happens if a thread is waiting on a stream.

Sunday, May 20, 2012

A Polite Interruption

We thought we were being clever when unit testing some IO code using PipedInputStreams instead of an implementation of InputStream that used system resources. Oops.

No less an expert than Brian Goetz is somewhat misleading when he says:

"The input and output stream classes may block waiting for an I/O to complete, but they do not throw InterruptedException, and they do not return early if they are interrupted." [1]

PipedInputStream behaves differently (from what I can tell) to all other implementations of InputStream when the thread blocking on its read method is interrupted. It returns immediately with an InterruptedIOException (basically, an InterruptedException that fits into the IOException class hierarchy).

From the JDK (1.6) source code:


    /**
     * The index of the position in the circular buffer at which the
     * next byte of data will be stored when received from the connected
     * piped output stream. in<0 implies the buffer is empty,
     * in==out implies the buffer is full
     * @since   JDK1.1
     */
    protected int in = -1;
.
.
    public synchronized int read()  throws IOException {
.
.


  while (in < 0) {
    if (closedByWriter) {
      /* closed by writer, return EOF */
      return -1;
    }
    if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
      throw new IOException("Pipe broken");
    }
            /* might be a writer waiting */
    notifyAll();
    try {
        wait(1000);
    } catch (InterruptedException ex) {
      throw new java.io.InterruptedIOException();
    }
  }


The read() method it just checking to see if anybody called interrupt() on the Thread running this code and if they had then the exception is thrown.

Although a call to Thread.interrupt() ultimately hits a native method, using strace I could not see any kernel calls being made (JDK build 1.7.0-ea-b125, Linux 2.6.32.9-70.fc12.i686). It appears that interrupt() just sets a flag. That is all.

So, how do you stop a thread that's blocking on reading from a stream? One way is to close the underlying resource (eg, by calling Socket.setSoTimeout() if the stream is associated with a socket).

But another way is to use Java NIO. If you look at the Thread.interrupt() method, you'll see this code:

.
.

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
.
.
    public void interrupt() {
.
.

    Interruptible b = blocker;
    if (b != null) {
      interrupt0(); // Just to set the interrupt flag
      b.interrupt();
      return;
    }



The object on which Thread.interrupt in turn calls interrupt is an instance of sun.nio.ch.Interruptible. This instance is set in the package protected blockedOn method and the call tree of this looks like:


blockedOn(Interruptible) : void - java.lang.Thread
blockedOn(Thread, Interruptible) : void - java.lang.new JavaLangAccess() {...}
[constructor] new JavaLangAccess() {...} - java.lang 
[callers]
blockedOn(Interruptible) : void - java.nio.channels.spi.AbstractInterruptibleChannel
begin() : void - java.nio.channels.spi.AbstractInterruptibleChannel
begin() : void - java.nio.channels.spi.AbstractSelector


Looking at java.nio.channels.spi.AbstractInterruptibleChannel, for instance, we find that the Interruptible is being used as a callback:


    interruptor = new Interruptible() {
    public void interrupt() {
      synchronized (closeLock) {
        if (!open)
          return;
        interrupted = true;
        open = false;
        try {
          AbstractInterruptibleChannel.this.implCloseChannel();
        } catch (IOException x) { }
      }
    }};

Whereupon:

"An implementation of this method must arrange for any other thread that is blocked in an I/O operation upon this channel to return immediately, either by throwing an exception or by returning normally." [2]


And this is how NIO works: the Thread class needed to be changed to accommodate the ability to interrupt a thread blocking on input. It also closes the underlying resource as explained in Java NIO:


"[T]he blocked thread will be sent a ClosedByInterruptException.  Additionally, if a thread's interrupt status is set, and that thread attempts to access a channel, the channel will immediately be closed, and the same exception will be thrown... It may seem rather draconian to shut down a channel just because a thread sleeping on that channel was interrupted. But this is an explicit design decision made by the NIO architects. Experience has shown that it's impossible to reliably handle interrupted I/O operations consistently across all operating systems. The requirement to provide deterministic channel behavior on all platforms led to the design choice of always closing channels when I/O operations are interrupted. This was deemed acceptable, because a thread is most often interrupted so it can be told to shut down. The java.nio package mandates this behavior to avoid the quagmire of operating-system peculiarities, which is especially treacherous in this area. This is a classic trade-off of features for robustness." [3]



[1] http://www.ibm.com/developerworks/java/library/j-jtp05236/index.html
[2] http://docs.oracle.com/javase/6/docs/api/java/nio/channels/spi/AbstractInterruptibleChannel.html
[3] Java NIO (O'Reilly) - Ron Hitchens