Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java
32288 views
1
/*
2
* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.nio.fs;
27
28
import java.nio.file.*;
29
import java.security.AccessController;
30
import java.security.PrivilegedAction;
31
import java.util.*;
32
import java.io.IOException;
33
import sun.misc.Unsafe;
34
35
import static sun.nio.fs.UnixNativeDispatcher.*;
36
import static sun.nio.fs.UnixConstants.*;
37
38
/**
39
* Linux implementation of WatchService based on inotify.
40
*
41
* In summary a background thread polls inotify plus a socket used for the wakeup
42
* mechanism. Requests to add or remove a watch, or close the watch service,
43
* cause the thread to wakeup and process the request. Events are processed
44
* by the thread which causes it to signal/queue the corresponding watch keys.
45
*/
46
47
class LinuxWatchService
48
extends AbstractWatchService
49
{
50
private static final Unsafe unsafe = Unsafe.getUnsafe();
51
52
// background thread to read change events
53
private final Poller poller;
54
55
LinuxWatchService(UnixFileSystem fs) throws IOException {
56
// initialize inotify
57
int ifd = - 1;
58
try {
59
ifd = inotifyInit();
60
} catch (UnixException x) {
61
String msg = (x.errno() == EMFILE) ?
62
"User limit of inotify instances reached or too many open files" :
63
x.errorString();
64
throw new IOException(msg);
65
}
66
67
// configure inotify to be non-blocking
68
// create socketpair used in the close mechanism
69
int sp[] = new int[2];
70
try {
71
configureBlocking(ifd, false);
72
socketpair(sp);
73
configureBlocking(sp[0], false);
74
} catch (UnixException x) {
75
UnixNativeDispatcher.close(ifd);
76
throw new IOException(x.errorString());
77
}
78
79
this.poller = new Poller(fs, this, ifd, sp);
80
this.poller.start();
81
}
82
83
@Override
84
WatchKey register(Path dir,
85
WatchEvent.Kind<?>[] events,
86
WatchEvent.Modifier... modifiers)
87
throws IOException
88
{
89
// delegate to poller
90
return poller.register(dir, events, modifiers);
91
}
92
93
@Override
94
void implClose() throws IOException {
95
// delegate to poller
96
poller.close();
97
}
98
99
/**
100
* WatchKey implementation
101
*/
102
private static class LinuxWatchKey extends AbstractWatchKey {
103
// inotify descriptor
104
private final int ifd;
105
// watch descriptor
106
private volatile int wd;
107
108
LinuxWatchKey(UnixPath dir, LinuxWatchService watcher, int ifd, int wd) {
109
super(dir, watcher);
110
this.ifd = ifd;
111
this.wd = wd;
112
}
113
114
int descriptor() {
115
return wd;
116
}
117
118
void invalidate(boolean remove) {
119
if (remove) {
120
try {
121
inotifyRmWatch(ifd, wd);
122
} catch (UnixException x) {
123
// ignore
124
}
125
}
126
wd = -1;
127
}
128
129
@Override
130
public boolean isValid() {
131
return (wd != -1);
132
}
133
134
@Override
135
public void cancel() {
136
if (isValid()) {
137
// delegate to poller
138
((LinuxWatchService)watcher()).poller.cancel(this);
139
}
140
}
141
}
142
143
/**
144
* Background thread to read from inotify
145
*/
146
private static class Poller extends AbstractPoller {
147
/**
148
* struct inotify_event {
149
* int wd;
150
* uint32_t mask;
151
* uint32_t len;
152
* char name __flexarr; // present if len > 0
153
* } act_t;
154
*/
155
private static final int SIZEOF_INOTIFY_EVENT = eventSize();
156
private static final int[] offsets = eventOffsets();
157
private static final int OFFSETOF_WD = offsets[0];
158
private static final int OFFSETOF_MASK = offsets[1];
159
private static final int OFFSETOF_LEN = offsets[3];
160
private static final int OFFSETOF_NAME = offsets[4];
161
162
private static final int IN_MODIFY = 0x00000002;
163
private static final int IN_ATTRIB = 0x00000004;
164
private static final int IN_MOVED_FROM = 0x00000040;
165
private static final int IN_MOVED_TO = 0x00000080;
166
private static final int IN_CREATE = 0x00000100;
167
private static final int IN_DELETE = 0x00000200;
168
169
private static final int IN_UNMOUNT = 0x00002000;
170
private static final int IN_Q_OVERFLOW = 0x00004000;
171
private static final int IN_IGNORED = 0x00008000;
172
173
// sizeof buffer for when polling inotify
174
private static final int BUFFER_SIZE = 8192;
175
176
private final UnixFileSystem fs;
177
private final LinuxWatchService watcher;
178
179
// inotify file descriptor
180
private final int ifd;
181
// socketpair used to shutdown polling thread
182
private final int socketpair[];
183
// maps watch descriptor to Key
184
private final Map<Integer,LinuxWatchKey> wdToKey;
185
// address of read buffer
186
private final long address;
187
188
Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) {
189
this.fs = fs;
190
this.watcher = watcher;
191
this.ifd = ifd;
192
this.socketpair = sp;
193
this.wdToKey = new HashMap<Integer,LinuxWatchKey>();
194
this.address = unsafe.allocateMemory(BUFFER_SIZE);
195
}
196
197
@Override
198
void wakeup() throws IOException {
199
// write to socketpair to wakeup polling thread
200
try {
201
write(socketpair[1], address, 1);
202
} catch (UnixException x) {
203
throw new IOException(x.errorString());
204
}
205
}
206
207
@Override
208
Object implRegister(Path obj,
209
Set<? extends WatchEvent.Kind<?>> events,
210
WatchEvent.Modifier... modifiers)
211
{
212
UnixPath dir = (UnixPath)obj;
213
214
int mask = 0;
215
for (WatchEvent.Kind<?> event: events) {
216
if (event == StandardWatchEventKinds.ENTRY_CREATE) {
217
mask |= IN_CREATE | IN_MOVED_TO;
218
continue;
219
}
220
if (event == StandardWatchEventKinds.ENTRY_DELETE) {
221
mask |= IN_DELETE | IN_MOVED_FROM;
222
continue;
223
}
224
if (event == StandardWatchEventKinds.ENTRY_MODIFY) {
225
mask |= IN_MODIFY | IN_ATTRIB;
226
continue;
227
}
228
}
229
230
// no modifiers supported at this time
231
if (modifiers.length > 0) {
232
for (WatchEvent.Modifier modifier: modifiers) {
233
if (modifier == null)
234
return new NullPointerException();
235
if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
236
continue; // ignore
237
return new UnsupportedOperationException("Modifier not supported");
238
}
239
}
240
241
// check file is directory
242
UnixFileAttributes attrs = null;
243
try {
244
attrs = UnixFileAttributes.get(dir, true);
245
} catch (UnixException x) {
246
return x.asIOException(dir);
247
}
248
if (!attrs.isDirectory()) {
249
return new NotDirectoryException(dir.getPathForExceptionMessage());
250
}
251
252
// register with inotify (replaces existing mask if already registered)
253
int wd = -1;
254
try {
255
NativeBuffer buffer =
256
NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls());
257
try {
258
wd = inotifyAddWatch(ifd, buffer.address(), mask);
259
} finally {
260
buffer.release();
261
}
262
} catch (UnixException x) {
263
if (x.errno() == ENOSPC) {
264
return new IOException("User limit of inotify watches reached");
265
}
266
return x.asIOException(dir);
267
}
268
269
// ensure watch descriptor is in map
270
LinuxWatchKey key = wdToKey.get(wd);
271
if (key == null) {
272
key = new LinuxWatchKey(dir, watcher, ifd, wd);
273
wdToKey.put(wd, key);
274
}
275
return key;
276
}
277
278
// cancel single key
279
@Override
280
void implCancelKey(WatchKey obj) {
281
LinuxWatchKey key = (LinuxWatchKey)obj;
282
if (key.isValid()) {
283
wdToKey.remove(key.descriptor());
284
key.invalidate(true);
285
}
286
}
287
288
// close watch service
289
@Override
290
void implCloseAll() {
291
// invalidate all keys
292
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
293
entry.getValue().invalidate(true);
294
}
295
wdToKey.clear();
296
297
// free resources
298
unsafe.freeMemory(address);
299
UnixNativeDispatcher.close(socketpair[0]);
300
UnixNativeDispatcher.close(socketpair[1]);
301
UnixNativeDispatcher.close(ifd);
302
}
303
304
/**
305
* Poller main loop
306
*/
307
@Override
308
public void run() {
309
try {
310
for (;;) {
311
int nReady, bytesRead;
312
313
// wait for close or inotify event
314
nReady = poll(ifd, socketpair[0]);
315
316
// read from inotify
317
try {
318
bytesRead = read(ifd, address, BUFFER_SIZE);
319
} catch (UnixException x) {
320
if (x.errno() != EAGAIN)
321
throw x;
322
bytesRead = 0;
323
}
324
325
// iterate over buffer to decode events
326
int offset = 0;
327
while (offset < bytesRead) {
328
long event = address + offset;
329
int wd = unsafe.getInt(event + OFFSETOF_WD);
330
int mask = unsafe.getInt(event + OFFSETOF_MASK);
331
int len = unsafe.getInt(event + OFFSETOF_LEN);
332
333
// file name
334
UnixPath name = null;
335
if (len > 0) {
336
int actual = len;
337
338
// null-terminated and maybe additional null bytes to
339
// align the next event
340
while (actual > 0) {
341
long last = event + OFFSETOF_NAME + actual - 1;
342
if (unsafe.getByte(last) != 0)
343
break;
344
actual--;
345
}
346
if (actual > 0) {
347
byte[] buf = new byte[actual];
348
unsafe.copyMemory(null, event + OFFSETOF_NAME,
349
buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual);
350
name = new UnixPath(fs, buf);
351
}
352
}
353
354
// process event
355
processEvent(wd, mask, name);
356
357
offset += (SIZEOF_INOTIFY_EVENT + len);
358
}
359
360
// process any pending requests
361
if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) {
362
try {
363
read(socketpair[0], address, BUFFER_SIZE);
364
boolean shutdown = processRequests();
365
if (shutdown)
366
break;
367
} catch (UnixException x) {
368
if (x.errno() != UnixConstants.EAGAIN)
369
throw x;
370
}
371
}
372
}
373
} catch (UnixException x) {
374
x.printStackTrace();
375
}
376
}
377
378
379
/**
380
* map inotify event to WatchEvent.Kind
381
*/
382
private WatchEvent.Kind<?> maskToEventKind(int mask) {
383
if ((mask & IN_MODIFY) > 0)
384
return StandardWatchEventKinds.ENTRY_MODIFY;
385
if ((mask & IN_ATTRIB) > 0)
386
return StandardWatchEventKinds.ENTRY_MODIFY;
387
if ((mask & IN_CREATE) > 0)
388
return StandardWatchEventKinds.ENTRY_CREATE;
389
if ((mask & IN_MOVED_TO) > 0)
390
return StandardWatchEventKinds.ENTRY_CREATE;
391
if ((mask & IN_DELETE) > 0)
392
return StandardWatchEventKinds.ENTRY_DELETE;
393
if ((mask & IN_MOVED_FROM) > 0)
394
return StandardWatchEventKinds.ENTRY_DELETE;
395
return null;
396
}
397
398
/**
399
* Process event from inotify
400
*/
401
private void processEvent(int wd, int mask, final UnixPath name) {
402
// overflow - signal all keys
403
if ((mask & IN_Q_OVERFLOW) > 0) {
404
for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {
405
entry.getValue()
406
.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
407
}
408
return;
409
}
410
411
// lookup wd to get key
412
LinuxWatchKey key = wdToKey.get(wd);
413
if (key == null)
414
return; // should not happen
415
416
// file deleted
417
if ((mask & IN_IGNORED) > 0) {
418
wdToKey.remove(wd);
419
key.invalidate(false);
420
key.signal();
421
return;
422
}
423
424
// event for directory itself
425
if (name == null)
426
return;
427
428
// map to event and queue to key
429
WatchEvent.Kind<?> kind = maskToEventKind(mask);
430
if (kind != null) {
431
key.signalEvent(kind, name);
432
}
433
}
434
}
435
436
// -- native methods --
437
438
// sizeof inotify_event
439
private static native int eventSize();
440
441
// offsets of inotify_event
442
private static native int[] eventOffsets();
443
444
private static native int inotifyInit() throws UnixException;
445
446
private static native int inotifyAddWatch(int fd, long pathAddress, int mask)
447
throws UnixException;
448
449
private static native void inotifyRmWatch(int fd, int wd)
450
throws UnixException;
451
452
private static native void configureBlocking(int fd, boolean blocking)
453
throws UnixException;
454
455
private static native void socketpair(int[] sv) throws UnixException;
456
457
private static native int poll(int fd1, int fd2) throws UnixException;
458
459
static {
460
AccessController.doPrivileged(new PrivilegedAction<Void>() {
461
public Void run() {
462
System.loadLibrary("nio");
463
return null;
464
}});
465
}
466
}
467
468