001/* VMChannel.java -- Native interface suppling channel operations.
002   Copyright (C) 2006 Free Software Foundation, Inc.
003
004The original of this file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037package gnu.java.nio;
038
039import gnu.classpath.Configuration;
040
041import java.io.IOException;
042import java.net.Inet4Address;
043import java.net.Inet6Address;
044import java.net.InetAddress;
045import java.net.InetSocketAddress;
046import java.net.SocketAddress;
047import java.net.SocketException;
048import java.nio.ByteBuffer;
049import java.nio.MappedByteBuffer;
050
051import org.jikesrvm.VM;
052import org.jikesrvm.mm.mminterface.MemoryManager;
053import org.jikesrvm.runtime.FileSystem;
054import org.vmmagic.pragma.NonMovingAllocation;
055
056
057/**
058 * Native interface to support configuring of channel to run in a non-blocking
059 * manner and support scatter/gather io operations.
060 * <p>
061 * JikesRVM-specific implementation by Robin Garner and Filip Pizlo.
062 * <p>
063 * Note: JavaDoc from GNU Classpath has been deleted on methods where it would
064 * cause JavaDoc warnings.
065 */
066public final class VMChannel
067{
068  /**
069   * Our reference implementation uses an integer to store the native
070   * file descriptor.
071   */
072  private final State nfd;
073
074  private Kind kind;
075
076  public VMChannel()
077  {
078    // XXX consider adding security check here, so only Classpath
079    // code may create instances.
080    this.nfd = new State();
081    kind = Kind.OTHER;
082  }
083
084  VMChannel(final int native_fd) throws IOException
085  {
086    this();
087    this.nfd.setNativeFD(native_fd);
088  }
089
090  public State getState()
091  {
092    return nfd;
093  }
094
095  /**
096   * Don't do fast I/O for the standard file descriptors - these are used
097   * for throwing exceptions, so may not be able to allocate.
098   *
099   * Cache the max of the IDs to avoid gratuitous native calls.
100   */
101  private static int MAX_STANDARD_FD;
102
103  private static int max(int[] values) {
104    int result = values[0];
105    for (int i : values)
106      if (i > result) result = i;
107    return result;
108  }
109
110  static
111  {
112    // load the shared library needed for native methods.
113    if (Configuration.INIT_LOAD_LIBRARY)
114    {
115      System.loadLibrary ("javanio");
116    }
117    initIDs();
118    MAX_STANDARD_FD = max(new int[] {stdin_fd(),stdin_fd(),stderr_fd()});
119  }
120
121  public static VMChannel getStdin() throws IOException
122  {
123    return new VMChannel(stdin_fd());
124  }
125
126  public static VMChannel getStdout() throws IOException
127  {
128    return new VMChannel(stdout_fd());
129  }
130
131  public static VMChannel getStderr() throws IOException
132  {
133    return new VMChannel(stderr_fd());
134  }
135
136  private static native int stdin_fd();
137  private static native int stdout_fd();
138  private static native int stderr_fd();
139
140
141  public void setBlocking(boolean blocking) throws IOException
142  {
143    setBlocking(nfd.getNativeFD(), blocking);
144  }
145
146  private static native void setBlocking(int fd, boolean blocking)
147  throws IOException;
148
149  public int available() throws IOException
150  {
151    return available(nfd.getNativeFD());
152  }
153
154  private static native int available(int native_fd) throws IOException;
155
156  /**
157   * A thread-local store of non-moving buffers.  Used to perform IO to
158   * in cases where the actual user buffer is in a moving space.
159   */
160  private static class LocalByteArray extends ThreadLocal<byte[]> {
161    private static final int INITIAL_BUFFER_SIZE = 8192;
162    @Override
163    @NonMovingAllocation
164    protected byte[] initialValue() {
165      return new byte[INITIAL_BUFFER_SIZE];
166    }
167
168    /**
169     * Get a buffer, ensuring it is at least 'len' in size
170     * @param len Minimum length of the buffer
171     * @return a new or recycled buffer
172     */
173    @NonMovingAllocation
174    public byte[] get(int len) {
175      byte[] buf = get();
176      if (buf.length < len) {
177        /* Allocate a new buffer by successive doubling of capacity */
178        int newCapacity = buf.length << 1;
179        while (newCapacity < len)
180          newCapacity <<= 1;
181        buf = new byte[newCapacity];
182        set(buf);
183      }
184      return buf;
185    }
186  }
187
188  /**
189   * Thread-local buffer for VM-side buffering of write calls
190   */
191  private static final LocalByteArray localByteArray = new LocalByteArray() ;
192
193  /**
194   * Reads a byte buffer directly using the supplied file descriptor.
195   *
196   * @param dst Direct Byte Buffer to read to.
197   * @return Number of bytes read.
198   * @throws IOException If an error occurs or dst is not a direct buffers.
199   */
200  public int read(ByteBuffer dst) throws IOException {
201    return read(dst,dst.position(),dst.limit()-dst.position());
202  }
203
204  /*
205   * Read a byte buffer, given a starting position and length.
206   * Looks at the type of buffer and decides which is the fastest way
207   * to perform the write.  If the buffer is backed by a byte array, use
208   * the internal method, otherwise push it out to classpath's native function
209   * (the slow way).
210   *
211   * @param dst
212   * @param pos
213   * @param len
214   * @return the number of bytes actually read
215   * @throws IOException
216   */
217  private int read(ByteBuffer dst, int pos, int len) throws IOException {
218    int bytes;
219    if (len == 1) {
220      int b = FileSystem.readByte(nfd.getNativeFD());
221      if (b >= 0) {
222        dst.put((byte)(b & 0xFF));
223        dst.position(pos+1);
224        return 1;
225      } else
226        bytes = b;
227    } else if (dst.hasArray()) {
228      bytes = read(dst.array(),pos,len);
229    } else {
230      return read(nfd.getNativeFD(), dst);
231    }
232    if (bytes > 0)
233      dst.position(pos+bytes);
234    return bytes;
235  }
236
237  /**
238   * Reads a byte array directly.  Performs optimal buffering.
239   *
240   * If the target buffer is pinned, use it directly.  Otherwise
241   * allocate one of the thread-local buffers, perform the IO to
242   * that, and copy the result to the target array.
243   *
244   * @param dst Byte array to read to
245   * @param pos Starting offset in the buffer
246   * @param len Number of bytes to read
247   * @return Number of bytes read.
248   * @throws IOException If an error occurs or dst is not a direct buffers.
249   */
250  private int read(byte[] dst, int pos, int len) throws IOException {
251    if (MemoryManager.willNeverMove(dst)) {
252      return read(nfd.getNativeFD(),dst,pos,len);
253    } else {
254      byte[] buffer;
255      // Rebuffer the IO in a thread-local byte array
256      buffer = localByteArray.get(len);
257
258      /* perform the read */
259      int bytes = read(nfd.getNativeFD(),buffer,0,len);
260      if (bytes > 0)
261        System.arraycopy(buffer,0,dst,pos,bytes);
262      return bytes;
263    }
264  }
265
266  /**
267   * Use JikesRVM's internal read function - the fast way.
268   *
269   * @param fd File descriptor
270   * @param dst Destination buffer
271   * @param position Starting offset in the buffer
272   * @param len Number of bytes to read
273   * @return Number of bytes read, or -1 for end of file.
274   * @throws IOException when an error occurs during reading
275   */
276  private static int read(int fd, byte[] dst, int position, int len) throws IOException {
277    if (VM.VerifyAssertions) VM._assert(MemoryManager.willNeverMove(dst));
278    int bytes = FileSystem.readBytes(fd,dst,position,len);
279    if (bytes < 0) {
280      throw new IOException("Error code "+Integer.toString(bytes));
281    }
282    if (bytes == 0) {
283      bytes = -1;
284    }
285    return bytes;
286  }
287
288  /**
289   * Classpath's native read method.  Slow, due to the amount of JNI processing.
290   *
291   * @param fd the file descriptor of the file to read
292   * @param dst the buffer that the read bytes will be written to
293   * @return the number of bytes actually read
294   * @throws IOException when an error occurs during reading
295   */
296  private static native int read(int fd, ByteBuffer dst) throws IOException;
297
298  public int read() throws IOException
299  {
300    //return read(nfd.getNativeFD());
301    int result = FileSystem.readByte(nfd.getNativeFD());
302    if (result < -1) {
303      throw new IOException("Error code "+Integer.toString(result));
304    }
305    return result;
306  }
307
308  private static native int read(int fd) throws IOException;
309
310  /**
311   * Reads into byte buffers directly using the supplied file descriptor.
312   * Assumes that the buffer list contains DirectBuffers.  Will perform a
313   * scattering read.
314   *
315   * @param dsts An array direct byte buffers.
316   * @param offset Index of the first buffer to read to.
317   * @param length The number of buffers to read to.
318   * @return Number of bytes read.
319   * @throws IOException If an error occurs or the dsts are not direct buffers.
320   */
321  public long readScattering(ByteBuffer[] dsts, int offset, int length)
322  throws IOException
323  {
324    if (offset + length > dsts.length)
325      throw new IndexOutOfBoundsException("offset + length > dsts.length");
326
327    return readScattering(nfd.getNativeFD(), dsts, offset, length);
328  }
329
330  private static native long readScattering(int fd, ByteBuffer[] dsts,
331      int offset, int length)
332  throws IOException;
333
334  public SocketAddress receive(ByteBuffer dst) throws IOException
335  {
336    if (kind != Kind.SOCK_DGRAM)
337      throw new SocketException("not a datagram socket");
338    ByteBuffer hostPort = ByteBuffer.allocateDirect(18);
339    int hostlen = receive(nfd.getNativeFD(), dst, hostPort);
340    if (hostlen == 0)
341      return null;
342    if (hostlen == 4) // IPv4
343    {
344      byte[] addr = new byte[4];
345      hostPort.get(addr);
346      int port = hostPort.getShort() & 0xFFFF;
347      return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
348    }
349    if (hostlen == 16) // IPv6
350    {
351      byte[] addr = new byte[16];
352      hostPort.get(addr);
353      int port = hostPort.getShort() & 0xFFFF;
354      return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
355    }
356
357    throw new SocketException("host address received with invalid length: "
358        + hostlen);
359  }
360
361  private static native int receive (int fd, ByteBuffer dst, ByteBuffer address)
362  throws IOException;
363
364  /**
365   * Writes from a byte array using the supplied file descriptor.
366   *
367   * @param src The source buffer.
368   * @param pos Starting offset in the buffer
369   * @param len Number of bytes to write
370   * @return Number of bytes written.
371   * @throws IOException when an error occurs during writing
372   */
373  public int write(byte[] src, int pos, int len) throws IOException {
374    if (MemoryManager.willNeverMove(src)) {
375      return write(nfd.getNativeFD(), src, pos, len);
376    } else {
377      byte[] buffer;
378      // Rebuffer the IO in a thread-local DirectBuffer
379      buffer = localByteArray.get(len);
380      if (VM.VerifyAssertions) VM._assert(MemoryManager.willNeverMove(buffer));
381      System.arraycopy(src, pos, buffer,0,len);
382      return write(nfd.getNativeFD(),buffer,0,len);
383    }
384  }
385
386  public int write(ByteBuffer src, int pos, int len) throws IOException {
387    int bytes;
388    if (len == 1) {
389      int ok = FileSystem.writeByte(nfd.getNativeFD(),src.get(pos));
390      if(ok == 0){
391        bytes = 1;
392      }else{
393        throw new IOException("Error code " + Integer.toString(ok));
394      }
395    } else if (src.hasArray()) {
396      bytes = write(src.array(),pos,len);
397    } else {
398      // Use classpath version, which does buffer housekeeping
399      return write(nfd.getNativeFD(), src);
400    }
401    if (bytes > 0)
402      src.position(src.position()+bytes);
403    return bytes;
404  }
405
406  public int write(ByteBuffer src) throws IOException {
407    if (nfd.getNativeFD() > MAX_STANDARD_FD)
408      return write(src,src.position(),src.limit()-src.position());
409    else
410      return write(nfd.getNativeFD(),src);
411  }
412
413  /**
414   * Use JikesRVM's internal read function - the fast way.
415   *
416   * @param fd File descriptor
417   * @param src Source buffer
418   * @param pos Starting offset in the buffer
419   * @param len Number of bytes to write
420   * @return Number of bytes written.
421   * @throws IOException when an error occurs during writing
422   */
423  private static int write(int fd, byte[] src, int pos, int len) throws IOException {
424    int bytes = FileSystem.writeBytes(fd,src,pos,len);
425    if (bytes < 0)
426      throw new IOException("Error code "+Integer.toString(bytes));
427    return bytes;
428  }
429
430  /**
431   * Classpath's native write method.  Slow, due to the amount of JNI processing.
432   *
433   * @param fd the file's descriptor
434   * @param src the source buffer for the bytes that will be written
435   * @return Number of bytes written
436   * @throws IOException when an error occurs during writing
437   */
438  private static native int write(int fd, ByteBuffer src) throws IOException;
439
440  public long writeGathering(ByteBuffer[] srcs, int offset, int length)
441  throws IOException
442  {
443    if (offset + length > srcs.length)
444      throw new IndexOutOfBoundsException("offset + length > srcs.length");
445
446    // A gathering write is limited to 16 buffers; when writing, ensure
447    // that we have at least one buffer with something in it in the 16
448    // buffer window starting at offset.
449    while (!srcs[offset].hasRemaining() && offset < srcs.length)
450      offset++;
451
452    // There are no buffers with anything to write.
453    if (offset == srcs.length)
454      return 0;
455
456    // If we advanced `offset' so far that we don't have `length'
457    // buffers left, reset length to only the remaining buffers.
458    if (length > srcs.length - offset)
459      length = srcs.length - offset;
460
461    return writeGathering(nfd.getNativeFD(), srcs, offset, length);
462  }
463
464  private native long writeGathering(int fd, ByteBuffer[] srcs,
465      int offset, int length)
466  throws IOException;
467
468  public int send(ByteBuffer src, InetSocketAddress dst)
469  throws IOException
470  {
471    InetAddress addr = dst.getAddress();
472    if (addr == null)
473      throw new NullPointerException();
474    if (addr instanceof Inet4Address)
475      return send(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
476    else if (addr instanceof Inet6Address)
477      return send6(nfd.getNativeFD(), src, addr.getAddress(), dst.getPort());
478    else
479      throw new SocketException("unrecognized inet address type");
480  }
481
482  // Send to an IPv4 address.
483  private static native int send(int fd, ByteBuffer src, byte[] addr, int port)
484  throws IOException;
485
486  // Send to an IPv6 address.
487  private static native int send6(int fd, ByteBuffer src, byte[] addr, int port)
488  throws IOException;
489
490  public void write(int b) throws IOException
491  {
492    //write(nfd.getNativeFD(), b);
493    int result = FileSystem.writeByte(nfd.getNativeFD(), b);
494    if (result < 0) {
495      throw new IOException("Error code "+Integer.toString(result));
496    }
497  }
498
499  private static native void write(int fd, int b) throws IOException;
500
501  private native static void initIDs();
502
503  // Network (socket) specific methods.
504
505  /**
506   * Create a new socket. This method will initialize the native file
507   * descriptor state of this instance.
508   *
509   * @param stream Whether or not to create a streaming socket, or a datagram
510   *  socket.
511   * @throws IOException If creating a new socket fails, or if this
512   *  channel already has its native descriptor initialized.
513   */
514  public void initSocket(boolean stream) throws IOException
515  {
516    if (nfd.isValid())
517      throw new IOException("native FD already initialized");
518    if (stream)
519      kind = Kind.SOCK_STREAM;
520    else
521      kind = Kind.SOCK_DGRAM;
522    nfd.setNativeFD(socket(stream));
523  }
524
525  /**
526   * Create a new socket, returning the native file descriptor.
527   *
528   * @param stream Set to true for streaming sockets; false for datagrams.
529   * @return The native file descriptor.
530   * @throws IOException If creating the socket fails.
531   */
532  private static native int socket(boolean stream) throws IOException;
533
534  /**
535   * Connect the underlying socket file descriptor to the remote host.
536   *
537   * @param saddr The address to connect to.
538   * @param timeout The connect timeout to use for blocking connects.
539   * @return True if the connection succeeded; false if the file descriptor
540   *  is in non-blocking mode and the connection did not immediately
541   *  succeed.
542   * @throws SocketException If an error occurs while connecting.
543   */
544  public boolean connect(InetSocketAddress saddr, int timeout)
545  throws SocketException
546  {
547    int fd;
548
549    InetAddress addr = saddr.getAddress();
550
551    // Translates an IOException into a SocketException to conform
552    // to the throws clause.
553    try
554    {
555      fd = nfd.getNativeFD();
556    }
557    catch (IOException ioe)
558    {
559      throw new SocketException(ioe.getMessage());
560    }
561
562    if (addr instanceof Inet4Address)
563      return connect(fd, addr.getAddress(), saddr.getPort(),
564          timeout);
565    if (addr instanceof Inet6Address)
566      return connect6(fd, addr.getAddress(), saddr.getPort(),
567          timeout);
568    throw new SocketException("unsupported internet address");
569  }
570
571  private static native boolean connect(int fd, byte[] addr, int port, int timeout)
572  throws SocketException;
573
574  private static native boolean connect6(int fd, byte[] addr, int port, int timeout)
575  throws SocketException;
576
577  /**
578   * Disconnect this channel, if it is a datagram socket. Disconnecting
579   * a datagram channel will disassociate it from any address, so the
580   * socket will remain open, but can send and receive datagrams from
581   * any address.
582   *
583   * @throws IOException If disconnecting this channel fails, or if this
584   *  channel is not a datagram channel.
585   */
586  public void disconnect() throws IOException
587  {
588    if (kind != Kind.SOCK_DGRAM)
589      throw new IOException("can only disconnect datagram channels");
590    disconnect(nfd.getNativeFD());
591  }
592
593  private static native void disconnect(int fd) throws IOException;
594
595  public InetSocketAddress getLocalAddress() throws IOException
596  {
597    if (!nfd.isValid())
598      return null;
599    ByteBuffer name = ByteBuffer.allocateDirect(18);
600    int namelen = getsockname(nfd.getNativeFD(), name);
601    if (namelen == 0) // not bound
602      return null; // XXX return some wildcard?
603    if (namelen == 4)
604    {
605      byte[] addr = new byte[4];
606      name.get(addr);
607      int port = name.getShort() & 0xFFFF;
608      return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
609    }
610    if (namelen == 16)
611    {
612      byte[] addr = new byte[16];
613      name.get(addr);
614      int port = name.getShort() & 0xFFFF;
615      return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
616    }
617    throw new SocketException("invalid address length");
618  }
619
620  private static native int getsockname(int fd, ByteBuffer name)
621  throws IOException;
622
623  public InetSocketAddress getPeerAddress() throws IOException
624  {
625    if (!nfd.isValid())
626      return null;
627    ByteBuffer name = ByteBuffer.allocateDirect(18);
628    int namelen = getpeername (nfd.getNativeFD(), name);
629    if (namelen == 0) // not connected yet
630      return null;
631    if (namelen == 4) // IPv4
632    {
633      byte[] addr = new byte[4];
634      name.get(addr);
635      int port = name.getShort() & 0xFFFF;
636      return new InetSocketAddress(Inet4Address.getByAddress(addr), port);
637    }
638    else if (namelen == 16) // IPv6
639    {
640      byte[] addr = new byte[16];
641      name.get(addr);
642      int port = name.getShort() & 0xFFFF;
643      return new InetSocketAddress(Inet6Address.getByAddress(addr), port);
644    }
645    throw new SocketException("invalid address length");
646  }
647
648  /*
649   * The format here is the peer address, followed by the port number.
650   * The returned value is the length of the peer address; thus, there
651   * will be LEN + 2 valid bytes put into NAME.
652   */
653  private static native int getpeername(int fd, ByteBuffer name)
654  throws IOException;
655
656  /**
657   * Accept an incoming connection, returning a new VMChannel, or null
658   * if the channel is nonblocking and no connection is pending.
659   *
660   * @return The accepted connection, or null.
661   * @throws IOException If an IO error occurs.
662   */
663  public VMChannel accept() throws IOException
664  {
665    int new_fd = accept(nfd.getNativeFD());
666    if (new_fd == -1) // non-blocking accept had no pending connection
667      return null;
668    return new VMChannel(new_fd);
669  }
670
671  private static native int accept(int native_fd) throws IOException;
672
673  // File-specific methods.
674
675  public void openFile(String path, int mode) throws IOException
676  {
677    if (nfd.isValid() || nfd.isClosed())
678      throw new IOException("can't reinitialize this channel");
679    int fd = open(path, mode);
680    nfd.setNativeFD(fd);
681    kind = Kind.FILE;
682  }
683
684  private static native int open(String path, int mode) throws IOException;
685
686  public long position() throws IOException
687  {
688    if (kind != Kind.FILE)
689      throw new IOException("not a file");
690    return position(nfd.getNativeFD());
691  }
692
693  private static native long position(int fd) throws IOException;
694
695  public void seek(long pos) throws IOException
696  {
697    if (kind != Kind.FILE)
698      throw new IOException("not a file");
699    seek(nfd.getNativeFD(), pos);
700  }
701
702  private static native void seek(int fd, long pos) throws IOException;
703
704  public void truncate(long length) throws IOException
705  {
706    if (kind != Kind.FILE)
707      throw new IOException("not a file");
708    truncate(nfd.getNativeFD(), length);
709  }
710
711  private static native void truncate(int fd, long len) throws IOException;
712
713  public boolean lock(long pos, long len, boolean shared, boolean wait)
714  throws IOException
715  {
716    if (kind != Kind.FILE)
717      throw new IOException("not a file");
718    return lock(nfd.getNativeFD(), pos, len, shared, wait);
719  }
720
721  private static native boolean lock(int fd, long pos, long len,
722      boolean shared, boolean wait)
723  throws IOException;
724
725  public void unlock(long pos, long len) throws IOException
726  {
727    if (kind != Kind.FILE)
728      throw new IOException("not a file");
729    unlock(nfd.getNativeFD(), pos, len);
730  }
731
732  private static native void unlock(int fd, long pos, long len) throws IOException;
733
734  public long size() throws IOException
735  {
736    if (kind != Kind.FILE)
737      throw new IOException("not a file");
738    return size(nfd.getNativeFD());
739  }
740
741  private static native long size(int fd) throws IOException;
742
743  public MappedByteBuffer map(char mode, long position, int size)
744  throws IOException
745  {
746    if (kind != Kind.FILE)
747      throw new IOException("not a file");
748    return map(nfd.getNativeFD(), mode, position, size);
749  }
750
751  private static native MappedByteBuffer map(int fd, char mode,
752      long position, int size)
753  throws IOException;
754
755  public boolean flush(boolean metadata) throws IOException
756  {
757    if (kind != Kind.FILE)
758      throw new IOException("not a file");
759    return flush(nfd.getNativeFD(), metadata);
760  }
761
762  private static native boolean flush(int fd, boolean metadata) throws IOException;
763
764  // Close.
765
766  /**
767   * Close this socket. The socket is also automatically closed when this
768   * object is finalized.
769   *
770   * @throws IOException If closing the socket fails, or if this object has
771   *  no open socket.
772   */
773  public void close() throws IOException
774  {
775    nfd.close();
776  }
777
778  static native void close(int native_fd) throws IOException;
779
780  /**
781   * <p>Provides a simple mean for the JNI code to find out whether the
782   * current thread was interrupted by a call to Thread.interrupt().</p>
783   *
784   * @return true if the current thread was interrupted, false otherwise
785   */
786  static boolean isThreadInterrupted()
787  {
788    return Thread.currentThread().isInterrupted();
789  }
790
791  // Inner classes.
792
793  /**
794   * A wrapper for a native file descriptor integer. This tracks the state
795   * of an open file descriptor, and ensures that
796   *
797   * This class need not be fully supported by virtual machines; if a
798   * virtual machine does not use integer file descriptors, or does and
799   * wishes to hide that, then the methods of this class may be stubbed out.
800   *
801   * System-specific classes that depend on access to native file descriptor
802   * integers SHOULD declare this fact.
803   */
804  public final class State
805  {
806    private int native_fd;
807    private boolean valid;
808    private boolean closed;
809
810    State()
811    {
812      native_fd = -1;
813      valid = false;
814      closed = false;
815    }
816
817    public boolean isValid()
818    {
819      return valid;
820    }
821
822    public boolean isClosed()
823    {
824      return closed;
825    }
826
827    public int getNativeFD() throws IOException
828    {
829      if (!valid)
830        throw new IOException("invalid file descriptor");
831      return native_fd;
832    }
833
834    void setNativeFD(final int native_fd) throws IOException
835    {
836      if (valid)
837        throw new IOException("file descriptor already initialized");
838      this.native_fd = native_fd;
839      valid = true;
840    }
841
842    public void close() throws IOException
843    {
844      if (!valid)
845        throw new IOException("invalid file descriptor");
846      try
847      {
848        VMChannel.close(native_fd);
849      }
850      finally
851      {
852        valid = false;
853        closed = true;
854      }
855    }
856
857    @Override
858    public String toString()
859    {
860      if (closed)
861        return "<<closed>>";
862      if (!valid)
863        return "<<invalid>>";
864      return String.valueOf(native_fd);
865    }
866
867    @Override
868    protected void finalize() throws Throwable
869    {
870      try
871      {
872        if (valid)
873          close();
874      }
875      finally
876      {
877        super.finalize();
878      }
879    }
880  }
881
882  /**
883   * An enumeration of possible kinds of channel.
884   */
885  static class Kind // XXX enum
886  {
887    /** A streaming (TCP) socket. */
888    static final Kind SOCK_STREAM = new Kind();
889
890    /** A datagram (UDP) socket. */
891    static final Kind SOCK_DGRAM = new Kind();
892
893    /** A file. */
894    static final Kind FILE = new Kind();
895
896    /** Something else; not a socket or file. */
897    static final Kind OTHER = new Kind();
898
899    private Kind() { }
900  }
901}