This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 246893
Collapse All | Expand All

(-)a/masterfs/src/org/netbeans/modules/masterfs/filebasedfs/utils/FileChangedManager.java (+29 lines)
Lines 183-188 Link Here
183
        return IDLE_IO.get() != null;
183
        return IDLE_IO.get() != null;
184
    }
184
    }
185
185
186
    /**
187
     * If invoked in {@link #idleIO(int, java.lang.Runnable,
188
     * java.lang.Runnable, java.util.concurrent.atomic.AtomicBoolean) idleIO},
189
     * wait until all priority IOs are completed and start {@link Runnable}
190
     * {@code run}. The {@code run} will not be blocked in FileChangedManager
191
     * even if another priority IO is started (this would cause deadlocks, see
192
     * bug 246893).
193
     */
194
    public static void waitNowAndRun(Runnable run) {
195
        Integer storedMaxLoad = IDLE_IO.get();
196
        if (storedMaxLoad != null) {
197
            try {
198
                waitIOLoadLowerThan(storedMaxLoad);
199
            } catch (InterruptedException ex) {
200
                if (isFine) {
201
                    LOG.log(Level.FINE, "Interrupted {0}", ex.getMessage());
202
                }
203
            }
204
            IDLE_IO.set(null);
205
            try {
206
                run.run();
207
            } finally {
208
                IDLE_IO.set(storedMaxLoad);
209
            }
210
        } else {
211
            run.run();
212
        }
213
    }
214
186
    public static void idleIO(int maximumLoad, Runnable r, Runnable goingToSleep, AtomicBoolean goOn) {
215
    public static void idleIO(int maximumLoad, Runnable r, Runnable goingToSleep, AtomicBoolean goOn) {
187
        Integer prev = IDLE_IO.get();
216
        Integer prev = IDLE_IO.get();
188
        Runnable pGoing = IDLE_CALL.get();
217
        Runnable pGoing = IDLE_CALL.get();
(-)a/masterfs/src/org/netbeans/modules/masterfs/watcher/Watcher.java (-1 / +16 lines)
Lines 52-57 Link Here
52
import java.util.logging.Level;
52
import java.util.logging.Level;
53
import java.util.logging.Logger;
53
import java.util.logging.Logger;
54
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory;
54
import org.netbeans.modules.masterfs.filebasedfs.fileobjects.FileObjectFactory;
55
import org.netbeans.modules.masterfs.filebasedfs.utils.FileChangedManager;
55
import org.netbeans.modules.masterfs.providers.InterceptionListener;
56
import org.netbeans.modules.masterfs.providers.InterceptionListener;
56
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
57
import org.netbeans.modules.masterfs.providers.ProvidedExtensions;
57
import org.openide.filesystems.FileObject;
58
import org.openide.filesystems.FileObject;
Lines 225-231 Link Here
225
            }
226
            }
226
        }
227
        }
227
        
228
        
228
        final void register(FileObject fo) {
229
        final void register(final FileObject fo) {
229
            if (fo.isValid() && !fo.isFolder()) {
230
            if (fo.isValid() && !fo.isFolder()) {
230
                LOG.log(Level.INFO, "Should be a folder: {0} data: {1} folder: {2} valid: {3}", new Object[]{fo, fo.isData(), fo.isFolder(), fo.isValid()});
231
                LOG.log(Level.INFO, "Should be a folder: {0} data: {1} folder: {2} valid: {3}", new Object[]{fo, fo.isData(), fo.isFolder(), fo.isValid()});
231
            }
232
            }
Lines 234-239 Link Here
234
            } catch (IOException ex) {
235
            } catch (IOException ex) {
235
                LOG.log(Level.INFO, "Exception while clearing the queue", ex);
236
                LOG.log(Level.INFO, "Exception while clearing the queue", ex);
236
            }
237
            }
238
            FileChangedManager.waitNowAndRun(new Runnable() {
239
                @Override
240
                public void run() {
241
                    registerSynchronized(fo);
242
                }
243
            });
244
        }
245
246
        /**
247
         * Part of registration process that cannot be blocked by
248
         * FileChangedManager, and which can take lock LOCK. See bug 246893.
249
         * (FileChangedManager's "lock" must be taken always first.)
250
         */
