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 30772
Collapse All | Expand All

(-)FolderChildren.java (-297 / +84 lines)
Line 25 Link Here
25
import org.openide.util.NbBundle;
Line 27 Link Here
28
import org.openide.nodes.AbstractNode;
Lines 39-42 Link Here
39
    /** initialization of children task */
40
    private Task initTask;
41
    /** last task that refreshes the children nodes */
42
    private Task refreshTask;
Line 47 Link Here
45
    
46
    /** Private req processor for the refresh tasks */
47
    private static RequestProcessor refRP = 
48
        new RequestProcessor("FolderChildren_refresher");
49
    
50
    /** The key used to denote the please wait node.
51
     * It is also used as a lock for the lazy initializaiton
52
     * of the pleaseWaitNode.
53
     */
54
    private static final Object PLEASE_WAIT_KEY = new Object();
55
    
56
    /** wait node - please use method getPleaseWaitNode()
57
     * to get a lazy initialized instance */
58
    private static Node pleaseWaitNode;
Line 96 Link Here
96
        initialize (true, false);
108
        refRP.post(new ChildrenRefreshRunnable(false));
97
--
Lines 99-118 Link Here
99
    /** Creates a key for given data object.
111
    /** Lazy initialization of the please wait node */
100
    * This method is here to create something different then data object,
