|
1 /* |
|
2 * Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any 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.UnixConstants.*; |
|
36 |
|
37 /** |
|
38 * Solaris implementation of WatchService based on file events notification |
|
39 * facility. |
|
40 */ |
|
41 |
|
42 class SolarisWatchService |
|
43 extends AbstractWatchService |
|
44 { |
|
45 private static final Unsafe unsafe = Unsafe.getUnsafe(); |
|
46 private static int addressSize = unsafe.addressSize(); |
|
47 |
|
48 private static int dependsArch(int value32, int value64) { |
|
49 return (addressSize == 4) ? value32 : value64; |
|
50 } |
|
51 |
|
52 /* |
|
53 * typedef struct port_event { |
|
54 * int portev_events; |
|
55 * ushort_t portev_source; |
|
56 * ushort_t portev_pad; |
|
57 * uintptr_t portev_object; |
|
58 * void *portev_user; |
|
59 * } port_event_t; |
|
60 */ |
|
61 private static final int SIZEOF_PORT_EVENT = dependsArch(16, 24); |
|
62 private static final int OFFSETOF_EVENTS = 0; |
|
63 private static final int OFFSETOF_SOURCE = 4; |
|
64 private static final int OFFSETOF_OBJECT = 8; |
|
65 |
|
66 /* |
|
67 * typedef struct file_obj { |
|
68 * timestruc_t fo_atime; |
|
69 * timestruc_t fo_mtime; |
|
70 * timestruc_t fo_ctime; |
|
71 * uintptr_t fo_pad[3]; |
|
72 * char *fo_name; |
|
73 * } file_obj_t; |
|
74 */ |
|
75 private static final int SIZEOF_FILEOBJ = dependsArch(40, 80); |
|
76 private static final int OFFSET_FO_NAME = dependsArch(36, 72); |
|
77 |
|
78 // port sources |
|
79 private static final short PORT_SOURCE_USER = 3; |
|
80 private static final short PORT_SOURCE_FILE = 7; |
|
81 |
|
82 // user-watchable events |
|
83 private static final int FILE_MODIFIED = 0x00000002; |
|
84 private static final int FILE_ATTRIB = 0x00000004; |
|
85 private static final int FILE_NOFOLLOW = 0x10000000; |
|
86 |
|
87 // exception events |
|
88 private static final int FILE_DELETE = 0x00000010; |
|
89 private static final int FILE_RENAME_TO = 0x00000020; |
|
90 private static final int FILE_RENAME_FROM = 0x00000040; |
|
91 private static final int UNMOUNTED = 0x20000000; |
|
92 private static final int MOUNTEDOVER = 0x40000000; |
|
93 |
|
94 // background thread to read change events |
|
95 private final Poller poller; |
|
96 |
|
97 SolarisWatchService(UnixFileSystem fs) throws IOException { |
|
98 int port = -1; |
|
99 try { |
|
100 port = portCreate(); |
|
101 } catch (UnixException x) { |
|
102 throw new IOException(x.errorString()); |
|
103 } |
|
104 |
|
105 this.poller = new Poller(fs, this, port); |
|
106 this.poller.start(); |
|
107 } |
|
108 |
|
109 @Override |
|
110 WatchKey register(Path dir, |
|
111 WatchEvent.Kind<?>[] events, |
|
112 WatchEvent.Modifier... modifiers) |
|
113 throws IOException |
|
114 { |
|
115 // delegate to poller |
|
116 return poller.register(dir, events, modifiers); |
|
117 } |
|
118 |
|
119 @Override |
|
120 void implClose() throws IOException { |
|
121 // delegate to poller |
|
122 poller.close(); |
|
123 } |
|
124 |
|
125 /** |
|
126 * WatchKey implementation |
|
127 */ |
|
128 private class SolarisWatchKey extends AbstractWatchKey |
|
129 implements DirectoryNode |
|
130 { |
|
131 private final UnixPath dir; |
|
132 private final UnixFileKey fileKey; |
|
133 |
|
134 // pointer to native file_obj object |
|
135 private final long object; |
|
136 |
|
137 // events (may be changed). set to null when watch key is invalid |
|
138 private volatile Set<? extends WatchEvent.Kind<?>> events; |
|
139 |
|
140 // map of entries in directory; created lazily; accessed only by |
|
141 // poller thread. |
|
142 private Map<Path,EntryNode> children; |
|
143 |
|
144 SolarisWatchKey(SolarisWatchService watcher, |
|
145 UnixPath dir, |
|
146 UnixFileKey fileKey, |
|
147 long object, |
|
148 Set<? extends WatchEvent.Kind<?>> events) |
|
149 { |
|
150 super(watcher); |
|
151 this.dir = dir; |
|
152 this.fileKey = fileKey; |
|
153 this.object = object; |
|
154 this.events = events; |
|
155 } |
|
156 |
|
157 UnixPath getFileRef() { |
|
158 return dir; |
|
159 } |
|
160 |
|
161 UnixFileKey getFileKey() { |
|
162 return fileKey; |
|
163 } |
|
164 |
|
165 @Override |
|
166 public long object() { |
|
167 return object; |
|
168 } |
|
169 |
|
170 void invalidate() { |
|
171 events = null; |
|
172 } |
|
173 |
|
174 Set<? extends WatchEvent.Kind<?>> events() { |
|
175 return events; |
|
176 } |
|
177 |
|
178 void setEvents(Set<? extends WatchEvent.Kind<?>> events) { |
|
179 this.events = events; |
|
180 } |
|
181 |
|
182 @Override |
|
183 public boolean isValid() { |
|
184 return events != null; |
|
185 } |
|
186 |
|
187 @Override |
|
188 public void cancel() { |
|
189 if (isValid()) { |
|
190 // delegate to poller |
|
191 poller.cancel(this); |
|
192 } |
|
193 } |
|
194 |
|
195 @Override |
|
196 public void addChild(Path name, EntryNode node) { |
|
197 if (children == null) |
|
198 children = new HashMap<Path,EntryNode>(); |
|
199 children.put(name, node); |
|
200 } |
|
201 |
|
202 @Override |
|
203 public void removeChild(Path name) { |
|
204 children.remove(name); |
|
205 } |
|
206 |
|
207 @Override |
|
208 public EntryNode getChild(Path name) { |
|
209 if (children != null) |
|
210 return children.get(name); |
|
211 return null; |
|
212 } |
|
213 } |
|
214 |
|
215 /** |
|
216 * Background thread to read from port |
|
217 */ |
|
218 private class Poller extends AbstractPoller { |
|
219 |
|
220 // maximum number of events to read per call to port_getn |
|
221 private static final int MAX_EVENT_COUNT = 128; |
|
222 |
|
223 // events that map to ENTRY_DELETE |
|
224 private static final int FILE_REMOVED = |
|
225 (FILE_DELETE|FILE_RENAME_TO|FILE_RENAME_FROM); |
|
226 |
|
227 // events that tell us not to re-associate the object |
|
228 private static final int FILE_EXCEPTION = |
|
229 (FILE_REMOVED|UNMOUNTED|MOUNTEDOVER); |
|
230 |
|
231 // address of event buffers (used to receive events with port_getn) |
|
232 private final long bufferAddress; |
|
233 |
|
234 private final SolarisWatchService watcher; |
|
235 |
|
236 // the I/O port |
|
237 private final int port; |
|
238 |
|
239 // maps file key (dev/inode) to WatchKey |
|
240 private final Map<UnixFileKey,SolarisWatchKey> fileKey2WatchKey; |
|
241 |
|
242 // maps file_obj object to Node |
|
243 private final Map<Long,Node> object2Node; |
|
244 |
|
245 /** |
|
246 * Create a new instance |
|
247 */ |
|
248 Poller(UnixFileSystem fs, SolarisWatchService watcher, int port) { |
|
249 this.watcher = watcher; |
|
250 this.port = port; |
|
251 this.bufferAddress = |
|
252 unsafe.allocateMemory(SIZEOF_PORT_EVENT * MAX_EVENT_COUNT); |
|
253 this.fileKey2WatchKey = new HashMap<UnixFileKey,SolarisWatchKey>(); |
|
254 this.object2Node = new HashMap<Long,Node>(); |
|
255 } |
|
256 |
|
257 @Override |
|
258 void wakeup() throws IOException { |
|
259 // write to port to wakeup polling thread |
|
260 try { |
|
261 portSend(port, 0); |
|
262 } catch (UnixException x) { |
|
263 throw new IOException(x.errorString()); |
|
264 } |
|
265 } |
|
266 |
|
267 @Override |
|
268 Object implRegister(Path obj, |
|
269 Set<? extends WatchEvent.Kind<?>> events, |
|
270 WatchEvent.Modifier... modifiers) |
|
271 { |
|
272 // no modifiers supported at this time |
|
273 if (modifiers.length > 0) { |
|
274 for (WatchEvent.Modifier modifier: modifiers) { |
|
275 if (modifier == null) |
|
276 return new NullPointerException(); |
|
277 if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) |
|
278 continue; // ignore |
|
279 return new UnsupportedOperationException("Modifier not supported"); |
|
280 } |
|
281 } |
|
282 |
|
283 UnixPath dir = (UnixPath)obj; |
|
284 |
|
285 // check file is directory |
|
286 UnixFileAttributes attrs = null; |
|
287 try { |
|
288 attrs = UnixFileAttributes.get(dir, true); |
|
289 } catch (UnixException x) { |
|
290 return x.asIOException(dir); |
|
291 } |
|
292 if (!attrs.isDirectory()) { |
|
293 return new NotDirectoryException(dir.getPathForExecptionMessage()); |
|
294 } |
|
295 |
|
296 // return existing watch key after updating events if already |
|
297 // registered |
|
298 UnixFileKey fileKey = attrs.fileKey(); |
|
299 SolarisWatchKey watchKey = fileKey2WatchKey.get(fileKey); |
|
300 if (watchKey != null) { |
|
301 updateEvents(watchKey, events); |
|
302 return watchKey; |
|
303 } |
|
304 |
|
305 // register directory |
|
306 long object = 0L; |
|
307 try { |
|
308 object = registerImpl(dir, (FILE_MODIFIED | FILE_ATTRIB)); |
|
309 } catch (UnixException x) { |
|
310 return x.asIOException(dir); |
|
311 } |
|
312 |
|
313 // create watch key and insert it into maps |
|
314 watchKey = new SolarisWatchKey(watcher, dir, fileKey, object, events); |
|
315 object2Node.put(object, watchKey); |
|
316 fileKey2WatchKey.put(fileKey, watchKey); |
|
317 |
|
318 // register all entries in directory |
|
319 registerChildren(dir, watchKey, false); |
|
320 |
|
321 return watchKey; |
|
322 } |
|
323 |
|
324 // cancel single key |
|
325 @Override |
|
326 void implCancelKey(WatchKey obj) { |
|
327 SolarisWatchKey key = (SolarisWatchKey)obj; |
|
328 if (key.isValid()) { |
|
329 fileKey2WatchKey.remove(key.getFileKey()); |
|
330 |
|
331 // release resources for entries in directory |
|
332 if (key.children != null) { |
|
333 for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) { |
|
334 EntryNode node = entry.getValue(); |
|
335 long object = node.object(); |
|
336 object2Node.remove(object); |
|
337 releaseObject(object, true); |
|
338 } |
|
339 } |
|
340 |
|
341 // release resources for directory |
|
342 long object = key.object(); |
|
343 object2Node.remove(object); |
|
344 releaseObject(object, true); |
|
345 |
|
346 // and finally invalidate the key |
|
347 key.invalidate(); |
|
348 } |
|
349 } |
|
350 |
|
351 // close watch service |
|
352 @Override |
|
353 void implCloseAll() { |
|
354 // release all native resources |
|
355 for (Long object: object2Node.keySet()) { |
|
356 releaseObject(object, true); |
|
357 } |
|
358 |
|
359 // invalidate all keys |
|
360 for (Map.Entry<UnixFileKey,SolarisWatchKey> entry: fileKey2WatchKey.entrySet()) { |
|
361 entry.getValue().invalidate(); |
|
362 } |
|
363 |
|
364 // clean-up |
|
365 object2Node.clear(); |
|
366 fileKey2WatchKey.clear(); |
|
367 |
|
368 // free global resources |
|
369 unsafe.freeMemory(bufferAddress); |
|
370 UnixNativeDispatcher.close(port); |
|
371 } |
|
372 |
|
373 /** |
|
374 * Poller main loop. Blocks on port_getn waiting for events and then |
|
375 * processes them. |
|
376 */ |
|
377 @Override |
|
378 public void run() { |
|
379 try { |
|
380 for (;;) { |
|
381 int n = portGetn(port, bufferAddress, MAX_EVENT_COUNT); |
|
382 assert n > 0; |
|
383 |
|
384 long address = bufferAddress; |
|
385 for (int i=0; i<n; i++) { |
|
386 boolean shutdown = processEvent(address); |
|
387 if (shutdown) |
|
388 return; |
|
389 address += SIZEOF_PORT_EVENT; |
|
390 } |
|
391 } |
|
392 } catch (UnixException x) { |
|
393 x.printStackTrace(); |
|
394 } |
|
395 } |
|
396 |
|
397 /** |
|
398 * Process a single port_event |
|
399 * |
|
400 * Returns true if poller thread is requested to shutdown. |
|
401 */ |
|
402 boolean processEvent(long address) { |
|
403 // pe->portev_source |
|
404 short source = unsafe.getShort(address + OFFSETOF_SOURCE); |
|
405 // pe->portev_object |
|
406 long object = unsafe.getAddress(address + OFFSETOF_OBJECT); |
|
407 // pe->portev_events |
|
408 int events = unsafe.getInt(address + OFFSETOF_EVENTS); |
|
409 |
|
410 // user event is trigger to process pending requests |
|
411 if (source != PORT_SOURCE_FILE) { |
|
412 if (source == PORT_SOURCE_USER) { |
|
413 // process any pending requests |
|
414 boolean shutdown = processRequests(); |
|
415 if (shutdown) |
|
416 return true; |
|
417 } |
|
418 return false; |
|
419 } |
|
420 |
|
421 // lookup object to get Node |
|
422 Node node = object2Node.get(object); |
|
423 if (node == null) { |
|
424 // should not happen |
|
425 return false; |
|
426 } |
|
427 |
|
428 // As a workaround for 6642290 and 6636438/6636412 we don't use |
|
429 // FILE_EXCEPTION events to tell use not to register the file. |
|
430 // boolean reregister = (events & FILE_EXCEPTION) == 0; |
|
431 boolean reregister = true; |
|
432 |
|
433 // If node is EntryNode then event relates to entry in directory |
|
434 // If node is a SolarisWatchKey (DirectoryNode) then event relates |
|
435 // to a watched directory. |
|
436 boolean isDirectory = (node instanceof SolarisWatchKey); |
|
437 if (isDirectory) { |
|
438 processDirectoryEvents((SolarisWatchKey)node, events); |
|
439 } else { |
|
440 boolean ignore = processEntryEvents((EntryNode)node, events); |
|
441 if (ignore) |
|
442 reregister = false; |
|
443 } |
|
444 |
|
445 // need to re-associate to get further events |
|
446 if (reregister) { |
|
447 try { |
|
448 events = FILE_MODIFIED | FILE_ATTRIB; |
|
449 if (!isDirectory) events |= FILE_NOFOLLOW; |
|
450 portAssociate(port, |
|
451 PORT_SOURCE_FILE, |
|
452 object, |
|
453 events); |
|
454 } catch (UnixException x) { |
|
455 // unable to re-register |
|
456 reregister = false; |
|
457 } |
|
458 } |
|
459 |
|
460 // object is not re-registered so release resources. If |
|
461 // object is a watched directory then signal key |
|
462 if (!reregister) { |
|
463 // release resources |
|
464 object2Node.remove(object); |
|
465 releaseObject(object, false); |
|
466 |
|
467 // if watch key then signal it |
|
468 if (isDirectory) { |
|
469 SolarisWatchKey key = (SolarisWatchKey)node; |
|
470 fileKey2WatchKey.remove( key.getFileKey() ); |
|
471 key.invalidate(); |
|
472 key.signal(); |
|
473 } else { |
|
474 // if entry then remove it from parent |
|
475 EntryNode entry = (EntryNode)node; |
|
476 SolarisWatchKey key = (SolarisWatchKey)entry.parent(); |
|
477 key.removeChild(entry.name()); |
|
478 } |
|
479 } |
|
480 |
|
481 return false; |
|
482 } |
|
483 |
|
484 /** |
|
485 * Process directory events. If directory is modified then re-scan |
|
486 * directory to register any new entries |
|
487 */ |
|
488 void processDirectoryEvents(SolarisWatchKey key, int mask) { |
|
489 if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) { |
|
490 registerChildren(key.getFileRef(), key, |
|
491 key.events().contains(StandardWatchEventKind.ENTRY_CREATE)); |
|
492 } |
|
493 } |
|
494 |
|
495 /** |
|
496 * Process events for entries in registered directories. Returns {@code |
|
497 * true} if events are ignored because the watch key has been cancelled. |
|
498 */ |
|
499 boolean processEntryEvents(EntryNode node, int mask) { |
|
500 SolarisWatchKey key = (SolarisWatchKey)node.parent(); |
|
501 Set<? extends WatchEvent.Kind<?>> events = key.events(); |
|
502 if (events == null) { |
|
503 // key has been cancelled so ignore event |
|
504 return true; |
|
505 } |
|
506 |
|
507 // entry modified |
|
508 if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) && |
|
509 events.contains(StandardWatchEventKind.ENTRY_MODIFY)) |
|
510 { |
|
511 key.signalEvent(StandardWatchEventKind.ENTRY_MODIFY, node.name()); |
|
512 } |
|
513 |
|
514 // entry removed |
|
515 if (((mask & (FILE_REMOVED)) != 0) && |
|
516 events.contains(StandardWatchEventKind.ENTRY_DELETE)) |
|
517 { |
|
518 // Due to 6636438/6636412 we may get a remove event for cases |
|
519 // where a rmdir/unlink/rename is attempted but fails. Until |
|
520 // this issue is resolved we re-lstat the file to check if it |
|
521 // exists. If it exists then we ignore the event. To keep the |
|
522 // workaround simple we don't check the st_ino so it isn't |
|
523 // effective when the file is replaced. |
|
524 boolean removed = true; |
|
525 try { |
|
526 UnixFileAttributes |
|
527 .get(key.getFileRef().resolve(node.name()), false); |
|
528 removed = false; |
|
529 } catch (UnixException x) { } |
|
530 |
|
531 if (removed) |
|
532 key.signalEvent(StandardWatchEventKind.ENTRY_DELETE, node.name()); |
|
533 } |
|
534 return false; |
|
535 } |
|
536 |
|
537 /** |
|
538 * Registers all entries in the given directory |
|
539 * |
|
540 * The {@code sendEvents} parameter indicates if ENTRY_CREATE events |
|
541 * should be queued when new entries are found. When initially |
|
542 * registering a directory then will always be false. When re-scanning |
|
543 * a directory then it depends on if the event is enabled or not. |
|
544 */ |
|
545 void registerChildren(UnixPath dir, |
|
546 SolarisWatchKey parent, |
|
547 boolean sendEvents) |
|
548 { |
|
549 // if the ENTRY_MODIFY event is not enabled then we don't need |
|
550 // modification events for entries in the directory |
|
551 int events = FILE_NOFOLLOW; |
|
552 if (parent.events().contains(StandardWatchEventKind.ENTRY_MODIFY)) |
|
553 events |= (FILE_MODIFIED | FILE_ATTRIB); |
|
554 |
|
555 DirectoryStream<Path> stream = null; |
|
556 try { |
|
557 stream = dir.newDirectoryStream(); |
|
558 } catch (IOException x) { |
|
559 // nothing we can do |
|
560 return; |
|
561 } |
|
562 try { |
|
563 for (Path entry: stream) { |
|
564 Path name = entry.getName(); |
|
565 |
|
566 // skip entry if already registered |
|
567 if (parent.getChild(name) != null) |
|
568 continue; |
|
569 |
|
570 // send ENTRY_CREATE if enabled |
|
571 if (sendEvents) { |
|
572 parent.signalEvent(StandardWatchEventKind.ENTRY_CREATE, name); |
|
573 } |
|
574 |
|
575 // register it |
|
576 long object = 0L; |
|
577 try { |
|
578 object = registerImpl((UnixPath)entry, events); |
|
579 } catch (UnixException x) { |
|
580 // can't register so ignore for now. |
|
581 continue; |
|
582 } |
|
583 |
|
584 // create node |
|
585 EntryNode node = new EntryNode(object, entry.getName(), parent); |
|
586 // tell the parent about it |
|
587 parent.addChild(entry.getName(), node); |
|
588 object2Node.put(object, node); |
|
589 } |
|
590 } catch (ConcurrentModificationException x) { |
|
591 // error during iteration which we ignore for now |
|
592 } finally { |
|
593 try { |
|
594 stream.close(); |
|
595 } catch (IOException x) { } |
|
596 } |
|
597 } |
|
598 |
|
599 /** |
|
600 * Update watch key's events. Where the ENTRY_MODIFY changes then we |
|
601 * need to update the events of registered children. |
|
602 */ |
|
603 void updateEvents(SolarisWatchKey key, Set<? extends WatchEvent.Kind<?>> events) { |
|
604 // update events, rembering if ENTRY_MODIFY was previously |
|
605 // enabled or disabled. |
|
606 boolean wasModifyEnabled = key.events() |
|
607 .contains(StandardWatchEventKind.ENTRY_MODIFY); |
|
608 key.setEvents(events); |
|
609 |
|
610 // check if ENTRY_MODIFY has changed |
|
611 boolean isModifyEnabled = events |
|
612 .contains(StandardWatchEventKind.ENTRY_MODIFY); |
|
613 if (wasModifyEnabled == isModifyEnabled) { |
|
614 return; |
|
615 } |
|
616 |
|
617 // if changed then update events of children |
|
618 if (key.children != null) { |
|
619 int ev = FILE_NOFOLLOW; |
|
620 if (isModifyEnabled) |
|
621 ev |= (FILE_MODIFIED | FILE_ATTRIB); |
|
622 |
|
623 for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) { |
|
624 long object = entry.getValue().object(); |
|
625 try { |
|
626 portAssociate(port, |
|
627 PORT_SOURCE_FILE, |
|
628 object, |
|
629 ev); |
|
630 } catch (UnixException x) { |
|
631 // nothing we can do. |
|
632 } |
|
633 } |
|
634 } |
|
635 } |
|
636 |
|
637 /** |
|
638 * Calls port_associate to register the given path. |
|
639 * Returns pointer to fileobj structure that is allocated for |
|
640 * the registration. |
|
641 */ |
|
642 long registerImpl(UnixPath dir, int events) |
|
643 throws UnixException |
|
644 { |
|
645 // allocate memory for the path (file_obj->fo_name field) |
|
646 byte[] path = dir.getByteArrayForSysCalls(); |
|
647 int len = path.length; |
|
648 long name = unsafe.allocateMemory(len+1); |
|
649 unsafe.copyMemory(path, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, |
|
650 name, (long)len); |
|
651 unsafe.putByte(name + len, (byte)0); |
|
652 |
|
653 // allocate memory for filedatanode structure - this is the object |
|
654 // to port_associate |
|
655 long object = unsafe.allocateMemory(SIZEOF_FILEOBJ); |
|
656 unsafe.setMemory(null, object, SIZEOF_FILEOBJ, (byte)0); |
|
657 unsafe.putAddress(object + OFFSET_FO_NAME, name); |
|
658 |
|
659 // associate the object with the port |
|
660 try { |
|
661 portAssociate(port, |
|
662 PORT_SOURCE_FILE, |
|
663 object, |
|
664 events); |
|
665 } catch (UnixException x) { |
|
666 // debugging |
|
667 if (x.errno() == EAGAIN) { |
|
668 System.err.println("The maximum number of objects associated "+ |
|
669 "with the port has been reached"); |
|
670 } |
|
671 |
|
672 unsafe.freeMemory(name); |
|
673 unsafe.freeMemory(object); |
|
674 throw x; |
|
675 } |
|
676 return object; |
|
677 } |
|
678 |
|
679 /** |
|
680 * Frees all resources for an file_obj object; optionally remove |
|
681 * association from port |
|
682 */ |
|
683 void releaseObject(long object, boolean dissociate) { |
|
684 // remove association |
|
685 if (dissociate) { |
|
686 try { |
|
687 portDissociate(port, PORT_SOURCE_FILE, object); |
|
688 } catch (UnixException x) { |
|
689 // ignore |
|
690 } |
|
691 } |
|
692 |
|
693 // free native memory |
|
694 long name = unsafe.getAddress(object + OFFSET_FO_NAME); |
|
695 unsafe.freeMemory(name); |
|
696 unsafe.freeMemory(object); |
|
697 } |
|
698 } |
|
699 |
|
700 /** |
|
701 * A node with native (file_obj) resources |
|
702 */ |
|
703 private static interface Node { |
|
704 long object(); |
|
705 } |
|
706 |
|
707 /** |
|
708 * A directory node with a map of the entries in the directory |
|
709 */ |
|
710 private static interface DirectoryNode extends Node { |
|
711 void addChild(Path name, EntryNode node); |
|
712 void removeChild(Path name); |
|
713 EntryNode getChild(Path name); |
|
714 } |
|
715 |
|
716 /** |
|
717 * An implementation of a node that is an entry in a directory. |
|
718 */ |
|
719 private static class EntryNode implements Node { |
|
720 private final long object; |
|
721 private final Path name; |
|
722 private final DirectoryNode parent; |
|
723 |
|
724 EntryNode(long object, Path name, DirectoryNode parent) { |
|
725 this.object = object; |
|
726 this.name = name; |
|
727 this.parent = parent; |
|
728 } |
|
729 |
|
730 @Override |
|
731 public long object() { |
|
732 return object; |
|
733 } |
|
734 |
|
735 Path name() { |
|
736 return name; |
|
737 } |
|
738 |
|
739 DirectoryNode parent() { |
|
740 return parent; |
|
741 } |
|
742 } |
|
743 |
|
744 // -- native methods -- |
|
745 |
|
746 private static native void init(); |
|
747 |
|
748 private static native int portCreate() throws UnixException; |
|
749 |
|
750 private static native void portAssociate(int port, int source, long object, int events) |
|
751 throws UnixException; |
|
752 |
|
753 private static native void portDissociate(int port, int source, long object) |
|
754 throws UnixException; |
|
755 |
|
756 private static native void portSend(int port, int events) |
|
757 throws UnixException; |
|
758 |
|
759 private static native int portGetn(int port, long address, int max) |
|
760 throws UnixException; |
|
761 |
|
762 static { |
|
763 AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
764 public Void run() { |
|
765 System.loadLibrary("nio"); |
|
766 return null; |
|
767 }}); |
|
768 init(); |
|
769 } |
|
770 } |