Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/windows/classes/sun/nio/fs/WindowsWatchService.java
41139 views
1
/*
2
* Copyright (c) 2008, 2016, 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.io.IOException;
29
import java.nio.file.NotDirectoryException;
30
import java.nio.file.Path;
31
import java.nio.file.StandardWatchEventKinds;
32
import java.nio.file.WatchEvent;
33
import java.nio.file.WatchKey;
34
import java.util.HashMap;
35
import java.util.Map;
36
import java.util.Set;
37
38
import jdk.internal.misc.Unsafe;
39
40
import static sun.nio.fs.WindowsNativeDispatcher.*;
41
import static sun.nio.fs.WindowsConstants.*;
42
43
/*
44
* Win32 implementation of WatchService based on ReadDirectoryChangesW.
45
*/
46
47
class WindowsWatchService
48
extends AbstractWatchService
49
{
50
private static final int WAKEUP_COMPLETION_KEY = 0;
51
52
// background thread to service I/O completion port
53
private final Poller poller;
54
55
/**
56
* Creates an I/O completion port and a daemon thread to service it
57
*/
58
WindowsWatchService(WindowsFileSystem fs) throws IOException {
59
// create I/O completion port
60
long port = 0L;
61
try {
62
port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0);
63
} catch (WindowsException x) {
64
throw new IOException(x.getMessage());
65
}
66
67
this.poller = new Poller(fs, this, port);
68
this.poller.start();
69
}
70
71
@Override
72
WatchKey register(Path path,
73
WatchEvent.Kind<?>[] events,
74
WatchEvent.Modifier... modifiers)
75
throws IOException
76
{
77
// delegate to poller
78
return poller.register(path, events, modifiers);
79
}
80
81
@Override
82
void implClose() throws IOException {
83
// delegate to poller
84
poller.close();
85
}
86
87
/**
88
* Windows implementation of WatchKey.
89
*/
90
private static class WindowsWatchKey extends AbstractWatchKey {
91
// file key (used to detect existing registrations)
92
private final FileKey fileKey;
93
94
// handle to directory
95
private volatile long handle = INVALID_HANDLE_VALUE;
96
97
// interest events
98
private Set<? extends WatchEvent.Kind<?>> events;
99
100
// subtree
101
private boolean watchSubtree;
102
103
// buffer for change events
104
private NativeBuffer buffer;
105
106
// pointer to bytes returned (in buffer)
107
private long countAddress;
108
109
// pointer to overlapped structure (in buffer)
110
private long overlappedAddress;
111
112
// completion key (used to map I/O completion to WatchKey)
113
private int completionKey;
114
115
// flag indicates that ReadDirectoryChangesW failed
116
// and overlapped I/O operation wasn't started
117
private boolean errorStartingOverlapped;
118
119
WindowsWatchKey(Path dir,
120
AbstractWatchService watcher,
121
FileKey fileKey)
122
{
123
super(dir, watcher);
124
this.fileKey = fileKey;
125
}
126
127
WindowsWatchKey init(long handle,
128
Set<? extends WatchEvent.Kind<?>> events,
129
boolean watchSubtree,
130
NativeBuffer buffer,
131
long countAddress,
132
long overlappedAddress,
133
int completionKey)
134
{
135
this.handle = handle;
136
this.events = events;
137
this.watchSubtree = watchSubtree;
138
this.buffer = buffer;
139
this.countAddress = countAddress;
140
this.overlappedAddress = overlappedAddress;
141
this.completionKey = completionKey;
142
return this;
143
}
144
145
long handle() {
146
return handle;
147
}
148
149
Set<? extends WatchEvent.Kind<?>> events() {
150
return events;
151
}
152
153
void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
154
this.events = events;
155
}
156
157
boolean watchSubtree() {
158
return watchSubtree;
159
}
160
161
NativeBuffer buffer() {
162
return buffer;
163
}
164
165
long countAddress() {
166
return countAddress;
167
}
168
169
long overlappedAddress() {
170
return overlappedAddress;
171
}
172
173
FileKey fileKey() {
174
return fileKey;
175
}
176
177
int completionKey() {
178
return completionKey;
179
}
180
181
void setErrorStartingOverlapped(boolean value) {
182
errorStartingOverlapped = value;
183
}
184
185
boolean isErrorStartingOverlapped() {
186
return errorStartingOverlapped;
187
}
188
189
// Invalidate the key, assumes that resources have been released
190
void invalidate() {
191
((WindowsWatchService)watcher()).poller.releaseResources(this);
192
handle = INVALID_HANDLE_VALUE;
193
buffer = null;
194
countAddress = 0;
195
overlappedAddress = 0;
196
errorStartingOverlapped = false;
197
}
198
199
@Override
200
public boolean isValid() {
201
return handle != INVALID_HANDLE_VALUE;
202
}
203
204
@Override
205
public void cancel() {
206
if (isValid()) {
207
// delegate to poller
208
((WindowsWatchService)watcher()).poller.cancel(this);
209
}
210
}
211
}
212
213
// file key to unique identify (open) directory
214
private static class FileKey {
215
private final int volSerialNumber;
216
private final int fileIndexHigh;
217
private final int fileIndexLow;
218
219
FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) {
220
this.volSerialNumber = volSerialNumber;
221
this.fileIndexHigh = fileIndexHigh;
222
this.fileIndexLow = fileIndexLow;
223
}
224
225
@Override
226
public int hashCode() {
227
return volSerialNumber ^ fileIndexHigh ^ fileIndexLow;
228
}
229
230
@Override
231
public boolean equals(Object obj) {
232
if (obj == this)
233
return true;
234
if (!(obj instanceof FileKey))
235
return false;
236
FileKey other = (FileKey)obj;
237
if (this.volSerialNumber != other.volSerialNumber) return false;
238
if (this.fileIndexHigh != other.fileIndexHigh) return false;
239
return this.fileIndexLow == other.fileIndexLow;
240
}
241
}
242
243
// all change events
244
private static final int ALL_FILE_NOTIFY_EVENTS =
245
FILE_NOTIFY_CHANGE_FILE_NAME |
246
FILE_NOTIFY_CHANGE_DIR_NAME |
247
FILE_NOTIFY_CHANGE_ATTRIBUTES |
248
FILE_NOTIFY_CHANGE_SIZE |
249
FILE_NOTIFY_CHANGE_LAST_WRITE |
250
FILE_NOTIFY_CHANGE_CREATION |
251
FILE_NOTIFY_CHANGE_SECURITY;
252
253
/**
254
* Background thread to service I/O completion port.
255
*/
256
private static class Poller extends AbstractPoller {
257
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
258
259
/*
260
* typedef struct _OVERLAPPED {
261
* ULONG_PTR Internal;
262
* ULONG_PTR InternalHigh;
263
* union {
264
* struct { DWORD Offset; DWORD OffsetHigh; };
265
* PVOID Pointer;
266
* };
267
* HANDLE hEvent;
268
* } OVERLAPPED;
269
*/
270
private static final short SIZEOF_DWORD = 4;
271
private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit
272
private static final short OFFSETOF_HEVENT =
273
(UNSAFE.addressSize() == 4) ? (short) 16 : 24;
274
275
276
/*
277
* typedef struct _FILE_NOTIFY_INFORMATION {
278
* DWORD NextEntryOffset;
279
* DWORD Action;
280
* DWORD FileNameLength;
281
* WCHAR FileName[1];
282
* } FileNameLength;
283
*/
284
private static final short OFFSETOF_NEXTENTRYOFFSET = 0;
285
private static final short OFFSETOF_ACTION = 4;
286
private static final short OFFSETOF_FILENAMELENGTH = 8;
287
private static final short OFFSETOF_FILENAME = 12;
288
289
// size of per-directory buffer for events (FIXME - make this configurable)
290
// Need to be less than 4*16384 = 65536. DWORD align.
291
private static final int CHANGES_BUFFER_SIZE = 16 * 1024;
292
293
private final WindowsFileSystem fs;
294
private final WindowsWatchService watcher;
295
private final long port;
296
297
// maps completion key to WatchKey
298
private final Map<Integer, WindowsWatchKey> ck2key;
299
300
// maps file key to WatchKey
301
private final Map<FileKey, WindowsWatchKey> fk2key;
302
303
// unique completion key for each directory
304
// native completion key capacity is 64 bits on Win64.
305
private int lastCompletionKey;
306
307
Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) {
308
this.fs = fs;
309
this.watcher = watcher;
310
this.port = port;
311
this.ck2key = new HashMap<>();
312
this.fk2key = new HashMap<>();
313
this.lastCompletionKey = 0;
314
}
315
316
@Override
317
void wakeup() throws IOException {
318
try {
319
PostQueuedCompletionStatus(port, WAKEUP_COMPLETION_KEY);
320
} catch (WindowsException x) {
321
throw new IOException(x.getMessage());
322
}
323
}
324
325
/**
326
* Register a directory for changes as follows:
327
*
328
* 1. Open directory
329
* 2. Read its attributes (and check it really is a directory)
330
* 3. Assign completion key and associated handle with completion port
331
* 4. Call ReadDirectoryChangesW to start (async) read of changes
332
* 5. Create or return existing key representing registration
333
*/
334
@Override
335
Object implRegister(Path obj,
336
Set<? extends WatchEvent.Kind<?>> events,
337
WatchEvent.Modifier... modifiers)
338
{
339
WindowsPath dir = (WindowsPath)obj;
340
boolean watchSubtree = false;
341
342
// FILE_TREE modifier allowed
343
for (WatchEvent.Modifier modifier: modifiers) {
344
if (ExtendedOptions.FILE_TREE.matches(modifier)) {
345
watchSubtree = true;
346
} else {
347
if (modifier == null)
348
return new NullPointerException();
349
if (!ExtendedOptions.SENSITIVITY_HIGH.matches(modifier) &&
350
!ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier) &&
351
!ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) {
352
return new UnsupportedOperationException("Modifier not supported");
353
}
354
}
355
}
356
357
// open directory
358
long handle;
359
try {
360
handle = CreateFile(dir.getPathForWin32Calls(),
361
FILE_LIST_DIRECTORY,
362
(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE),
363
OPEN_EXISTING,
364
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED);
365
} catch (WindowsException x) {
366
return x.asIOException(dir);
367
}
368
369
boolean registered = false;
370
try {
371
// read attributes and check file is a directory
372
WindowsFileAttributes attrs;
373
try {
374
attrs = WindowsFileAttributes.readAttributes(handle);
375
} catch (WindowsException x) {
376
return x.asIOException(dir);
377
}
378
if (!attrs.isDirectory()) {
379
return new NotDirectoryException(dir.getPathForExceptionMessage());
380
}
381
382
// check if this directory is already registered
383
FileKey fk = new FileKey(attrs.volSerialNumber(),
384
attrs.fileIndexHigh(),
385
attrs.fileIndexLow());
386
WindowsWatchKey existing = fk2key.get(fk);
387
388
// if already registered and we're not changing the subtree
389
// modifier then simply update the event and return the key.
390
if (existing != null && watchSubtree == existing.watchSubtree()) {
391
existing.setEvents(events);
392
return existing;
393
}
394
395
// Can overflow the int type capacity.
396
// Skip WAKEUP_COMPLETION_KEY value.
397
int completionKey = ++lastCompletionKey;
398
if (completionKey == WAKEUP_COMPLETION_KEY)
399
completionKey = ++lastCompletionKey;
400
401
// associate handle with completion port
402
try {
403
CreateIoCompletionPort(handle, port, completionKey);
404
} catch (WindowsException x) {
405
return new IOException(x.getMessage());
406
}
407
408
// allocate memory for events, including space for other structures
409
// needed to do overlapped I/O
410
int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED;
411
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
412
413
long bufferAddress = buffer.address();
414
long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED;
415
long countAddress = overlappedAddress - SIZEOF_DWORD;
416
417
// zero the overlapped structure
418
UNSAFE.setMemory(overlappedAddress, SIZEOF_OVERLAPPED, (byte)0);
419
420
// start async read of changes to directory
421
try {
422
createAndAttachEvent(overlappedAddress);
423
424
ReadDirectoryChangesW(handle,
425
bufferAddress,
426
CHANGES_BUFFER_SIZE,
427
watchSubtree,
428
ALL_FILE_NOTIFY_EVENTS,
429
countAddress,
430
overlappedAddress);
431
} catch (WindowsException x) {
432
closeAttachedEvent(overlappedAddress);
433
buffer.release();
434
return new IOException(x.getMessage());
435
}
436
437
WindowsWatchKey watchKey;
438
if (existing == null) {
439
// not registered so create new watch key
440
watchKey = new WindowsWatchKey(dir, watcher, fk)
441
.init(handle, events, watchSubtree, buffer, countAddress,
442
overlappedAddress, completionKey);
443
// map file key to watch key
444
fk2key.put(fk, watchKey);
445
} else {
446
// directory already registered so need to:
447
// 1. remove mapping from old completion key to existing watch key
448
// 2. release existing key's resources (handle/buffer)
449
// 3. re-initialize key with new handle/buffer
450
ck2key.remove(existing.completionKey());
451
releaseResources(existing);
452
watchKey = existing.init(handle, events, watchSubtree, buffer,
453
countAddress, overlappedAddress, completionKey);
454
}
455
// map completion map to watch key
456
ck2key.put(completionKey, watchKey);
457
458
registered = true;
459
return watchKey;
460
461
} finally {
462
if (!registered) CloseHandle(handle);
463
}
464
}
465
466
/**
467
* Cancels the outstanding I/O operation on the directory
468
* associated with the given key and releases the associated
469
* resources.
470
*/
471
private void releaseResources(WindowsWatchKey key) {
472
if (!key.isErrorStartingOverlapped()) {
473
try {
474
CancelIo(key.handle());
475
GetOverlappedResult(key.handle(), key.overlappedAddress());
476
} catch (WindowsException expected) {
477
// expected as I/O operation has been cancelled
478
}
479
}
480
CloseHandle(key.handle());
481
closeAttachedEvent(key.overlappedAddress());
482
key.buffer().free();
483
}
484
485
/**
486
* Creates an unnamed event and set it as the hEvent field
487
* in the given OVERLAPPED structure
488
*/
489
private void createAndAttachEvent(long ov) throws WindowsException {
490
long hEvent = CreateEvent(false, false);
491
UNSAFE.putAddress(ov + OFFSETOF_HEVENT, hEvent);
492
}
493
494
/**
495
* Closes the event attached to the given OVERLAPPED structure. A
496
* no-op if there isn't an event attached.
497
*/
498
private void closeAttachedEvent(long ov) {
499
long hEvent = UNSAFE.getAddress(ov + OFFSETOF_HEVENT);
500
if (hEvent != 0 && hEvent != INVALID_HANDLE_VALUE)
501
CloseHandle(hEvent);
502
}
503
504
// cancel single key
505
@Override
506
void implCancelKey(WatchKey obj) {
507
WindowsWatchKey key = (WindowsWatchKey)obj;
508
if (key.isValid()) {
509
fk2key.remove(key.fileKey());
510
ck2key.remove(key.completionKey());
511
key.invalidate();
512
}
513
}
514
515
// close watch service
516
@Override
517
void implCloseAll() {
518
// cancel all keys
519
ck2key.values().forEach(WindowsWatchKey::invalidate);
520
521
fk2key.clear();
522
ck2key.clear();
523
524
// close I/O completion port
525
CloseHandle(port);
526
}
527
528
// Translate file change action into watch event
529
private WatchEvent.Kind<?> translateActionToEvent(int action) {
530
switch (action) {
531
case FILE_ACTION_MODIFIED :
532
return StandardWatchEventKinds.ENTRY_MODIFY;
533
534
case FILE_ACTION_ADDED :
535
case FILE_ACTION_RENAMED_NEW_NAME :
536
return StandardWatchEventKinds.ENTRY_CREATE;
537
538
case FILE_ACTION_REMOVED :
539
case FILE_ACTION_RENAMED_OLD_NAME :
540
return StandardWatchEventKinds.ENTRY_DELETE;
541
542
default :
543
return null; // action not recognized
544
}
545
}
546
547
// process events (list of FILE_NOTIFY_INFORMATION structures)
548
private void processEvents(WindowsWatchKey key, int size) {
549
long address = key.buffer().address();
550
551
int nextOffset;
552
do {
553
int action = UNSAFE.getInt(address + OFFSETOF_ACTION);
554
555
// map action to event
556
WatchEvent.Kind<?> kind = translateActionToEvent(action);
557
if (key.events().contains(kind)) {
558
// copy the name
559
int nameLengthInBytes = UNSAFE.getInt(address + OFFSETOF_FILENAMELENGTH);
560
if ((nameLengthInBytes % 2) != 0) {
561
throw new AssertionError("FileNameLength is not a multiple of 2");
562
}
563
char[] nameAsArray = new char[nameLengthInBytes/2];
564
UNSAFE.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray,
565
Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
566
567
// create FileName and queue event
568
WindowsPath name = WindowsPath
569
.createFromNormalizedPath(fs, new String(nameAsArray));
570
key.signalEvent(kind, name);
571
}
572
573
// next event
574
nextOffset = UNSAFE.getInt(address + OFFSETOF_NEXTENTRYOFFSET);
575
address += (long)nextOffset;
576
} while (nextOffset != 0);
577
}
578
579
/**
580
* Poller main loop
581
*/
582
@Override
583
public void run() {
584
for (;;) {
585
CompletionStatus info;
586
try {
587
info = GetQueuedCompletionStatus(port);
588
} catch (WindowsException x) {
589
// this should not happen
590
x.printStackTrace();
591
return;
592
}
593
594
// wakeup
595
if (info.completionKey() == WAKEUP_COMPLETION_KEY) {
596
boolean shutdown = processRequests();
597
if (shutdown) {
598
return;
599
}
600
continue;
601
}
602
603
// map completionKey to get WatchKey
604
WindowsWatchKey key = ck2key.get((int)info.completionKey());
605
if (key == null) {
606
// We get here when a registration is changed. In that case
607
// the directory is closed which causes an event with the
608
// old completion key.
609
continue;
610
}
611
612
boolean criticalError = false;
613
int errorCode = info.error();
614
int messageSize = info.bytesTransferred();
615
if (errorCode == ERROR_NOTIFY_ENUM_DIR) {
616
// buffer overflow
617
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
618
} else if (errorCode != 0 && errorCode != ERROR_MORE_DATA) {
619
// ReadDirectoryChangesW failed
620
criticalError = true;
621
} else {
622
// ERROR_MORE_DATA is a warning about incomplete
623
// data transfer over TCP/UDP stack. For the case
624
// [messageSize] is zero in the most of cases.
625
626
if (messageSize > 0) {
627
// process non-empty events.
628
processEvents(key, messageSize);
629
} else if (errorCode == 0) {
630
// insufficient buffer size
631
// not described, but can happen.
632
key.signalEvent(StandardWatchEventKinds.OVERFLOW, null);
633
}
634
635
// start read for next batch of changes
636
try {
637
ReadDirectoryChangesW(key.handle(),
638
key.buffer().address(),
639
CHANGES_BUFFER_SIZE,
640
key.watchSubtree(),
641
ALL_FILE_NOTIFY_EVENTS,
642
key.countAddress(),
643
key.overlappedAddress());
644
} catch (WindowsException x) {
645
// no choice but to cancel key
646
criticalError = true;
647
key.setErrorStartingOverlapped(true);
648
}
649
}
650
if (criticalError) {
651
implCancelKey(key);
652
key.signal();
653
}
654
}
655
}
656
}
657
}
658
659