112
    private static Node getPleaseWaitNode() {
101
    * because the data object should be finalized when not needed and
113
        boolean created = false;
102
    * that is why it should not be used as a key.
114
        synchronized (PLEASE_WAIT_KEY) {
103
    *
115
            if (pleaseWaitNode == null) {
104
    * @param obj data object
116
                pleaseWaitNode = new AbstractNode(Children.LEAF);
105
    * @return key representing the data object.
117
                created = true;
106
    */
118
            }
107
    static Object createKey (DataObject obj) {
119
        }
108
        return new Pair (obj.getPrimaryFile ());
120
        if (created) {
109
    }
121
            pleaseWaitNode.setName("wait"); // NOI18N
110
122
            pleaseWaitNode.setDisplayName(
111
    /** This method takes the key created by createKey and converts it
123
                NbBundle.getBundle(FolderChildren.class).getString("LBL_Wait")
112
    * into primary file.
124
            );
113
    *
125
            ((AbstractNode)pleaseWaitNode).setIconBase("org/openide/resources/wait"); //NOI18N
114
    * @param key the key
126
        }
115
    * @return primary file of the key
127
        return pleaseWaitNode;       
116
    */
117
    private static FileObject getFile (Object key) {
118
        return ((Pair)key).primaryFile;
119
--
Line 120 Link Here
120
129
    
121
--
Line 125 Link Here
134
        if (key == PLEASE_WAIT_KEY) {
135
            return new Node[] { getPleaseWaitNode() };
136
        }
Line 126 Link Here
126
        FileObject fo = getFile (key);
138
        FileObject fo = ((Pair)key).primaryFile;
127
--
Lines 141-183 Link Here
141
153
  
142
    /** Improves the searching capability to wait till all children
154
    public Node[] getNodes(boolean optimalResult) {
143
    * are found and then searching thru them.
155
        if (optimalResult) {
144
    */
156
            Task t = refRP.post(new ChildrenRefreshRunnable(false));
145
    public Node findChild (final String name) {
157
            t.waitFinished();
146
        // start the initialization
158
            return getNodes();
147
        Node[] forget = getNodes (); // DO NOT DELETE
148
        
149
        // waits till the list of children is created
150
        
151
        // JST: the folowing code is replacement for
152
        // initialize (false, false).waitFinished ()
153
        // 
154
        // the orgiginal code caused deadlocks when called with readAccess,
155
        // because the refreshChildren (which we wait for to be finished)
156
        // method calls setKeys and they require write request
157
        //
158
        // this code could probably be replaced by 
159
        // Children.MUTEX.isReadAccess, if such call would be added to Mutex
160
        class Jst implements Runnable {
161
            public boolean checkReadOrWrite;
162
            public boolean inReadAccess = true;
163
            public boolean inWriteAccess = true;
164
            public void run () {
165
                if (checkReadOrWrite) {
166
                    inReadAccess = false;
167
                } else {
168
                    inWriteAccess = false;
169
                }
170
            }
171
        }
172
        Jst t = new Jst ();
173
        // the code will run either immediatally or after we leave readAccess
174
        // section
175
        Children.MUTEX.postWriteRequest(t);
176
        t.checkReadOrWrite = true;
177
        Children.MUTEX.postReadRequest(t);
178
        
179
        if (!t.inReadAccess && !t.inWriteAccess) {
180
            // if we are not in readAccess we can safely wait till
181
            // refreshChildren finishes and sets new keys for the 
182
            initialize (name == null, false).waitFinished ();
183
            refreshTask.waitFinished ();
184
--
Lines 185-188 Link Here
185
        
160
        return super.getNodes(optimalResult);
186
        // do the regular child lookup
187
        Node node = super.findChild (name);
188
        return node;
189
--
Line 190 Link Here
190
162
    
191
--
Line 194 Link Here
194
        initialize (true, true);
166
        // set the please wait node
195
--
167
        setKeys (java.util.Collections.singleton(PLEASE_WAIT_KEY));
Line 197 Link Here
170
        // start the refresh task
171
        refreshChildren();
Lines 204-234 Link Here
204
        setKeys (java.util.Collections.EMPTY_SET);
205
    }
206
207
    /** Ensures that the content of children will be filled sometime.
208
    * @param force true if the content should be filled immediatelly
209
    */
210
    private Task initialize (boolean force, boolean waitFirst) {
211
        
212
        Task t = initTask;
213
214
        if (t != null && t.isFinished()) {
215
            // if the original refresh task is finished we whould check if there are
216
            // some new changes in progress and wait for them to finish
217
            FolderList l = FolderList.find (folder.getPrimaryFile(), true);
218
            l.waitProcessingFinished ();
219
            
220
            t = initTask;
221
        }
222
        
223
        if (t != null && !force) {
224
            return t;
225
        }
226
        
227
//        if (t != null) {
228
//            t.waitFinished ();
229
//            refreshTask.waitFinished ();        
230
//            return t;
231
//        }
232
233
        final Addition add = new Addition (waitFirst);
234
        refreshTask = add.refTask;
Lines 236-243 Link Here
236
        initTask = t = folder.computeChildrenList (add);
180
        // the parameter true means to set the children to empty
237
        t.addTaskListener (add);
181
        // we don't call the setKeys directly here because
238
182
        // there can be a task spawned by refreshChildren - so
239
        if (waitFirst) {
183
        // we want to clear the children after that task is finished
240
            add.waitFirst ();
184
        refRP.post(new ChildrenRefreshRunnable(true));
241
        }
242
        
243
        return t;
244
--
Lines 251-365 Link Here
251
    /** time delay between two inserts of nodes */
192
    /**
252
    private static final int TIME_DELAY = 1024;
193
     * Instances of this class are posted to the request processor refRP
253
194
     * (FolderChildren_refresher). We do this because we do not want
254
    /** Private req processor for Addition's refresh tasks */
195
     * to call setKeys synchronously.
255
    private static RequestProcessor refRP = 
196
     */
256
        new RequestProcessor("FolderChildren_refresher");
197
    private final class ChildrenRefreshRunnable implements Runnable {
257
    
198
        /** if set to true we just clear the children */
258
    /** Support for incremental adding of new nodes.
199
        private boolean clear;
259
    *
200
        
260
    * <P>
201
        /** Remember the parameter. */
261
    * There is a deadlock warning:
202
        ChildrenRefreshRunnable(boolean clear) {
262
    * <OL>
203
            this.clear = clear;
263
    *   <LI>A thread waiting in the waitFirst method can have MUTEX.readAccess
264
    *   <LI>Thread running run () needs access to MUTEX.writeAccess (because of setKeys)
265
    *   <LI>Be sure that the thread leaves waitFirst before writeAccess is needed
266
    * </OL>
267
    */
268
    private class Addition
269
    implements TaskListener, FolderListListener, Runnable {
270
        static final long serialVersionUID =-4194617547214845940L;
271
272
        /** last time of addition */
273
        private long time = System.currentTimeMillis () + TIME_DELAY;
274
        /** delay */
275
        private int delay = TIME_DELAY;
276
277
        /** update the nodes during processing or only at the end */
278
        private boolean processingUpdate;
279
        
280
        /** a keys to be later refreshed */
281
        private List refKeys;
282
        
283
        // fix of #27025. We have to keep reference to all data objects
284
        // for the whole lifetime of this object otherwise it can happen
285
        // that a data object is GCed between calling refreshKeys and
286
        // createNodes
287
        private List dataObjects = new ArrayList();
288
        
289
        /** processing of DataFolder.computeChildrenList finished */
290
        private boolean processingFinished;
291
        
292
        /** a task that is used to request refreshing of keys */
293
        private RequestProcessor.Task refTask = refRP.create(this); // NOI18N
294
295
        /** @param processingUpdate update the nodes during
296
        *  processing or only at the end 
297
        */
298
        public Addition (boolean processingUpdate) {
299
            this.processingUpdate = processingUpdate;
300
        }
301
302
        /** Another object has been recognized.
303
         * @param obj the object recognized
304
         * @param arr array where the implementation should add the
305
         *    object
306
         */
307
        public void process(DataObject obj, java.util.List arr) {
308
            if (!filter.acceptDataObject (obj)) {
309
                return;
310
            }
311
312
            // first accepted object is notified to the waiting thread in
313
            boolean first = arr.isEmpty ();
314
            arr.add (obj);
315
            
316
            // see comment for the variable dataObjects
317
            dataObjects.add (obj);
318
319
            if (!processingUpdate) {
320
                // if we should not notify during processing update
321
                // skip the rest
322
                return;
323
            }
324
325
            if (first) {
326
                synchronized (this) {
327
                    this.notify ();
328
                    refreshKeys (arr);
329
                }
330
                return;
331
            }
332
333
            if (System.currentTimeMillis () > time) {
334
                if (!arr.isEmpty ()) {
335
                    // add the nodes
336
                    synchronized (this) {
337
                        refreshKeys (arr);
338
                    }
339
                    delay *= 2;
340
                }
341
342
                time = System.currentTimeMillis () + delay;
343
            }
344
        }
345
346
        /** All objects has been recognized.
347
         * @param arr list of DataObjects
348
         */
349
        public void finished(java.util.List arr) {
350
            synchronized (this) {
351
                this.notify ();
352
                this.processingFinished = true;
353
                // change the order because initialize method has already finished
354
                refreshKeys (arr);
355
            }
356
        }
357
358
        /** Getter for first map.
359
        */
360
        public synchronized void waitFirst () {
361
            try {
362
                this.wait (50);
363
            } catch (InterruptedException e) {
364
                throw new IllegalStateException();
365
            }
366
--
Lines 367-369 Link Here
367
205
        /** calls setKeys with the folder children 
368
        /** Called when a task finishes running.
206
         * or with empty collection if clear set to true.
369
         * @param task the finished task
370
--
Lines 371-395 Link Here
371
        public void taskFinished(Task task) {
372
            initTask = Task.EMPTY;
373
        }
374
        
375
        /** Refreshes the children.
376
        * @param ch collection of children data objects
377
        */
378
        private void refreshKeys (List ch) {
379
            if (err != null) err.log("refreshKeys: " + ch);
380
            ListIterator it = ch.listIterator ();
381
            LinkedList l = new LinkedList ();
382
383
            while (it.hasNext ()) {
384
                DataObject obj = (DataObject)it.next ();
385
                l.add (createKey (obj));
386
            }
387
388
            // remember the keys
389
            refKeys = l;
390
            // request method run to be called from request processor thread
391
            refTask.schedule (0);
392
        }
393
        
394
        /** Refreshes the keys in safe thread (that nobody should wait for)
395
        */
Lines 397-410 Link Here
397
            List toSet;
209
            if (clear) {
398
            synchronized (this) { // 1
210
                setKeys (java.util.Collections.EMPTY_SET);
399
                toSet = refKeys;
211
                return;
400
                refKeys = null;
401
            }
402
403
            if (toSet != null) {
404
                // toSet can be null if 
405
                //   1. a task blocks on synchronized(this) // 1
406
                //   2. refreshKeys calls refKey = <newvalue> + refTask.schedule
407
                //   3. a task uses the new value, but is already planned =>
408
                //   4. it will be run next time with toSet == null
409
                if (FolderChildren.this.err != null) FolderChildren.this.err.log("setKeys: " + toSet);
410
                FolderChildren.this.setKeys(toSet);
411
--
Lines 412-419 Link Here
412
            synchronized (this) {
213
            DataObject []ch = folder.getChildren();
413
                if (processingFinished) {
214
            Object []keys = new Object[ch.length];
414
                    if (FolderChildren.this.refreshTask == refTask) {
215
            for (int i = 0; i < keys.length; i++) {
415
                        if (err != null) err.log("fix for #30153 applied ");
216
                keys[i] = new Pair(ch[i].getPrimaryFile());
416
                        FolderChildren.this.refreshTask = Task.EMPTY;
417
//                        refTask.cancel();
418
                    }
419
                }
420
--
Line 421 Link Here
218
            setKeys(Arrays.asList(keys));
Line 423 Link Here
423
221
    
424
--
Line 425 Link Here
425
    */
223
     * It serves as a key for the given data object.
426
--
224
     * It is here to create something different then data object,
225
     * because the data object should be finalized when not needed and
226
     * that is why it should not be used as a key.
227
     */

Return to bug 30772