-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathChannelFD.java
More file actions
169 lines (145 loc) · 5.7 KB
/
ChannelFD.java
File metadata and controls
169 lines (145 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package org.python.util;
import jnr.enxio.channels.NativeDeviceChannel;
import jnr.enxio.channels.NativeSelectableChannel;
import jnr.posix.FileStat;
import jnr.posix.POSIX;
import jnr.posix.util.Platform;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by headius on 5/24/14.
*/
public class ChannelFD implements Closeable {
public ChannelFD(Channel fd, POSIX posix, FilenoUtil filenoUtil) {
assert fd != null;
this.ch = fd;
this.posix = posix;
this.filenoUtil = filenoUtil;
initFileno();
initChannelTypes();
refs = new AtomicInteger(1);
filenoUtil.registerWrapper(realFileno, this);
filenoUtil.registerWrapper(fakeFileno, this);
}
private void initFileno() {
realFileno = FilenoUtil.filenoFrom(ch);
if (realFileno == -1) {
fakeFileno = filenoUtil.getNewFileno();
} else {
fakeFileno = -1;
}
}
public ChannelFD dup() {
if (realFileno != -1 && !Platform.IS_WINDOWS) {
// real file descriptors, so we can dup directly
// TODO: investigate how badly this might damage JVM streams (prediction: not badly)
return new ChannelFD(new NativeDeviceChannel(posix.dup(realFileno)), posix, filenoUtil);
}
// TODO: not sure how well this combines native and non-native streams
// simulate dup by copying our channel into a new ChannelFD and incrementing ref count
Channel ch = this.ch;
ChannelFD fd = new ChannelFD(ch, posix, filenoUtil);
fd.refs = refs;
fd.refs.incrementAndGet();
return fd;
}
public int dup2From(POSIX posix, ChannelFD dup2Source) {
if (dup2Source.realFileno != -1 && realFileno != -1 && chFile == null) {
// real file descriptors, so we can dup2 directly
// ...but FileChannel tracks mode on its own, so we can't dup2 into it
// TODO: investigate how badly this might damage JVM streams (prediction: not badly)
return posix.dup2(dup2Source.realFileno, realFileno);
}
// TODO: not sure how well this combines native and non-native streams
// simulate dup2 by forcing filedes's channel into filedes2
this.ch = dup2Source.ch;
initFileno();
initChannelTypes();
this.refs = dup2Source.refs;
this.refs.incrementAndGet();
this.currentLock = dup2Source.currentLock;
return 0;
}
public void close() throws IOException {
// tidy up
finish(true);
}
public int bestFileno() {
return realFileno == -1 ? fakeFileno : realFileno;
}
private void finish(boolean close) throws IOException {
synchronized (refs) {
// if refcount is at or below zero, we're no longer valid
if (refs.get() <= 0) {
throw new ClosedChannelException();
}
// if channel is already closed, we're no longer valid
if (!ch.isOpen()) {
throw new ClosedChannelException();
}
// otherwise decrement and possibly close as normal
if (close) {
int count = refs.decrementAndGet();
if (count <= 0) {
// if we're the last referrer, close the channel
try {
ch.close();
} finally {
filenoUtil.unregisterWrapper(realFileno);
filenoUtil.unregisterWrapper(fakeFileno);
}
}
}
}
}
private void initChannelTypes() {
assert realFileno != -1 || fakeFileno != -1 : "initialize filenos before initChannelTypes";
if (ch instanceof ReadableByteChannel) chRead = (ReadableByteChannel)ch;
else chRead = null;
if (ch instanceof WritableByteChannel) chWrite = (WritableByteChannel)ch;
else chWrite = null;
if (ch instanceof SeekableByteChannel) chSeek = (SeekableByteChannel)ch;
else chSeek = null;
if (ch instanceof SelectableChannel) chSelect = (SelectableChannel)ch;
else chSelect = null;
if (ch instanceof FileChannel) chFile = (FileChannel)ch;
else chFile = null;
if (ch instanceof SocketChannel) chSock = (SocketChannel)ch;
else chSock = null;
if (ch instanceof NativeSelectableChannel) chNative = (NativeSelectableChannel)ch;
else chNative = null;
if (chNative != null) {
// we have an ENXIO channel, but need to know if it's a regular file to skip selection
FileStat stat = posix.fstat(chNative.getFD());
if (stat.isFile()) {
chSelect = null;
isNativeFile = true;
}
}
}
public Channel ch;
public ReadableByteChannel chRead;
public WritableByteChannel chWrite;
public SeekableByteChannel chSeek;
public SelectableChannel chSelect;
public FileChannel chFile;
public SocketChannel chSock;
public NativeSelectableChannel chNative;
public int realFileno;
public int fakeFileno;
private AtomicInteger refs;
public FileLock currentLock;
private final POSIX posix;
public boolean isNativeFile = false;
private final FilenoUtil filenoUtil;
}