Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 13 Nov 1998 17:44:59 -0500 (EST)
From:      Gunther Schadow <gunther@aurora.rg.iupui.edu>
To:        freebsd-java@FreeBSD.ORG
Subject:   green threads vs. native threads ...
Message-ID:  <199811132244.RAA14641@aurora.rg.iupui.edu>

next in thread | raw e-mail | index | archive | help
Hi,

I just wrote my first threadded Java application, and I am diappointed
about the way it behaves. I am wondering whether or not the reason for
the problem I am going to explain is that I am using green threads
rather than native threads. Here goes:

I have a simple database server (non JDBC, my own protocol, very
simple) on a VAX VMS written in VAX BASIC (urgh.) The rest of my
application I develop on FreeBSD in Java. It's simple: the client
sends a request for data over TCP to the server and gets the data as a
response. This data retrieval is a performance problem. It turned out
that both client and server spend most of their time waiting for each
other while the data is in transit over the network. 

Here is what I did: to receive big amounts of data from a single
query, I created a "receiver" thread, that collects incomming data
messages as they arrive and puts them into a buffer. The main thread
takes messages from the receiver's buffer as needed. The main thread
has a lot of work to do with the messages which should give the
receiver thread enough time to read from the network. So, ideally,
both receiver and main thread should be quite busy working in
parallel, where receiver spends most of the time waiting in
java.net.SocketInputStream.read, and the main thread should be busy
processing the data. Collisions at the buffer access (which is
properly protected by a monitor) should occur not frequently.

However, as it turned out, both threads are still waiting on each
other and, most of the time, the threads seem to execute sequentially
rather than parallel. In the folloing "trace" you see a period "."
wherever the main thread gets a meessage from the receiver and an
asterisk "*" when the receiver puts a new message on the buffer. If
the buffer runs empty, the main thread suspends itself (indicated by
"w"). The receiver thread wakes the main thread up again when it
deposits a new message in the buffer. Conversely, if the buffer is
full (128 messages in this case) the receiver thread suspends itself
(indicated by "r") and is waked up again by the main thread when a
message is withdrawn from the buffer. Here is what happens:

w**********.*..*..*..*..*..*..*..*..*..w

The main thread is suspended right upfront, because the buffer is
empty at the start. Then the receiver places a couple of messages into
the buffer "*********". One is taken out ".", one in "*", two out
"..", one in, two out, one in ... until the buffer is empty again and
the main thread suspended "w". This looks kind of as expected.

But look at the rest:  ******************************
*********************.................................................
..w**************************...**************************************
.*.*.*****************************************************************
***r.................................*********************************
r.....................................................................
...........................................................w**********
**********************************************************************
***********************************************r......................
......................................................................
..................................w***********************************
**********************************************************************
**********************r...............................................
......................................................................
..........w********************************************...............
.............................

It seems like the two threads do no longer run in parallel. A whole
bunch of messages is deposited and then a whole bunch is taken out:
bunch in, bunch out, bunch in, bunch out. What you can not see is that
these dots and stars appear one by one, so that you would expect both
threads to execute during any period between two dots and stars. So,
the pattern should be ".*..**...***..*..*.*..**.***.***.***.*..**",
but what I get is bunch in, bunch out, bunch in, bunch out. This
bunch-in-bunch-out is definitely not the behavior that I see when I
run two spearate PROCESSES on FreeBSD (as you know, it always seems
like all processes run simultaneously).

Sometimes the whole thing stops at a dot for a few seconds
(indication that the main thread does some heavy stuff) without the
receiver thread taking over. Note also, that the little "r"s indicate
that the buffer actually is about to overflow several times after 128
messages received. This "w*************r.............w*************r"
indicates that each thread is running until it is suspended due to
buffer over- or underflow!

Now isn't there something seriously wrong with the scheduling of green
threads? I am wondering whether native thread will do better? What
version of FreeBSD do I have to upgrade to to get Java with native
threads? Do green threads properly suspend a thread that is waiting
for a device, just like the OS does it with processes? Or is the green
thread sheduler just a time slice weel turning at an equal speed?
Seems like old Windows 3.11 scheduler to me. Any Idea?

In order for you to see what I do, or to try it out, I append the
simple Receiver class at the end here:

class Overflow extends Throwable { };

public class Receiver 
  extends Thread
{
  // THE RINGBUFFER
  // 1. capacity
  static final int capacity = 128;
  // 2. the buffer array
  Message[] buffer = new Message[capacity];
  // 3. the writer's (receiver's) pointer
  int wpointer = 0;
  // 4. the reader's (client's, main thread's) pointer
  int rpointer = capacity;
  

  // STUFF THAT HANDLES SUSPENSION FOR UNDER- OR OVERFLOW 
  // this is raised (locally) for under and overflow.
  Overflow overflow = new Overflow();
  // if buffer underflow occured
  boolean client_waits = false;
  // if buffer overflow occured
  boolean receiver_waits = false;
  // a handle for the receiver to wake up a suspended client
  Thread client;


  // VARIABLES PERTAINING TO THE MESSAGE STREAM

  // this is the source of incomming messages
  Connection conn;
  // the end of the message stream is indicated in this flag
  boolean end = false;



  // THE MONITOR

  // used by receiver thread to deposit incoming message
  private synchronized void padd(Message dat)
       throws Overflow
  {
    if(wpointer == rpointer)
      {
	receiver_waits = true;
	throw overflow;
      }
    
    buffer[wpointer++] = dat;
    if(wpointer >= capacity) 
      wpointer = 0;

    if(client_waits)
      {
	client_waits = false;
	client.resume();
      }
  }

  // used by receiver thread to mark the end of the message stream
  private synchronized void pend() { end = true; }
  
  // used by client (main) thread to withdraw message
  private synchronized Message pget()
       throws Overflow
  {
    int npointer = rpointer + 1;
    if(npointer >= capacity) 
      npointer = 0;
    
    if(npointer == wpointer)
      if(end)
	return null;
      else 
	{
	  client_waits = true;
	  throw overflow;
	}
    
    rpointer = npointer;

    Message r = buffer[rpointer];

    if(receiver_waits)
      {
	receiver_waits = false;
	this.resume();
      }

    return r;
  }

  // end of monitor


  // THE PUBLIC INTERFACE TO THE CLIENT THREAD:

  public boolean hasMoreMessages() { return ! end; }
  
  public Row nextMessage() { 
    while(true) {
      try {
        return pget(); 
      } catch(Overflow x) { 
	System.out.print("w");
	client.suspend(); 
      }
    }
  }
  
  public Receiver(Connection conn) 
  {
    super("receiver");
    this.conn   = conn;
    this.client = currentThread();
  }


  // THE RECEIVER THREAD LOOP
  
  public void run() 
  {
      while(true) {
	// receive a data message
	Message msg = conn.receive();
	
	switch(msg.getType()) {
	case Message.DAT:
	  // save message
	  while(true)
	    {
	      try {
		padd(msg);
		System.out.print("*");
		break;
	      } catch(Overflow o) { }
	      System.out.print("r");
	      this.suspend();
	    }
	  break;
	  
	case Message.END:
	  end = true;
	  this.stop();
	}
      }    
  }

}


thanks and sorry for the long posting ...
-Gunther

Gunther Schadow ----------------------------------- http://aurora.rg.iupui.edu
Regenstrief Institute for Health Care
1001 W 10th Street RG5, Indianapolis IN 46202, Phone: (317) 630 7960
schadow@aurora.rg.iupui.edu ---------------------- #include <usual/disclaimer>

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-java" in the body of the message



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199811132244.RAA14641>