Thread scheduling problem in Linux jdk-1.1.7B
=============================================

I am currently developing a GUI for an upcoming game server "VC"
(telnet external.nj.nec.com 4000). After finishing the graphics part I
thought the rest - that is connecting the GUI to the server via socket
- would be trivial. Well - it's not.

It seems that the thread scheduler doesn't continue threads which wait
for I/O although all other threads are waiting. The appended code
illustrates the problem. "TestSocket" connects an applet to the
text-based server (VC), awaits keyboard input which it sends to VC,
and writes VC messages to stout. Depending on the global flags
RUN_TICKER and COMMUNICATE TestSocket's behaviour can be changed to
illustrate the problems.

On my machine (kernel 2.2.5, RedHat 6.0, jdk-1.1.7B-glibc,
green_threads) the program - started by "java TestSocket" - behaves
like follows:


RUN_TICKER = false, COMMUNICATE = false :

no connection to VC, keyboard input echoed to stdout. 

                                     OK


RUN_TICKER = false, COMMUNICATE = true :

connection to VC established - OK, but keyboard input is no longer
being echoed. br.readLine() seems to hang. Messages from VC are
correctly written to stdout, though. 

                                      unusable


RUN_TICKER = true, COMMUNICATE = true :

connection to VC established - OK. "ticker" is a max-priority thread
which stays in a loop sleeping and printing dots. Now, keyboard inputs
are echoed but not sent to the server. This indicates that a socket
related thread is hanging. The funny thing, however, is that when a
message is sent to the applet from the server it is correctly
displayed as before and seems to unlock the write routine. After
reading a message from VC the keyboard command gets delivered to VC.

                                     better, but still unusable


In this application all threads are sleeping most of the time. Unless
I am a victim of some nasty selfmade deadlock I think the reported
behaviour is due to a scheduler bug. NB.: The original applet
is working under Netscape-4.6 Java.

Comments - and especially workarounds - are very welcome.

--
Michael Buro             [EMAIL PROTECTED] 
NEC Research Institute   
4 Independence Way,      Tel: +1 (609) 951-2703
Princeton NJ 08540 USA   Fax: +1 (609) 951-2488

http://www.neci.nj.nec.com/homepages/mic


Appendix:
=========


  Here is how to log on VC to repeat the experiments:

  > telnet external.nj.nec.com 4000 <return>
 
  > ... asking for login & password (arbitrary)

  > tell mic hi <return>

    sends "hi" to the applet

  > who <return>

    reports connected users. Typed into the stdin
    window, the list of connected users should be sent to
    the applet and written on stdout.



The code:


//  TestSocket.java

import java.io.*;
import java.net.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;

import SocketComm;
import Ticker;


// not yet a real applet. start via "main"

public class TestSocket extends Applet
{
  public static final boolean RUN_TICKER  = false;
  public static final boolean COMMUNICATE = true;

  SocketComm sc   = null;
  VCReader   vcr  = null;
  Thread   ticker = null;  

  class VCReader extends Thread {

    public VCReader() {

      // connect to VC
      
      String s = "";
      
      try {
          s = sc.connect("external.nj.nec.com", 4000, "mic", "");
      }
      catch(UnknownHostException f) { throw new Error("" + f); }
      catch(IOException f) { throw new Error("" + f); }

      // print all received messages

      System.out.println(s);
    }

    public void run() {

      // handle messages coming from VC
      
      while (true) {
        String s = sc.read_line();
        System.out.println(s);
      }
    }
  }

  TestSocket()
  {
    sc = new SocketComm();

    ticker = new Thread(new Ticker(1000));
    ticker.setPriority(Thread.MAX_PRIORITY);

    if (RUN_TICKER) ticker.start();
        
    if (COMMUNICATE) {
      vcr = new VCReader();
      vcr.start();
    }
  }
  
  public static void main(String[] args) throws IOException {

    TestSocket ts = new TestSocket();
    BufferedReader br = new BufferedReader (new InputStreamReader(System.in));

    System.out.println("so far so good");

    while (true) {
      String line = br.readLine();
      System.out.println("input was: " + line);

      if (TestSocket.COMMUNICATE) ts.sc.write_line(line);
    }
  }
}



///////////////////////////////////////////////////////////////

// SocketCom.java

import java.io.*;
import java.net.*;
import java.util.*;

class SocketComm
{
  private String host;
  private int    port;
  private Socket so;
  private BufferedReader br;
  private BufferedWriter bw;

  public SocketComm()
  {
    host = null;
    port = 0;
    so   = null;
    br   = null;
    bw   = null;
  }

  public void write_line(String line)
  {
    try { bw.write(line); bw.newLine(); bw.flush(); }
    catch(IOException e) {
      System.out.println("write_line: " + e);
      System.exit(-1);
    }
  }

  public String read_line()
  {
    String s = "";

    try {
      s = br.readLine();
    }
    catch (IOException e) {
      System.out.println("read_line: " + e);
      System.exit(-1);
    }

    return s;
  }

  // return all messages sent from server
  
  public String connect(String host, int port, String name, String password)
    throws IOException, UnknownHostException
  {
    System.out.println("trying to connect " + name + "@" + host + ":" + port);

    String collected = "";

    this.host = host;
    this.port = port;
    
    so  = new Socket(host, port);

    if (false) { // test exception
      IOException e = new IOException("connect: test");
      throw e;
    }
    
    br = new BufferedReader(new InputStreamReader(so.getInputStream()));
    bw = new BufferedWriter(new OutputStreamWriter(so.getOutputStream()));

    String line;

    // wait for login prompt

    while (true) {
      line = br.readLine();
      collected += line + "\n";
      if (line.endsWith("use).")) break;
    }

    // send login

    write_line(name);

    // wait for password prompt
    
    while (true) {
      line = br.readLine();
      collected += line + "\n";
      if (line.endsWith("password: ")) break;
    }

    // send password
    
    write_line(password);

    // wait for READY
    
    while (true) {
      line = br.readLine();
      collected += line + "\n";
      if (line.endsWith("READY")) break;
    }

    System.out.println("connected");

    // done
    
    return collected;
  }


}


/////////////////////////////////////////////////////////////////////////

// Misc.java

public class Misc {

  public static void sleep(int n) 
  {
    try { Thread.currentThread().sleep(n); }
    catch(InterruptedException e) {;}
  }

}



////////////////////////////////////////////////////////////////////////

// Ticker.java

import java.lang.*;

public class Ticker implements Runnable
{
  int interval;
  
  public Ticker(int interval)
  {
    this.interval = interval;
  }

  public void run()
  {
    while (true) { Misc.sleep(interval); 
    System.out.println(".");
    }
  }
}


----------------------------------------------------------------------
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to