1/*
2 * Copyright (c) 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.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 8148192
26 * @summary Invoking close asynchronously can cause register to fail with
27 *     an IOException rather than a ClosedWatchServiceException
28 * @run main LotsOfCloses
29 */
30
31import java.io.IOException;
32import java.io.UncheckedIOException;
33import java.nio.file.ClosedWatchServiceException;
34import java.nio.file.FileSystems;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.nio.file.StandardWatchEventKinds;
38import java.nio.file.WatchService;
39import java.util.Random;
40import java.util.concurrent.Callable;
41import java.util.concurrent.ExecutorService;
42import java.util.concurrent.Executors;
43import java.util.concurrent.Future;
44
45public class LotsOfCloses {
46
47    private static final Random RAND = new Random();
48
49    public static void main(String[] args) throws Exception {
50        Path dir = Files.createTempDirectory("tmp");
51        ExecutorService pool = Executors.newCachedThreadPool();
52        try {
53
54            // run the test repeatedly
55            long start = System.currentTimeMillis();
56            while ((System.currentTimeMillis() - start) < 5000) {
57                test(dir, pool);
58            }
59
60        } finally {
61            pool.shutdown();
62        }
63
64    }
65
66    /**
67     * Create a WatchService to watch for changes in the given directory
68     * and then attempt to close the WatchService and change a registration
69     * at the same time.
70     */
71    static void test(Path dir, ExecutorService pool) throws Exception {
72        WatchService watcher = FileSystems.getDefault().newWatchService();
73
74        // initial registration
75        dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
76
77        // submit tasks to close the WatchService and update the registration
78        Future<Void> closeResult;
79        Future<Boolean> registerResult;
80
81        if (RAND.nextBoolean()) {
82            closeResult = pool.submit(newCloserTask(watcher));
83            registerResult = pool.submit(newRegisterTask(watcher, dir));
84        } else {
85            registerResult = pool.submit(newRegisterTask(watcher, dir));
86            closeResult = pool.submit(newCloserTask(watcher));
87        }
88
89        closeResult.get();
90        registerResult.get();
91
92    }
93
94    /**
95     * Returns a task that closes the given WatchService.
96     */
97    static Callable<Void> newCloserTask(WatchService watcher) {
98        return () ->  {
99            try {
100                watcher.close();
101                return null;
102            } catch (IOException ioe) {
103                throw new UncheckedIOException(ioe);
104            }
105        };
106    }
107
108    /**
109     * Returns a task that updates the registration of a directory with
110     * a WatchService.
111     */
112    static Callable<Boolean> newRegisterTask(WatchService watcher, Path dir) {
113        return () -> {
114            try {
115                dir.register(watcher, StandardWatchEventKinds.ENTRY_DELETE);
116                return true;
117            } catch (ClosedWatchServiceException e) {
118                return false;
119            } catch (IOException ioe) {
120                throw new UncheckedIOException(ioe);
121            }
122        };
123    }
124}
125