Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-1237893

Solaris: block on read of System.in blocks all threads

    XMLWordPrintable

Details

    • 1.2beta3
    • generic, sparc
    • solaris_2.4, solaris_2.5, solaris_2.5.1, solaris_2.6
    • Not verified

    Description

      Under Solaris, doing a simple blocking read of stdin doesn't yeild, blocking
      all threads. Reproducable with the following code...thread "t" doesn't ever
      get to run, while the main thread is waiting for keyboard input (and echoing
      the lines):


      import java.io.*;

      public class ThreadBlock implements Runnable {
          public void run() {
      System.out.println("I'm here");
          }
          public static void main(String argv[]) {
      Thread t;
      DataInputStream commandInput = new DataInputStream(System.in);
      String line;

      t = new Thread(new ThreadBlock(), "test");
      t.start();
      try {
      while ((line = commandInput.readLine()) != null) {
      System.out.println(line);
      }
      } catch (IOException e) {
      System.err.println("IO exception on commandInput: " + e);
      }
          }
      }

      The description field as copied from bug report 1234747 follows:


      Try the program below. It should print out a message every second while
      waiting for input. This is not what happens though. Instead it wakes up
      the printer thread each time you enter a line of input.


      import java.io.*;

      class ReadTest extends Thread {
          String str;

          ReadTest(String str) {
      this.str = str;
      start();
          }

          public void run() {
      System.out.println("ready: " + str);
      while (true) {
      try {sleep(1000);} catch (InterruptedException e) {}
      System.out.println(str);
      }
          }

          public static void main(String argv[]) {
      new ReadTest("printer");

      try {
      System.out.println("reader");

      DataInputStream in = new DataInputStream(System.in);
      for (int i = 0 ; i < 100 ; i++) {
      System.out.println("INPUT " + i + " = " +in.readLine());
      try {sleep(1000);} catch (InterruptedException e) {}
      }
      } catch (IOException e) {
      e.printStackTrace();
      }
          }
      }

      The description field as copied from bug report 1234076 follows:


      From: ###@###.### (Delong Yu)



      System.in.read() blocks other threads. If it is suspended in jdb, the other
      threads are running fine, but once it is resumed, all the other threads stop.

      Here is an example (modified Clock.java):

      ance and flashes a lot. (YUCK).
      mport java.awt.Graphics;
      import java.util.Date;
      import java.io.IOException;

      public class Clock extends java.applet.Applet implements Runnable {

          Thread clockThread;
              AnotherThread anotherThread;
              OneMoreThread oneMoreThread;

          public void start() {
              if (clockThread == null) {
                  clockThread = new Thread(this, "Clock");
                  clockThread.start();
              }
              if (oneMoreThread == null) {
                  oneMoreThread = new OneMoreThread();
                  oneMoreThread.start();
              }
              if (anotherThread == null) {
                  anotherThread = new AnotherThread();
                  anotherThread.start();
              }
          }
          public void run() {
              while (clockThread != null) {
                  repaint();
                  try {
                      clockThread.sleep(1000);
                  } catch (InterruptedException e){
                  }
              }
          }
          public void paint(Graphics g) {
              Date now = new Date();
              g.drawString(now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds()
      , 5, 10);
          }
          public void stop() {
              clockThread.stop();
              clockThread = null;
          }
      }

      class AnotherThread extends Thread {


              public AnotherThread () {
              }

              public void run () {

                      try {
                              while(true) {
                                      System.in.read();
                                      System.out.println("read a byte");
                              }
                      } catch(IOException e) {
                      }

              }
      }

      class OneMoreThread extends Thread {


              public OneMoreThread () {
              }

              public void run () {

                      try {
                              while(true) {
                                      System.out.println("before sleep");
                                      this.sleep(1000);
                              }
                      }
                      catch(InterruptedException e) {
                              System.err.println("InterruptedException " +e );
                      }

              }
      }

      The description field as copied from bug report 1254377 follows:

      A process with two threads, one receiving UDP packets and the other reading from standard input, receives no UDP packets.

      There are two code samples below. The first, udpchat.java, is a java class that demonstrates the problem. It has two threads. One thread reads lines from standard input and sends them in UDP packets. The other thread receives UDP packets and displays them on standard output. The problem is that the receiving/displaying thread never receives the packets. The reading/sending thread works fine. Setting the SENDTHREAD constant to false disables the reading/sending thread; in this case, the receiving/displaying thread works fine.

      The second code sample is udpchat.c, which is the equivalent program in C. This is useful for testing the java program and for exploring how both programs work. Note that udpchat.c works perfectly.

      Note that both programs send and receive from the same port, so you need to use two computers to demonstrate this bug.

      /* ************ start: udpchat.java *************** */

      import java.net.DatagramSocket;
      import java.net.DatagramPacket;
      import java.net.InetAddress;
      import java.io.IOException;
      import java.net.SocketException;
      import java.net.UnknownHostException;

      class udpchat
      implements Runnable
      {
      int port;
      Thread receiver;
      DatagramSocket sock;
      InetAddress maddr;
      static final int MAXBYTESPERPACKET = 500;
      static final boolean DEBUG = true;
      static final boolean SENDTHREAD = true;

      public static void main(String args[])
      {
      String host;
      int port = 1414;
      udpchat me;

      if (args.length < 1)
      {
      System.err.println("Usage: java udpchat dest [port]");
      System.exit(1);
      }
      host = args[0];
      if (args.length >= 2)
      {
      port = Integer.parseInt(args[1]);
      if (args.length >= 3)
      {
      System.err.println("Usage: java udpchat dest [port]");
      System.exit(1);
      }
      }
      if (DEBUG) System.err.println("Host: "+host+" port: "+port);
      me = new udpchat(host,port);
      me.go();
      }
      public udpchat(String host, int port)
      {
      try {maddr = InetAddress.getByName(host);}
      catch (UnknownHostException e)
      {
      System.err.println("unknown host: "+host+": "+e.getMessage());
      System.exit(1);
      }
      try {sock = new DatagramSocket(port);}
      catch (SocketException e)
      {
      System.err.println("socket: "+e.getMessage());
      System.exit(1);
      }
      this.port = port;
      }
      public void go()
      {
      int c, i = 0;
      byte bbuf[] = new byte[MAXBYTESPERPACKET];
      DatagramPacket spacket;

      receiver = new Thread(this);
      receiver.start();

      if (SENDTHREAD) {
      try
      {
      while ((c = System.in.read()) != -1)
      {
      receiver.suspend();
      while (i < MAXBYTESPERPACKET && c != -1 && c != '\\n')
      {
      bbuf[i++] = (byte) c;
      c = System.in.read();
      }
      if (c == '\\n') bbuf[i++] = (byte) c;
      spacket = new DatagramPacket(bbuf,i,maddr,port);
      i = 0;
      sock.send(spacket);
      receiver.resume();
      }
      }
      catch (IOException e)
      {
      System.err.println("IOException: "+e.getMessage());
      System.exit(1);
      }
      } else { // !SENDTHREAD
      try {receiver.join();} catch (InterruptedException e)
      {
      System.err.println("join: "+e.getMessage());
      System.exit(1);
      }
      } // SENDTHREAD

      receiver.stop();
      sock.close();
      }
      public void run()
      {
      byte bbuf[] = new byte[MAXBYTESPERPACKET];
      DatagramPacket rpacket = new DatagramPacket(bbuf,MAXBYTESPERPACKET);

      while (true)
      {
      try {sock.receive(rpacket);}
      catch (IOException e)
      {
      System.err.println("recv: IOException: "+e.getMessage());
      System.exit(1);
      }
      System.out.write(rpacket.getData(),0,rpacket.getLength());
      if (DEBUG) System.err.println("Got packet, length "+rpacket.getLength());
      }
      }
      }
      /* ************* end: udpchat.java ************** */

      /* ************* start: udpchat.c ************** */
      /* compile with: cc -o udpchat udpchat.c -lsocket -lnsl */
      #include <sys/types.h>
      #include <sys/socket.h>
      #include <netinet/in.h>
      #include <netdb.h>
      #include <unistd.h>
      #include <stdio.h>
      #include <errno.h>
      #include <stdlib.h>

      #define BUFLEN 80

      int main(int argc, char **argv)
      {
      int sock;
      struct sockaddr_in serveraddress, destaddress;
      int port = 1414; /* port to use */
      int optval; /* for setsockopt */
      fd_set readset;
      char buf[BUFLEN];
      int dlen;
      struct in_addr defaddr;
      struct hostent *hp, def;
      char *alist[1];
      char * host;
      struct sockaddr fromaddress;
      int fromaddrlen;

      if (argc > 3 || argc < 2)
      {
      fprintf(stderr,"Usage: %s dest[portnum]\\n",argv[0]);
      exit(1);
      }
      host = argv[1];
      if (argc > 2)
      port = atoi(argv[2]);
      printf("Sending to %s port %d and listening on port %d.\\n",host,port,port);
      if (!(hp = gethostbyname(host)))
      {
      defaddr.s_addr = inet_addr(host);
      if (defaddr.s_addr == -1)
      {
      fprintf(stderr,"unknown host: %s\\n", host);
      return 1;
      }
      def.h_name = host;
      def.h_addr_list = alist;
      def.h_addr = (char *)&defaddr;
      def.h_length = sizeof(struct in_addr);
      def.h_addrtype = AF_INET;
      def.h_aliases = 0;
      hp = &def;
      }
      destaddress.sin_family = hp->h_addrtype;
      bcopy(hp->h_addr, (char *)&destaddress.sin_addr, hp->h_length);
      destaddress.sin_port = htons(port);
      serveraddress.sin_family = AF_INET;
      serveraddress.sin_port = htons(port);
      serveraddress.sin_addr.s_addr = 0;
      sock = socket(AF_INET,SOCK_DGRAM,0);
      if (sock == -1)
      {
      perror("socket");
      exit(1);
      }
      optval = 1;
      if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void *)&optval,sizeof(optval)))
      {
      perror("setsockopt(reuseaddr)");
      exit(1);
      }
      if (bind(sock,(struct sockaddr *) &serveraddress,sizeof(serveraddress)) != 0)
      {
      perror("bind");
      exit(1);
      }

      FD_ZERO(&readset);
      FD_SET(sock,&readset);
      FD_SET(0,&readset);
      while (select(getdtablesize(),&readset,NULL,NULL,NULL)>0)
      {
      if (FD_ISSET(sock,&readset))
      {
      fromaddrlen = sizeof(fromaddress);
      dlen = recvfrom(sock,buf,BUFLEN,0,&fromaddress,
      &fromaddrlen);
      if (dlen < 0)
      {
      perror("read remote");
      close(sock);
      return 1;
      }
      write(1,buf,dlen);
      }
      if (FD_ISSET(0,&readset))
      {
      if (fgets(buf,BUFLEN,stdin) == NULL)
      if (!feof(stdin))
      {
      perror("read local");
      close(sock);
      return 1;
      }
      else
      {
      close(sock);
      return 0;
      }
      if (sendto(sock,buf,strlen(buf),0, (struct sockaddr *)&destaddress, sizeof(destaddress)) == -1)
      {
      perror("send");
      close(sock);
      return 1;
      }
      }
      FD_SET(sock,&readset);
      FD_SET(0,&readset);
      }
      perror("select");
      close(sock);
      return 1;
      }
      /* ********** end: udpchat.c ********** */


      More information from Steve Soule, 6/11/96: I've attempted to track down this problem and I think I've found the cause of the problem. File descriptors 0, 1, and 2 (stdin, stdout, and stderr) are never set to non-blocking mode; see the note at line 249 of src/solaris/java/green_threads/src/iomgr.c. I feel that this is correct; I tried setting stdin to non-blocking on one of my shells and it flipped out. However, many of the I/O routines in iomgr.c and io_md.c (the ones that call check_single_fd()) assume that their file descriptor is set to non-blocking mode. In particular, the routine sysReadFD in io_md.c, when called for stdin, blocks when it calls read, and so it never yields, even after it actually reads something. Unfortunately, the only fix I can think of is to, say, call poll() or select() in every routine that currently calls check_single_fd().

      Also note that file descriptors 0, 1, and 2 are probably not getting added to the poll table, so the interrupt/polling/wakeup code might not be effective for them. I did a truss on java running udpchat and saw that the interrupt handler that got called when a UDP packet arrived called poll() with a list of 4 file descriptors. I would guess that one of these file descriptors is the UDP socket, but what are the other 3? poll() returned the value 4, indicating that something was happening on all 4 file descriptors. I don't know if this was the correct result, but it may just be the result of the fact that the polling table is set up so that file descriptors are marked with POLLIN|POLLOUT, so an event is indicated if they're ready for reading or writing. Since file descriptors are usually ready for writing, perhaps this isn't appropriate, since it means that lots of threads get woken up when they don't need to be. You might want to fix this, but I don't think it's important.

      Attachments

        Issue Links

          Activity

            People

              never Tom Rodriguez
              duke J. Duke
              Votes:
              0 Vote for this issue
              Watchers:
              0 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: