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  *   http://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     /**
66      * Close the file descriptor.
67      */

68     public void close() throws IOException {
69         for (;;) {
70             int state = this.state;
71             if (isClosed(state)) {
72                 return;
73             }
74             // Once a close operation happens, the channel is considered shutdown.
75             if (casState(state, state | STATE_ALL_MASK)) {
76                 break;
77             }
78         }
79         int res = close(fd);
80         if (res < 0) {
81             throw newIOException("close", res);
82         }
83     }
84
85     /**
86      * Returns {@code trueif the file descriptor is open.
87      */

88     public boolean isOpen() {
89         return !isClosed(state);
90     }
91
92     public final int write(ByteBuffer buf, int pos, int limit) throws IOException {
93         int res = write(fd, buf, pos, limit);
94         if (res >= 0) {
95             return res;
96         }
97         return ioResult("write", res);
98     }
99
100     public final int writeAddress(long address, int pos, int limit) throws IOException {
101         int res = writeAddress(fd, address, pos, limit);
102         if (res >= 0) {
103             return res;
104         }
105         return ioResult("writeAddress", res);
106     }
107
108     public final long writev(ByteBuffer[] buffers, int offset, int length, long maxBytesToWrite) throws IOException {
109         long res = writev(fd, buffers, offset, min(IOV_MAX, length), maxBytesToWrite);
110         if (res >= 0) {
111             return res;
112         }
113         return ioResult("writev", (int) res);
114     }
115
116     public final long writevAddresses(long memoryAddress, int length) throws IOException {
117         long res = writevAddresses(fd, memoryAddress, length);
118         if (res >= 0) {
119             return res;
120         }
121         return ioResult("writevAddresses", (int) res);
122     }
123
124     public final int read(ByteBuffer buf, int pos, int limit) throws IOException {
125         int res = read(fd, buf, pos, limit);
126         if (res > 0) {
127             return res;
128         }
129         if (res == 0) {
130             return -1;
131         }
132         return ioResult("read", res);
133     }
134
135     public final int readAddress(long address, int pos, int limit) throws IOException {
136         int res = readAddress(fd, address, pos, limit);
137         if (res > 0) {
138             return res;
139         }
140         if (res == 0) {
141             return -1;
142         }
143         return ioResult("readAddress", res);
144     }
145
146     @Override
147     public String toString() {
148         return "FileDescriptor{" +
149                 "fd=" + fd +
150                 '}';
151     }
152
153     @Override
154     public boolean equals(Object o) {
155         if (this == o) {
156             return true;
157         }
158         if (!(o instanceof FileDescriptor)) {
159             return false;
160         }
161
162         return fd == ((FileDescriptor) o).fd;
163     }
164
165     @Override
166     public int hashCode() {
167         return fd;
168     }
169
170     /**
171      * Open a new {@link FileDescriptor} for the given path.
172      */

173     public static FileDescriptor from(String path) throws IOException {
174         int res = open(checkNotNull(path, "path"));
175         if (res < 0) {
176             throw newIOException("open", res);
177         }
178         return new FileDescriptor(res);
179     }
180
181     /**
182      * Open a new {@link FileDescriptor} for the given {@link File}.
183      */

184     public static FileDescriptor from(File file) throws IOException {
185         return from(checkNotNull(file, "file").getPath());
186     }
187
188     /**
189      * @return [0] = read end, [1] = write end
190      */

191     public static FileDescriptor[] pipe() throws IOException {
192         long res = newPipe();
193         if (res < 0) {
194             throw newIOException("newPipe", (int) res);
195         }
196         return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)};
197     }
198
199     final boolean casState(int expected, int update) {
200         return stateUpdater.compareAndSet(this, expected, update);
201     }
202
203     static boolean isClosed(int state) {
204         return (state & STATE_CLOSED_MASK) != 0;
205     }
206
207     static boolean isInputShutdown(int state) {
208         return (state & STATE_INPUT_SHUTDOWN_MASK) != 0;
209     }
210
211     static boolean isOutputShutdown(int state) {
212         return (state & STATE_OUTPUT_SHUTDOWN_MASK) != 0;
213     }
214
215     static int inputShutdown(int state) {
216         return state | STATE_INPUT_SHUTDOWN_MASK;
217     }
218
219     static int outputShutdown(int state) {
220         return state | STATE_OUTPUT_SHUTDOWN_MASK;
221     }
222
223     private static native int open(String path);
224     private static native int close(int fd);
225
226     private static native int write(int fd, ByteBuffer buf, int pos, int limit);
227     private static native int writeAddress(int fd, long address, int pos, int limit);
228     private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length, long maxBytesToWrite);
229     private static native long writevAddresses(int fd, long memoryAddress, int length);
230
231     private static native int read(int fd, ByteBuffer buf, int pos, int limit);
232     private static native int readAddress(int fd, long address, int pos, int limit);
233
234     private static native long newPipe();
235 }
236