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