1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.newsclub.net.unix.memory;
19
20 import java.io.FileDescriptor;
21 import java.io.IOException;
22 import java.lang.foreign.Arena;
23 import java.lang.foreign.MemorySegment;
24 import java.util.ArrayList;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Objects;
30 import java.util.WeakHashMap;
31
32 import org.newsclub.net.unix.CleanableState;
33 import org.newsclub.net.unix.MemoryImplUtilInternal;
34
35 import com.kohlschutter.annotations.compiletime.SuppressFBWarnings;
36
37 class SharedMemoryCleaner extends CleanableState {
38 private final Map<MemorySegment, Integer> segments = new LinkedHashMap<>();
39 private final Map<Futex, Futex> futexes = new WeakHashMap<>();
40 private Arena arena;
41 private final boolean closeArena;
42 private MemorySegment arenaSegment;
43 final FileDescriptor fd;
44
45 SharedMemoryCleaner(Arena arena, Object observed, FileDescriptor fd) {
46 super(observed);
47 if (arena == null) {
48 this.arena = null;
49 this.closeArena = true;
50 } else {
51 this.arena = arena;
52 this.closeArena = false;
53 }
54 this.fd = fd;
55 }
56
57 void registerMemorySegment(MemorySegment ms, int duplicates) {
58 synchronized (segments) {
59 segments.put(ms, duplicates);
60 }
61 }
62
63 @Override
64 @SuppressWarnings("PMD.CognitiveComplexity")
65 @SuppressFBWarnings("USO_UNSAFE_OBJECT_SYNCHRONIZATION")
66 protected synchronized void doClean() throws IOException {
67 if (!SharedMemory.isUtilLoaded()) {
68
69 return;
70 }
71
72 Map<FileDescriptor, Long> map = SharedMemory.FD_MEMORY;
73 if (map != null && fd != null) {
74 synchronized (map) {
75 map.remove(fd);
76 }
77 }
78
79 synchronized (futexes) {
80 if (!futexes.isEmpty()) {
81 for (Futex f : futexes.keySet()) {
82 try {
83 f.tryWake(true);
84 f.close();
85 } catch (Exception e) {
86
87 }
88 }
89 futexes.clear();
90 }
91 }
92
93 if (closeArena && arena != null) {
94 arena.close();
95 }
96
97 MemoryImplUtilInternal util = SharedMemory.getUtil();
98
99 IOException exc = null;
100 if (fd != null && fd.valid()) {
101 try {
102 util.close(fd);
103 } catch (IOException e) {
104 exc = e;
105 }
106 }
107
108 List<Entry<MemorySegment, Integer>> list;
109 synchronized (segments) {
110 list = new ArrayList<>(segments.entrySet()).reversed();
111 segments.clear();
112 }
113 for (Map.Entry<MemorySegment, Integer> en : list) {
114 MemorySegment ms = en.getKey();
115 int duplicates = en.getValue();
116
117 long addr = ms.address();
118 long length = ms.byteSize();
119 if (ms.scope().isAlive()) {
120 util.madvise(addr, length, MemoryImplUtilInternal.MADV_FREE_NOW, true);
121 continue;
122 }
123
124 try {
125 util.unmap(addr, length, duplicates, false);
126 } catch (IOException e) {
127 if (exc == null) {
128 exc = e;
129 } else {
130 exc.addSuppressed(e);
131 }
132 }
133 }
134
135 if (exc != null) {
136 throw exc;
137 }
138 }
139
140 public boolean isCovered(MemorySegment segment) {
141 Objects.requireNonNull(segment);
142 long start = segment.address();
143 long end = start + segment.byteSize();
144 synchronized (segments) {
145 for (MemorySegment ms : segments.keySet()) {
146 if (ms == segment) {
147 return true;
148 }
149 long addr = ms.address();
150 if (start >= addr && end <= addr + ms.byteSize()) {
151 return true;
152 }
153 }
154 }
155 return false;
156 }
157
158 public synchronized MemorySegment getArenaSegment() {
159 if (this.arenaSegment == null) {
160 this.arenaSegment = getArena().allocate(0);
161 }
162 return arenaSegment;
163 }
164
165 public void registerFutex(Futex futex) {
166 synchronized (futexes) {
167 futexes.put(futex, futex);
168 }
169 }
170
171 public void checkCovered(MemorySegment addr) throws IOException {
172 if (!isCovered(addr)) {
173 throw new IOException("Not a MemorySegment of ours");
174 }
175 }
176
177 public synchronized Arena getArena() {
178 if (this.arena == null) {
179 this.arena = Arena.ofShared();
180 }
181 return this.arena;
182 }
183 }