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.
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.
- duplicates
-
JDK-1237893 Solaris: block on read of System.in blocks all threads
- Closed