251
        private void registerSynchronized(FileObject fo) {
237
            synchronized (LOCK) {
252
            synchronized (LOCK) {
238
                NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl);
253
                NotifierKeyRef<KEY> kr = new NotifierKeyRef<KEY>(fo, null, null, impl);
239
                if (getReferences().contains(kr)) {
254
                if (getReferences().contains(kr)) {
(-)a/masterfs/test/unit/src/org/netbeans/modules/masterfs/filebasedfs/utils/FileChangedManagerDeadlockTest.java (+223 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2014 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2014 Sun Microsystems, Inc.
41
 */
42
package org.netbeans.modules.masterfs.filebasedfs.utils;
43
44
import java.io.File;
45
import java.io.IOException;
46
import java.util.concurrent.Callable;
47
import java.util.concurrent.Semaphore;
48
import java.util.concurrent.atomic.AtomicBoolean;
49
import static junit.framework.Assert.assertTrue;
50
import org.junit.Before;
51
import org.junit.Test;
52
import org.netbeans.junit.MockServices;
53
import org.netbeans.modules.masterfs.providers.Notifier;
54
import org.netbeans.modules.masterfs.watcher.Watcher;
55
import org.openide.filesystems.FileUtil;
56
import org.openide.util.Exceptions;
57
import org.openide.util.Lookup;
58
59
/**
60
 *
61
 * @author jhavlin
62
 */
63
public class FileChangedManagerDeadlockTest {
64
65
    @Before
66
    public void setUp() {
67
        MockServices.setServices(MockNotifier.class);
68
    }
69
70
    @Test
71
    public void testDeadlockWhenNotifierCallsSecurityManager() throws
72
            InterruptedException {
73
74
        // permits - progress
75
        // 1 - Priority IO can enter priority section (lock taken by idle IO)
76
        // 2 - Idle IO can ask for some IO operation (security manager blocked)
77
        Semaphore s = new Semaphore(0);
78
79
        Lookup.getDefault().lookup(MockNotifier.class).setSemaphore(s);
80
81
        IdleIO idleIO = new IdleIO(s);
82
        PriorityIO priorityIO = new PriorityIO(s);
83
84
        // Thread 1 - Enter idleIO, add a watch -> wait for priority
85
        Thread idle = new Thread(idleIO, "Idle IO Thread");
86
87
        // Thread 2 - Enter priority IO, wait for watch's lock.
88
        Thread priority = new Thread(priorityIO, "Priority IO Thread");
89
90
        idle.start();
91
        priority.start();
92
93
        idle.join(2000);
94
        priority.join(2000);
95
96
        assertTrue("Idle IO should be finished", idleIO.finished.get());
97
        assertTrue("Priority IO should be finished", priorityIO.finished.get());
98
    }
99
100
    private static abstract class IORunnable implements Runnable {
101
102
        protected Semaphore semaphore;
103
        final AtomicBoolean finished = new AtomicBoolean(false);
104
105
        public IORunnable(Semaphore semaphore) {
106
            this.semaphore = semaphore;
107
        }
108
109
        @Override
110
        public void run() {
111
            try {
112
                runInner();
113
            } catch (Exception ex) {
114
                Exceptions.printStackTrace(ex);
115
            }
116
            finished.set(true);
117
        }
118
119
        public abstract void runInner() throws Exception;
120
121
        public void addSomeWatch() {
122
            String homeDir = System.getProperty("user.home");
123
            Watcher.register(FileUtil.toFileObject(
124
                    new File(homeDir).getAbsoluteFile()));
125
        }
126
    }
127
128
    private static class IdleIO extends IORunnable {
129
130
        public IdleIO(Semaphore semaphore) {
131
            super(semaphore);
132
        }
133
134
        @Override
135
        public void runInner() {
136
            FileChangedManager.idleIO(50, new Runnable() {
137
138
                @Override
139
                public void run() {
140
                    // Add watch in idle thread. This cannot finish, because
141
                    // it takes Watcher lock and then waits for the priority
142
                    // thread, which waits for the watcher lock.
143
                    addSomeWatch();
144
                }
145
            }, null, new AtomicBoolean(true));
146
        }
147
    }
148
149
    private static class PriorityIO extends IORunnable {
150
151
        public PriorityIO(Semaphore semaphore) {
152
            super(semaphore);
153
        }
154
155
        @Override
156
        public void runInner() throws Exception {
157
158
            // Start priority thread after the lock is taken by the idle thread.
159
            semaphore.acquire(1);
160
            FileChangedManager.priorityIO(new Callable<Boolean>() {
161
                @Override
162
                public Boolean call() throws Exception {
163
                    // Continue the idle thread. FileChangedManager will now
164
                    // wait for this priority thread to complete.
165
                    semaphore.release(2);
166
                    // But this thread cannot complete, because it needs the
167
                    // watcher lock which is held by the idle thread.
168
                    addSomeWatch();
169
                    return true;
170
                }
171
            });
172
        }
173
    }
174
175
    @SuppressWarnings("PublicInnerClass")
176
    public static class MockNotifier extends Notifier<String> {
177
178
        private Semaphore semaphore = null;
179
180
        public void setSemaphore(Semaphore semaphore) {
181
            this.semaphore = semaphore;
182
        }
183
184
        /**
185
         * Simulate a notifier that invokes SecurityManager when adding a watch.
186
         *
187
         * @param path
188
         * @return
189
         * @throws IOException
190
         */
191
        @Override
192
        protected String addWatch(String path) throws IOException {
193
            if (semaphore != null) {
194
                // Idle thread has watcher lock. Priority thread can start.
195
                semaphore.release(1);
196
            }
197
            try {
198
                // Priority thread has entered the priority section. Idle thread
199
                // can continue.
200
                semaphore.acquire(2);
201
            } catch (InterruptedException ex) {
202
                Exceptions.printStackTrace(ex);
203
            }
204
            FileChangedManager.getInstance().checkRead(path);
205
            return "path";
206
        }
207
208
        @Override
209
        protected void removeWatch(String key) throws IOException {
210
        }
211
212
        @Override
213
        protected synchronized String nextEvent() throws IOException,
214
                InterruptedException {
215
            wait(); // wait forever
216
            return null;
217
        }
218
219
        @Override
220
        protected void start() throws IOException {
221
        }
222
    }
223
}

Return to bug 246893