LotsOfEvents.java revision 2197:cddb43b12d28
1/*
2 * Copyright 2010 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.
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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/* @test
25 * @bug 6907760 6929532
26 * @summary Tests WatchService behavior when lots of events are pending
27 * @library ..
28 * @run main/timeout=180 LotsOfEvents
29 */
30
31import java.nio.file.*;
32import static java.nio.file.StandardWatchEventKind.*;
33import java.io.IOException;
34import java.io.OutputStream;
35import java.util.*;
36import java.util.concurrent.TimeUnit;
37
38public class LotsOfEvents {
39
40    static final Random rand = new Random();
41
42    public static void main(String[] args) throws Exception {
43        Path dir = TestUtil.createTemporaryDirectory();
44        try {
45            testOverflowEvent(dir);
46            testModifyEventsQueuing(dir);
47        } finally {
48            TestUtil.removeAll(dir);
49        }
50    }
51
52    /**
53     * Tests that OVERFLOW events are not retreived with other events.
54     */
55    static void testOverflowEvent(Path dir)
56        throws IOException, InterruptedException
57    {
58        WatchService watcher = dir.getFileSystem().newWatchService();
59        try {
60            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE);
61
62            // create a lot of files
63            int n = 1024;
64            Path[] files = new Path[n];
65            for (int i=0; i<n; i++) {
66                files[i] = dir.resolve("foo" + i).createFile();
67            }
68
69            // give time for events to accumulate (improve chance of overflow)
70            Thread.sleep(1000);
71
72            // check that we see the create events (or overflow)
73            drainAndCheckOverflowEvents(watcher, ENTRY_CREATE, n);
74
75            // delete the files
76            for (int i=0; i<n; i++) {
77                files[i].delete();
78            }
79
80            // give time for events to accumulate (improve chance of overflow)
81            Thread.sleep(1000);
82
83            // check that we see the delete events (or overflow)
84            drainAndCheckOverflowEvents(watcher, ENTRY_DELETE, n);
85        } finally {
86            watcher.close();
87        }
88    }
89
90    static void drainAndCheckOverflowEvents(WatchService watcher,
91                                            WatchEvent.Kind<?> expectedKind,
92                                            int count)
93        throws IOException, InterruptedException
94    {
95        // wait for key to be signalled - the timeout is long to allow for
96        // polling implementations
97        WatchKey key = watcher.poll(15, TimeUnit.SECONDS);
98        if (key != null && count == 0)
99            throw new RuntimeException("Key was signalled (unexpected)");
100        if (key == null && count > 0)
101            throw new RuntimeException("Key not signalled (unexpected)");
102
103        int nread = 0;
104        boolean gotOverflow = false;
105        while (key != null) {
106            List<WatchEvent<?>> events = key.pollEvents();
107            for (WatchEvent<?> event: events) {
108                WatchEvent.Kind<?> kind = event.kind();
109                if (kind == expectedKind) {
110                    // expected event kind
111                    if (++nread > count)
112                        throw new RuntimeException("More events than expected!!");
113                } else if (kind == OVERFLOW) {
114                    // overflow event should not be retrieved with other events
115                    if (events.size() > 1)
116                        throw new RuntimeException("Overflow retrieved with other events");
117                    gotOverflow = true;
118                } else {
119                    throw new RuntimeException("Unexpected event '" + kind + "'");
120                }
121            }
122            if (!key.reset())
123                throw new RuntimeException("Key is no longer valid");
124            key = watcher.poll(2, TimeUnit.SECONDS);
125        }
126
127        // check that all expected events were received or there was an overflow
128        if (nread < count && !gotOverflow)
129            throw new RuntimeException("Insufficient events");
130    }
131
132    /**
133     * Tests that check that ENTRY_MODIFY events are queued efficiently
134     */
135    static void testModifyEventsQueuing(Path dir)
136        throws IOException, InterruptedException
137    {
138        // this test uses a random number of files
139        final int nfiles = 5 + rand.nextInt(10);
140        DirectoryEntry[] entries = new DirectoryEntry[nfiles];
141        for (int i=0; i<nfiles; i++) {
142            entries[i] = new DirectoryEntry(dir.resolve("foo" + i));
143
144            // "some" of the files exist, some do not.
145            entries[i].deleteIfExists();
146            if (rand.nextBoolean())
147                entries[i].create();
148        }
149
150        WatchService watcher = dir.getFileSystem().newWatchService();
151        try {
152            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
153
154            // do several rounds of noise and test
155            for (int round=0; round<10; round++) {
156
157                // make some noise!!!
158                for (int i=0; i<100; i++) {
159                    DirectoryEntry entry = entries[rand.nextInt(nfiles)];
160                    int action = rand.nextInt(10);
161                    switch (action) {
162                        case 0 : entry.create(); break;
163                        case 1 : entry.deleteIfExists(); break;
164                        default: entry.modifyIfExists();
165                    }
166                }
167
168                // process events and ensure that we don't get repeated modify
169                // events for the same file.
170                WatchKey key = watcher.poll(15, TimeUnit.SECONDS);
171                while (key != null) {
172                    Set<Path> modified = new HashSet<Path>();
173                    for (WatchEvent<?> event: key.pollEvents()) {
174                        WatchEvent.Kind<?> kind = event.kind();
175                        Path file = (kind == OVERFLOW) ? null : (Path)event.context();
176                        if (kind == ENTRY_MODIFY) {
177                            boolean added = modified.add(file);
178                            if (!added) {
179                                throw new RuntimeException(
180                                    "ENTRY_MODIFY events not queued efficiently");
181                            }
182                        } else {
183                            if (file != null) modified.remove(file);
184                        }
185                    }
186                    if (!key.reset())
187                        throw new RuntimeException("Key is no longer valid");
188                    key = watcher.poll(2, TimeUnit.SECONDS);
189                }
190            }
191
192        } finally {
193            watcher.close();
194        }
195    }
196
197    static class DirectoryEntry {
198        private final Path file;
199        DirectoryEntry(Path file) {
200            this.file = file;
201        }
202        void create() throws IOException {
203            if (file.notExists())
204                file.createFile();
205
206        }
207        void deleteIfExists() throws IOException {
208            file.deleteIfExists();
209        }
210        void modifyIfExists() throws IOException {
211            if (file.exists()) {
212                OutputStream out = file.newOutputStream(StandardOpenOption.APPEND);
213                try {
214                    out.write("message".getBytes());
215                } finally {
216                    out.close();
217                }
218            }
219        }
220    }
221
222}
223