1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix;
19
20 import java.io.Closeable;
21 import java.io.FileDescriptor;
22 import java.io.IOException;
23 import java.io.InterruptedIOException;
24 import java.net.SocketTimeoutException;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.locks.LockSupport;
31
32 import org.eclipse.jdt.annotation.Nullable;
33 import org.newsclub.net.unix.AFSelector.PollFd;
34
35
36
37
38
39
40
41 final class VirtualThreadPollerNaive implements VirtualThreadPoller {
42 private static final int POLL_INTERVAL_MILLIS = 1_000;
43
44
45 private static final Map<FileDescriptor, PollJob> POLL_JOBS = new ConcurrentHashMap<>();
46
47 private static final InterruptedIOException POLL_INTERRUPTED_SENTINEL =
48 new InterruptedIOException();
49
50 private static final String PROP_POLLJOB_EXECUTOR =
51 "org.newsclub.net.unix.VirtualThreadPoller.use-common-pool";
52
53
54
55 private static final java.util.concurrent.ExecutorService POLLJOB_EXECUTOR =
56 Boolean.parseBoolean(System.getProperty(PROP_POLLJOB_EXECUTOR, "false")) ?
57 ThreadUtil.commonPool() : ThreadUtil.newWorkStealingPool();
58
59 private static final class PollJob {
60 private final List<Thread> waitingThreads = new LinkedList<>();
61 private final FileDescriptor fd;
62 private final int mode;
63 private final long now;
64 private final AFSupplier<Integer> timeout;
65
66 PollJob(FileDescriptor fd, int mode, long now, AFSupplier<Integer> timeout) {
67 this.fd = fd;
68 this.mode = mode;
69 this.now = now;
70 this.timeout = timeout;
71 }
72
73 @SuppressWarnings("PMD.CognitiveComplexity")
74 AFFuture<@Nullable IOException> trigger(Thread waitingThread) {
75 synchronized (fd) {
76 waitingThreads.add(waitingThread);
77 }
78 return AFFuture.supplyAsync(() -> {
79 try {
80 Thread thread = Thread.currentThread();
81 PollFd pfd = new PollFd(new FileDescriptor[] {fd}, new int[] {mode});
82 do {
83 if (thread.isInterrupted() || !fd.valid()) {
84 return POLL_INTERRUPTED_SENTINEL;
85 }
86 try {
87 NativeUnixSocket.poll(pfd, POLL_INTERVAL_MILLIS);
88 } catch (IOException e) {
89 return e;
90 }
91 if (thread.isInterrupted() || !fd.valid()) {
92 return POLL_INTERRUPTED_SENTINEL;
93 }
94 if (pfd.rops[0] != 0) {
95 break;
96 }
97
98 int timeoutMillis = timeout.get();
99 if (timeoutMillis > 0) {
100 if ((System.currentTimeMillis() - now) >= timeoutMillis) {
101
102 break;
103 }
104 }
105 } while (true);
106 } finally {
107 Thread threadToWake = null;
108 try {
109 synchronized (fd) {
110 threadToWake = waitingThreads.remove(0);
111 if (waitingThreads.isEmpty()) {
112 POLL_JOBS.remove(fd);
113 }
114 }
115 } finally {
116 if (threadToWake != null) {
117 LockSupport.unpark(threadToWake);
118 }
119 }
120 }
121
122 return null;
123 }, POLLJOB_EXECUTOR)::get;
124 }
125 }
126
127 @Override
128 public void parkThreadUntilReady(FileDescriptor fd, int mode, long now,
129 AFSupplier<Integer> timeout, Closeable closeOnInterrupt) throws IOException {
130 Thread virtualThread = Thread.currentThread();
131
132 PollJob job = Java7Util.computeIfAbsent(POLL_JOBS, fd, (k) -> new PollJob(fd, mode, now,
133 timeout));
134 AFFuture<@Nullable IOException> future = job.trigger(virtualThread);
135
136 LockSupport.park();
137 if (virtualThread.isInterrupted()) {
138 throw SocketClosedByInterruptException.newInstanceAndClose(closeOnInterrupt);
139 }
140
141 try {
142 IOException ex = future.get();
143 if (ex != null) {
144 if (ex == POLL_INTERRUPTED_SENTINEL) {
145 throw SocketClosedByInterruptException.newInstanceAndClose(closeOnInterrupt);
146 }
147 throw ex;
148 }
149 } catch (InterruptedException | ExecutionException e) {
150 throw SocketClosedByInterruptException.newInstanceAndClose(closeOnInterrupt);
151 }
152
153 int timeoutMillis = timeout.get();
154 if (timeoutMillis > 0) {
155 if ((System.currentTimeMillis() - now) >= timeoutMillis) {
156 throw new SocketTimeoutException();
157 }
158 }
159 }
160 }