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