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]