View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.unix;
17  
18  
19  import java.io.File;
20  import java.io.IOException;
21  import java.nio.ByteBuffer;
22  import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
23  
24  import static io.netty.channel.unix.Errors.ioResult;
25  import static io.netty.channel.unix.Errors.newIOException;
26  import static io.netty.channel.unix.Limits.IOV_MAX;
27  import static io.netty.util.internal.ObjectUtil.checkNotNull;
28  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
29  import static java.lang.Math.min;
30  
31  /**
32   * Native {@link FileDescriptor} implementation which allows to wrap an {@code int} and provide a
33   * {@link FileDescriptor} for it.
34   */
35  public class FileDescriptor {
36  
37      private static final AtomicIntegerFieldUpdater<FileDescriptor> stateUpdater =
38              AtomicIntegerFieldUpdater.newUpdater(FileDescriptor.class, "state");
39  
40      private static final int STATE_CLOSED_MASK = 1;
41      private static final int STATE_INPUT_SHUTDOWN_MASK = 1 << 1;
42      private static final int STATE_OUTPUT_SHUTDOWN_MASK = 1 << 2;
43      private static final int STATE_ALL_MASK = STATE_CLOSED_MASK |
44                                                STATE_INPUT_SHUTDOWN_MASK |
45                                                STATE_OUTPUT_SHUTDOWN_MASK;
46  
47      /**
48       * Bit map = [Output Shutdown | Input Shutdown | Closed]
49       */
50      volatile int state;
51      final int fd;
52  
53      public FileDescriptor(int fd) {
54          checkPositiveOrZero(fd, "fd");
55          this.fd = fd;
56      }
57  
58      /**
59       * Return the int value of the filedescriptor.
60       */
61      public final int intValue() {
62          return fd;
63      }
64  
65      protected boolean markClosed() {
66          for (;;) {
67              int state = this.state;
68              if (isClosed(state)) {
69                  return false;
70              }
71              // Once a close operation happens, the channel is considered shutdown.
72              if (casState(state, state | STATE_ALL_MASK)) {
73                  return true;
74              }
75          }
76      }
77  
78      /**
79       * Close the file descriptor.
80       */
81      public void close() throws IOException {
82          if (markClosed()) {
83              int res = close(fd);
84              if (res < 0) {
85                  throw newIOException("close", res);
86              }
87          }
88      }
89  
90      /**
91       * Returns {@code true} if the file descriptor is open.
92       */
93      public boolean isOpen() {
94          return !isClosed(state);
95      }
96  
97      public final int write(ByteBuffer buf, int pos, int limit) throws IOException {
98          int res = write(fd, buf, pos, limit);
99          if (res >= 0) {
100             return res;
101         }
102         return ioResult("write", res);
103     }
104 
105     public final int writeAddress(long address, int pos, int limit) throws IOException {
106         int res = writeAddress(fd, address, pos, limit);
107         if (res >= 0) {
108             return res;
109         }
110         return ioResult("writeAddress", res);
111     }
112 
113     public final long writev(ByteBuffer[] buffers, int offset, int length, long maxBytesToWrite) throws IOException {
114         long res = writev(fd, buffers, offset, min(IOV_MAX, length), maxBytesToWrite);
115         if (res >= 0) {
116             return res;
117         }
118         return ioResult("writev", (int) res);
119     }
120 
121     public final long writevAddresses(long memoryAddress, int length) throws IOException {
122         long res = writevAddresses(fd, memoryAddress, length);
123         if (res >= 0) {
124             return res;
125         }
126         return ioResult("writevAddresses", (int) res);
127     }
128 
129     public final int read(ByteBuffer buf, int pos, int limit) throws IOException {
130         int res = read(fd, buf, pos, limit);
131         if (res > 0) {
132             return res;
133         }
134         if (res == 0) {
135             return -1;
136         }
137         return ioResult("read", res);
138     }
139 
140     public final int readAddress(long address, int pos, int limit) throws IOException {
141         int res = readAddress(fd, address, pos, limit);
142         if (res > 0) {
143             return res;
144         }
145         if (res == 0) {
146             return -1;
147         }
148         return ioResult("readAddress", res);
149     }
150 
151     @Override
152     public String toString() {
153         return "FileDescriptor{" +
154                 "fd=" + fd +
155                 '}';
156     }
157 
158     @Override
159     public boolean equals(Object o) {
160         if (this == o) {
161             return true;
162         }
163         if (!(o instanceof FileDescriptor)) {
164             return false;
165         }
166 
167         return fd == ((FileDescriptor) o).fd;
168     }
169 
170     @Override
171     public int hashCode() {
172         return fd;
173     }
174 
175     /**
176      * Open a new {@link FileDescriptor} for the given path, write-only.
177      * <p>
178      * <strong>WARNING:</strong> if {@code path} already exists it is <strong>truncated to zero length</strong>; if it
179      * does not exist it is <strong>created</strong> with mode {@code 0666 &amp; ~umask}. Do not pass untrusted or
180      * externally-derived paths to this method.
181      */
182     public static FileDescriptor from(String path) throws IOException {
183         int res = open(checkNotNull(path, "path"));
184         if (res < 0) {
185             throw newIOException("open", res);
186         }
187         return new FileDescriptor(res);
188     }
189 
190     /**
191      * Open a new {@link FileDescriptor} for the given {@link File}, write-only.
192      * <p>
193      * <strong>WARNING:</strong> see {@link #from(String)}: an existing file is truncated and a missing one is created.
194      */
195     public static FileDescriptor from(File file) throws IOException {
196         return from(checkNotNull(file, "file").getPath());
197     }
198 
199     /**
200      * @return [0] = read end, [1] = write end
201      */
202     public static FileDescriptor[] pipe() throws IOException {
203         long res = newPipe();
204         if (res < 0) {
205             throw newIOException("newPipe", (int) res);
206         }
207         return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)};
208     }
209 
210     final boolean casState(int expected, int update) {
211         return stateUpdater.compareAndSet(this, expected, update);
212     }
213 
214     static boolean isClosed(int state) {
215         return (state & STATE_CLOSED_MASK) != 0;
216     }
217 
218     static boolean isInputShutdown(int state) {
219         return (state & STATE_INPUT_SHUTDOWN_MASK) != 0;
220     }
221 
222     static boolean isOutputShutdown(int state) {
223         return (state & STATE_OUTPUT_SHUTDOWN_MASK) != 0;
224     }
225 
226     static int inputShutdown(int state) {
227         return state | STATE_INPUT_SHUTDOWN_MASK;
228     }
229 
230     static int outputShutdown(int state) {
231         return state | STATE_OUTPUT_SHUTDOWN_MASK;
232     }
233 
234     private static native int open(String path);
235     private static native int close(int fd);
236 
237     private static native int write(int fd, ByteBuffer buf, int pos, int limit);
238     private static native int writeAddress(int fd, long address, int pos, int limit);
239     private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length, long maxBytesToWrite);
240     private static native long writevAddresses(int fd, long memoryAddress, int length);
241 
242     private static native int read(int fd, ByteBuffer buf, int pos, int limit);
243     private static native int readAddress(int fd, long address, int pos, int limit);
244 
245     private static native long newPipe();
246 }