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.
177      */
178     public static FileDescriptor from(String path) throws IOException {
179         int res = open(checkNotNull(path, "path"));
180         if (res < 0) {
181             throw newIOException("open", res);
182         }
183         return new FileDescriptor(res);
184     }
185 
186     /**
187      * Open a new {@link FileDescriptor} for the given {@link File}.
188      */
189     public static FileDescriptor from(File file) throws IOException {
190         return from(checkNotNull(file, "file").getPath());
191     }
192 
193     /**
194      * @return [0] = read end, [1] = write end
195      */
196     public static FileDescriptor[] pipe() throws IOException {
197         long res = newPipe();
198         if (res < 0) {
199             throw newIOException("newPipe", (int) res);
200         }
201         return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)};
202     }
203 
204     final boolean casState(int expected, int update) {
205         return stateUpdater.compareAndSet(this, expected, update);
206     }
207 
208     static boolean isClosed(int state) {
209         return (state & STATE_CLOSED_MASK) != 0;
210     }
211 
212     static boolean isInputShutdown(int state) {
213         return (state & STATE_INPUT_SHUTDOWN_MASK) != 0;
214     }
215 
216     static boolean isOutputShutdown(int state) {
217         return (state & STATE_OUTPUT_SHUTDOWN_MASK) != 0;
218     }
219 
220     static int inputShutdown(int state) {
221         return state | STATE_INPUT_SHUTDOWN_MASK;
222     }
223 
224     static int outputShutdown(int state) {
225         return state | STATE_OUTPUT_SHUTDOWN_MASK;
226     }
227 
228     private static native int open(String path);
229     private static native int close(int fd);
230 
231     private static native int write(int fd, ByteBuffer buf, int pos, int limit);
232     private static native int writeAddress(int fd, long address, int pos, int limit);
233     private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length, long maxBytesToWrite);
234     private static native long writevAddresses(int fd, long memoryAddress, int length);
235 
236     private static native int read(int fd, ByteBuffer buf, int pos, int limit);
237     private static native int readAddress(int fd, long address, int pos, int limit);
238 
239     private static native long newPipe();
240 }