NBIO: Nonblocking I/O for Java
Release notes

Matt Welsh, mdw@cs.berkeley.edu
Last updated 11 July 2002

[Back to SEDA Release Documentation]

Introduction

NBIO is a library which implements nonblocking I/O facilities for Java. Surprisingly, the standard JDK libraries (prior to JDK 1.4) do not provide nonblocking I/O. This means that in order to implement applications (such as web servers and other Internet services) which support many concurrent I/O streams, a large number of threads must be used. However, the overhead of threading (in Java, as well as more generally) limits the performance of such an implementation.

What is needed is a nonblocking I/O library which allows a small number of threads to be used, along with a select() or poll() like mechanism to test for incoming I/O events on a large number of streams. This is what NBIO provides.

Requirements

NBIO is implemented using native calls (through JNI) to create and manage nonblocking sockets, as well as to provide select()-like functionality (that is, to test for I/O readiness or completion across a large number of sockets). For event delivery, NBIO supports both the UNIX poll(2) system call and the /dev/poll event-delivery mechanism. (Note that select() itself is not used, mainly because on most UNIX systems select() does not perform well with a large number of sockets.) NBIO is known to work on Linux 2.2 and 2.4 systems, Solaris 7 and 8, FreeBSD, and HP/UX. Because it uses standard UNIX system calls, it should simply work with, or at least be easy to port to, a large number of other UNIX systems.

We have also made a BETA release of NBIO for Windows 2000 systems. It is available from the SEDA SourceForge pages.

Note on JDK 1.4 java.nio package

The recently-announced JDK 1.4 beta includes the package java.nio which, among other things, provides nonblocking I/O primitives for Java. As it turns out I am on the expert group for the Sun Java Specification Request for this package (see this link for more details). More details on this new API can be found at this URL; as you can see, java.nio has been influenced somewhat by the NBIO APIs.

My plan is to continue developing and supporting NBIO until stable, released versions of JDK 1.4 are available on Linux both from Sun and IBM. At that point I will deprecate NBIO in favor of the new APIs, but NBIO will continue to be supported (although the development will be frozen). Migration from NBIO to java.nio should not be difficult. If you are in need of an efficient, working nonblocking I/O library for Java, my recommendation is to go ahead and use NBIO, and move over to java.nio once it is available.

Note that the Sandstorm system can use either NBIO or java.nio for nonblocking I/O.

Compiling NBIO

If you are compiling the complete SEDA code tree (including NBIO, Sandstorm, etc.), simply follow the instructions in the Sandstorm release notes instead of those given here. If you only wish to compile NBIO, you need to configure the following environment variables:

  1. Be sure that your CLASSPATH contains the following two entries:

    For example, under Bourne-style UNIX shells, type:

      CLASSPATH=$CLASSPATH:/path/to/seda/src:.
      export CLASSPATH
    
    Under C-style shells:
      setenv CLASSPATH "$CLASSPATH":/path/to/seda/src:.
    
    Replacing /path/to/seda with the directory where you unpacked the SEDA release.

  2. Be sure that your LD_LIBRARY_PATH contains the "seda/lib" directory in the SEDA release. This is necessary for the JVM to locate the NBIO native libraries.

    Under Bourne-style shells,

      LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/seda/lib
      export LD_LIBRARY_PATH
    
    Under C-style shells,
      setenv LD_LIBRARY_PATH "$LD_LIBRARY_PATH":/path/to/seda/lib
    

To compile NBIO, simply cd to the seda/src/nbio directory and type make. If there are any problems with compilation, be absolutely sure that seda/src and "." are in fact on your CLASSPATH. This is the most common problem when building the code.

Package contents

This package is called seda.nbio.

You can read the JavaDoc documentation for NBIO here. The public classes included in the package are:

NonblockingInputStream
A subclass of java.io.InputStream which supports nonblocking semantics.

NonblockingOutputStream
A subclass of java.io.OutputStream which supports nonblocking semantics.

NonblockingSocket
A subclass of java.net.Socket which supports nonblocking semantics. Socket connection and read/write are nonblocking.

