1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.uring;
17
18 import io.netty.channel.Channel;
19 import io.netty.channel.ChannelFuture;
20 import io.netty.channel.ChannelFutureListener;
21 import io.netty.channel.ChannelOutboundBuffer;
22 import io.netty.channel.ChannelPipeline;
23 import io.netty.channel.ChannelPromise;
24 import io.netty.channel.IoRegistration;
25 import io.netty.channel.unix.DomainSocketAddress;
26 import io.netty.channel.unix.DomainSocketChannel;
27 import io.netty.channel.unix.DomainSocketChannelConfig;
28 import io.netty.channel.unix.DomainSocketReadMode;
29 import io.netty.channel.unix.Errors;
30 import io.netty.channel.unix.FileDescriptor;
31 import io.netty.channel.unix.PeerCredentials;
32 import io.netty.util.internal.logging.InternalLogger;
33 import io.netty.util.internal.logging.InternalLoggerFactory;
34
35 import java.io.IOException;
36 import java.net.SocketAddress;
37
38
39
40
41 public final class IoUringDomainSocketChannel extends AbstractIoUringStreamChannel implements DomainSocketChannel {
42 private static final InternalLogger logger = InternalLoggerFactory.getInstance(IoUringDomainSocketChannel.class);
43
44 private final IoUringDomainSocketChannelConfig config;
45
46 private volatile DomainSocketAddress local;
47 private volatile DomainSocketAddress remote;
48
49 public IoUringDomainSocketChannel() {
50 super(null, LinuxSocket.newSocketDomain(), false);
51 config = new IoUringDomainSocketChannelConfig(this);
52 }
53
54 IoUringDomainSocketChannel(Channel parent, FileDescriptor fd) {
55 this(parent, new LinuxSocket(fd.intValue()));
56 }
57
58 IoUringDomainSocketChannel(Channel parent, LinuxSocket fd) {
59 super(parent, fd, true);
60 local = fd.localDomainSocketAddress();
61 remote = fd.remoteDomainSocketAddress();
62 config = new IoUringDomainSocketChannelConfig(this);
63 }
64
65 @Override
66 public DomainSocketChannelConfig config() {
67 return config;
68 }
69
70 @Override
71 public DomainSocketAddress localAddress() {
72 return local;
73 }
74
75 @Override
76 public DomainSocketAddress remoteAddress() {
77 return remote;
78 }
79
80
81
82
83
84 public PeerCredentials peerCredentials() throws IOException {
85 return socket.getPeerCredentials();
86 }
87
88 @Override
89 protected Object filterOutboundMessage(Object msg) {
90 if (msg instanceof FileDescriptor) {
91 return msg;
92 }
93 return super.filterOutboundMessage(msg);
94 }
95
96 @Override
97 protected AbstractUringUnsafe newUnsafe() {
98 return new IoUringDomainSocketUnsafe();
99 }
100
101 @Override
102 protected boolean allowMultiShotPollIn() {
103
104
105 return false;
106 }
107
108 @Override
109 protected boolean socketIsEmpty(int flags) {
110 return IoUring.isUnixDomainSocketInqSupported() && super.socketIsEmpty(flags);
111 }
112
113 @Override
114 protected boolean shouldCompleteReadLoop(int flags, boolean multishot) {
115 if (IoUring.isUnixDomainSocketInqSupported()) {
116 return socketIsEmpty(flags);
117 }
118
119
120
121
122 return multishot;
123 }
124
125 private final class IoUringDomainSocketUnsafe extends IoUringStreamUnsafe {
126
127 private MsgHdrMemory writeMsgHdrMemory;
128 private MsgHdrMemory readMsgHdrMemory;
129 private FileDescriptor pendingSendFd;
130
131 @Override
132 protected int scheduleWriteSingle(Object msg) {
133 if (msg instanceof FileDescriptor) {
134
135
136 if (writeMsgHdrMemory == null) {
137 writeMsgHdrMemory = new MsgHdrMemory();
138 }
139 int fd = Native.duplicateFd(((FileDescriptor) msg).intValue());
140 if (fd < 0) {
141 handleWriteError(Errors.newIOException("fcntl(F_DUPFD_CLOEXEC)", fd));
142 return 0;
143 }
144
145 pendingSendFd = new FileDescriptor(fd);
146 try {
147 IoUringIoOps ioUringIoOps = prepSendFdIoOps(pendingSendFd, writeMsgHdrMemory);
148 writeId = submitWrite(ioUringIoOps);
149 writeOpCode = Native.IORING_OP_SENDMSG;
150 if (writeId == 0) {
151 MsgHdrMemory memory = writeMsgHdrMemory;
152 writeMsgHdrMemory = null;
153 memory.release();
154 return 0;
155 }
156 return 1;
157 } finally {
158 if (writeId == 0) {
159 closePendingSendFd();
160 }
161 }
162 }
163 return super.scheduleWriteSingle(msg);
164 }
165
166 private void closePendingSendFd() {
167 FileDescriptor fd = pendingSendFd;
168 pendingSendFd = null;
169 try {
170 fd.close();
171 } catch (IOException e) {
172 logger.debug("Error while closing a duplicated file descriptor", e);
173 }
174 }
175
176 @Override
177 boolean writeComplete0(byte op, int res, int flags, long data, int outstanding) {
178 if (op == Native.IORING_OP_SENDMSG) {
179 writeId = 0;
180 writeOpCode = 0;
181 closePendingSendFd();
182 ChannelOutboundBuffer channelOutboundBuffer = unsafe().outboundBuffer();
183
184 if (channelOutboundBuffer == null || res == Native.ERRNO_ECANCELED_NEGATIVE) {
185 return true;
186 }
187 try {
188 int nativeCallResult = res >= 0 ? res : Errors.ioResult("io_uring sendmsg", res);
189 if (nativeCallResult >= 0) {
190 channelOutboundBuffer.remove();
191 }
192 } catch (Throwable throwable) {
193 handleWriteError(throwable);
194 }
195 return true;
196 }
197 return super.writeComplete0(op, res, flags, data, outstanding);
198 }
199
200 private IoUringIoOps prepSendFdIoOps(FileDescriptor fileDescriptor, MsgHdrMemory msgHdrMemory) {
201 msgHdrMemory.setScmRightsFd(fileDescriptor.intValue());
202 return IoUringIoOps.newSendmsg(
203 fd().intValue(), (byte) 0, 0, msgHdrMemory.address(), msgHdrMemory.idx());
204 }
205
206 @Override
207 protected int scheduleRead0(boolean first, boolean socketIsEmpty) {
208 DomainSocketReadMode readMode = config.getReadMode();
209 switch (readMode) {
210 case FILE_DESCRIPTORS:
211 return scheduleRecvReadFd();
212 case BYTES:
213 return super.scheduleRead0(first, socketIsEmpty);
214 default:
215 throw new Error("Unexpected read mode: " + readMode);
216 }
217 }
218
219 private int scheduleRecvReadFd() {
220
221
222 if (readMsgHdrMemory == null) {
223 readMsgHdrMemory = new MsgHdrMemory();
224 }
225 readMsgHdrMemory.prepRecvReadFd();
226 IoRegistration registration = registration();
227 IoUringIoOps ioUringIoOps = IoUringIoOps.newRecvmsg(
228 fd().intValue(), (byte) 0, 0, readMsgHdrMemory.address(), readMsgHdrMemory.idx());
229 readId = registration.submit(ioUringIoOps);
230 readOpCode = Native.IORING_OP_RECVMSG;
231 if (readId == 0) {
232 MsgHdrMemory memory = readMsgHdrMemory;
233 readMsgHdrMemory = null;
234 memory.release();
235 return 0;
236 }
237 return 1;
238 }
239
240 @Override
241 protected void readComplete0(byte op, int res, int flags, short data, int outstanding) {
242 if (op == Native.IORING_OP_RECVMSG) {
243 readId = 0;
244 if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
245 return;
246 }
247 final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
248 final ChannelPipeline pipeline = pipeline();
249 try {
250 int nativeCallResult = res >= 0 ? res : Errors.ioResult("io_uring recvmsg", res);
251 if (nativeCallResult == 0) {
252
253 shutdownInput(true);
254 return;
255 }
256 int nativeFd = readMsgHdrMemory.getScmRightsFd();
257 allocHandle.lastBytesRead(nativeFd);
258 allocHandle.incMessagesRead(1);
259 pipeline.fireChannelRead(new FileDescriptor(nativeFd));
260 } catch (Throwable throwable) {
261 handleReadException(pipeline, null, throwable, false, allocHandle);
262 } finally {
263 allocHandle.readComplete();
264 pipeline.fireChannelReadComplete();
265 }
266 return;
267 }
268 super.readComplete0(op, res, flags, data, outstanding);
269 }
270
271 @Override
272 public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
273
274 ChannelPromise channelPromise = newPromise().addListener(new ChannelFutureListener() {
275 @Override
276 public void operationComplete(ChannelFuture future) throws Exception {
277 if (future.isSuccess()) {
278 local = localAddress != null
279 ? (DomainSocketAddress) localAddress
280 : socket.localDomainSocketAddress();
281 remote = (DomainSocketAddress) remoteAddress;
282 promise.setSuccess();
283 } else {
284 promise.setFailure(future.cause());
285 }
286 }
287 });
288 super.connect(remoteAddress, localAddress, channelPromise);
289 }
290
291 @Override
292 public void unregistered() {
293 super.unregistered();
294 if (readMsgHdrMemory != null) {
295 readMsgHdrMemory.release();
296 readMsgHdrMemory = null;
297 }
298 if (writeMsgHdrMemory != null) {
299 writeMsgHdrMemory.release();
300 writeMsgHdrMemory = null;
301 }
302 }
303 }
304
305 @Override
306 boolean isPollInFirst() {
307 DomainSocketReadMode readMode = config.getReadMode();
308 switch (readMode) {
309 case BYTES:
310 return super.isPollInFirst();
311 case FILE_DESCRIPTORS:
312 return false;
313 default:
314 throw new Error("Unexpected read mode: " + readMode);
315 }
316 }
317 }