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

(-)src/org/openide/loaders/Bundle.properties (+2 lines)
Line 200 Link Here
200
201
LBL_Wait=Please wait...
(-)src/org/openide/loaders/FolderChildren.java (-304 / +54 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
47
45
    /** this is true between addNotify and removeNotify */
48
--
46
    private boolean active;
47
    
48
    /** Private req processor for the refresh tasks */
49
    private static RequestProcessor refRP = 
50
        new RequestProcessor("FolderChildren_Refresh"); // NOI18N
51
    
Line 80 Link Here
80
            refreshChildren ();
84
            refreshChildren();
81
--
Line 88 Link Here
88
            refreshChildren ();
92
            refreshChildren();
89
--
Lines 95-118 Link Here
95
    void refreshChildren () {
99
    Task refreshChildren() {
96
        initialize (true, false);
100
        return refRP.post(new ChildrenRefreshRunnable());
97
    }
98
99
    /** Creates a key for given data object.
100
    * This method is here to create something different then data object,
101
    * because the data object should be finalized when not needed and
102
    * that is why it should not be used as a key.
103
    *
104
    * @param obj data object
105
    * @return key representing the data object.
106
    */
107
    static Object createKey (DataObject obj) {
108
        return new Pair (obj.getPrimaryFile ());
109
    }
110
111
    /** This method takes the key created by createKey and converts it
112
    * into primary file.
113
    *
114
    * @param key the key
115
    * @return primary file of the key
116
    */
117
    private static FileObject getFile (Object key) {
118
        return ((Pair)key).primaryFile;
119
--
Line 126 Link Here
126
        FileObject fo = getFile (key);
108
        FileObject fo = ((Pair)key).primaryFile;
127
--
Line 130 Link Here
130
Lines 141-183 Link Here
141
122
  
142
    /** Improves the searching capability to wait till all children
123
    public Node[] getNodes(boolean optimalResult) {
143
    * are found and then searching thru them.
124
        if (optimalResult) {
144
    */
125
            active = true;
145
    public Node findChild (final String name) {
126
            Task t = refreshChildren();
146
        // start the initialization
127
            t.waitFinished();
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
        
129
        return getNodes();
186
        // do the regular child lookup
187
        Node node = super.findChild (name);
188
        return node;
189
--
Line 190 Link Here
190
131
    
191
--
Line 194 Link Here
194
        initialize (true, true);
Line 197 Link Here
137
        // 
138
        active = true;
139
        // start the refresh task to compute the children
140
        refreshChildren();
Lines 204-243 Link Here
204
        setKeys (java.util.Collections.EMPTY_SET);
148
        //
205
    }
149
        active = false;
206
150
        // we don't call the setKeys directly here because
207
    /** Ensures that the content of children will be filled sometime.
151
        // there can be a task spawned by refreshChildren - so
208
    * @param force true if the content should be filled immediatelly
152
        // we want to clear the children after that task is finished
209
    */
153
        refreshChildren();
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;
235
        
236
        initTask = t = folder.computeChildrenList (add);
237
        t.addTaskListener (add);
238
239
        if (waitFirst) {
240
            add.waitFirst ();
241
        }
242
        
243
        return t;
244
--
Lines 250-256 Link Here
250
251
    /** time delay between two inserts of nodes */
252
    private static final int TIME_DELAY = 1024;
253
254
    /** Private req processor for Addition's refresh tasks */
255
    private static RequestProcessor refRP = 
256
        new RequestProcessor("FolderChildren_refresher");
Lines 258-369 Link Here
258
    /** Support for incremental adding of new nodes.
161
    /**
259
    *
162
     * Instances of this class are posted to the request processor refRP
260
    * <P>
163
     * (FolderChildren_refresher). We do this because we do not want
261
    * There is a deadlock warning:
164
     * to call setKeys synchronously.
262
    * <OL>
165
     */
263
    *   <LI>A thread waiting in the waitFirst method can have MUTEX.readAccess
166
    private final class ChildrenRefreshRunnable implements Runnable {
264
    *   <LI>Thread running run () needs access to MUTEX.writeAccess (because of setKeys)
167
        /** calls setKeys with the folder children 
265
    *   <LI>Be sure that the thread leaves waitFirst before writeAccess is needed
168
         * or with empty collection if active is false
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
        }
367
368
        /** Called when a task finishes running.
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;
171
            if (! active) {
398
            synchronized (this) { // 1
172
                setKeys (java.util.Collections.EMPTY_SET);
399
                toSet = refKeys;
173
                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) {
175
            DataObject []ch = folder.getChildren();
413
                if (processingFinished) {
176
            Object []keys = new Object[ch.length];
414
                    if (FolderChildren.this.refreshTask == refTask) {
177
            for (int i = 0; i < keys.length; i++) {
415
                        if (err != null) err.log("fix for #30153 applied ");
178
                keys[i] = new Pair(ch[i].getPrimaryFile());
416
                        FolderChildren.this.refreshTask = Task.EMPTY;
417
//                        refTask.cancel();
418
                    }
419
                }
420
--
Line 421 Link Here
180
            setKeys(Arrays.asList(keys));
Line 423 Link Here
423
183
    
424
--
Line 425 Link Here
425
    */
185
     * It serves as a key for the given data object.
426
--
186
     * It is here to create something different then data object,
187
     * because the data object should be finalized when not needed and
188
     * that is why it should not be used as a key.
189
     */

Return to bug 30772