NonblockingServerSocket
A variant of java.net.ServerSocket which supports nonblocking semantics. Connection accept is nonblocking.

NonblockingDatagramSocket
A variant of java.net.DatagramSocket which supports nonblocking semantics. Socket read/write are nonblocking.

NonblockingMulticastSocket
A variant of java.net.MulticastSocket which supports nonblocking semantics. Socket read/write are nonblocking.

SelectSet
A class which implements select(), allowing you to poll for events across a number of nonblocking I/O streams. The interface is in fact very similar to the SVR4 poll() system call.

SelectItem
A class which represents a single nonblocking I/O stream to be used with SelectSet.

Test programs

In the test directory you will find a number of test programs which demonstrate the use of this library:

test/test
Blocking and nonblocking variants of a simple server and client application, used for demonstration purposes.

test/p2p-bench
The TCPBench program is a point-to-point TCP benchmark, like ttcp for Java. It can use either blocking or nonblocking sockets, and reports both round-trip latency and bandwidth for a given message size.

test/multi-bench
The MultiServer program is a benchmark which implements a simple TCP server which supports many connections. It is capable of using either nonblocking sockets (with a single thread) or blocking sockets (with many threads). It exchanges a series of packets which each client that connects to it. The benchmark reports the total bandwidth measured by the server. The MultiClient program is used as the client.

Using the library

Note that NBIO is for use with NATIVE THREADS ONLY, not the "Green Threads" package supported by older JVMs. I believe that all JVMs from JDK 1.3 onwards only support native threads, so this is not a problem for most users. However, if you are using an older JVM with Green Threads, you need to be aware of this issue.

The problem is that a blocking call to SelectSet.select() actually blocks the OS thread making the call. If you are using Green Threads, this will block the entire JVM, because Green Threads uses a single OS thread to emulate many Java threads. This is fine for a single-threaded application, but in general you only want to block the Java thread making the call. Supporting this behavior under Green Threads is complicated and I don't think it's important; with the NBIO library you should be able to use native threads, since you will need much fewer of them than you would with blocking I/O.

Of course, you can use the other NBIO routines (nonblocking sockets, etc.) with Green Threads, since they do not block. SelectSet.select() is the only real problem. However, since Green Threads appears to be obsolete, I strongly recommend only using Native Threads with NBIO.

If you are running on Linux 2.2.x systems you may wish to increase your file descriptor limit, which increases the number of simultaneous socket connections the system can have. This is important as one of the basic uses for NBIO is to write server applications which can support many simultaneous connections (many more connections than were possible using threads). This is relatively simple to do:

  1. Create the file /etc/initscript (if it does not already exist) and place the following commands into it:
    umask 022
    ulimit -c 2097151
    ulimit -Hn 32768
    PATH=/bin:/sbin:/usr/bin:/usr/sbin
    export PATH
    eval exec "$4"
    
    If /etc/initscript already exists, add the command
    ulimit -Hn 32768
    
    to it.

  2. Add the following commands to /etc/rc.d/rc.local (or some other startup script which is run at boot time):
    echo 32768 > /proc/sys/fs/file-max
    echo 65536 > /proc/sys/fs/inode-max
    

  3. Add the following command to your login scripts (e.g., .bashrc or .cshrc):
    ulimit -n 32768
    

  4. Reboot the system for the above changes to take effect.

In the future we would like to support nonblocking disk I/O, however, most UNIX-based operating systems do not in fact support nonblocking access to disk files; you must use a lower-level "raw disk" facility instead. If anyone wishes to implement nonblocking file or disk I/O for NBIO, please get in touch with the SEDA project developers.

Copyright license

This code is covered under the following Open Source license:

Copyright (c) 2002 by Matt Welsh and The Regents of the University of California. All rights reserved.

Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without written agreement is hereby granted, provided that the above copyright notice and the following two paragraphs appear in all copies of this software.

IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

If you have any questions, comments, or bug reports, don't hesitate to get in touch with me!


Matt Welsh / mdw@cs.berkeley.edu