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