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

(-)src/org/openide/nodes/Children.java (-19 / +83 lines)
Lines 307-314 Link Here
307
    public final Node[] getNodes () {
307
    public final Node[] getNodes () {
308
        //Thread.dumpStack();
308
        //Thread.dumpStack();
309
        //System.err.println(off + "getNodes: " + getNode ());
309
        //System.err.println(off + "getNodes: " + getNode ());
310
        boolean[] results = new boolean[2];
310
        for (;;) {
311
        for (;;) {
311
            boolean initialized = isInitialized ();
312
            results[1] = isInitialized ();
313
            
314
            // initializes the ChildrenArray possibly calls 
315
            // addNotify if this is for the first time
316
            ChildrenArray array = getArray(results); // fils results[0]
317
            
312
            //System.err.println(off + "  initialized: " + initialized);
318
            //System.err.println(off + "  initialized: " + initialized);
313
            //      off = off + "  "; // NOI18N
319
            //      off = off + "  "; // NOI18N
314
            // forbid any modifications to this hierarchy
320
            // forbid any modifications to this hierarchy
Lines 316-322 Link Here
316
            try {
322
            try {
317
                PR.enterReadAccess ();
323
                PR.enterReadAccess ();
318
                
324
                
319
                nodes = computeNodes ();
325
                nodes = array.nodes ();
320
            } finally {
326
            } finally {
321
                PR.exitReadAccess ();
327
                PR.exitReadAccess ();
322
            }
328
            }
Lines 329-338 Link Here
329
            // we computed the nodes, somebody changed them (as a
335
            // we computed the nodes, somebody changed them (as a
330
            // result of addNotify) => we have to compute them
336
            // result of addNotify) => we have to compute them
331
            // again
337
            // again
332
            if (initialized) {
338
            if (results[1]) {
333
                // otherwise it is ok.
339
                // otherwise it is ok.
334
                return nodes;
340
                return nodes;
335
            }
341
            }
342
            
343
            if (results[0]) {
344
                // looks like the result cannot be computed, just give empty one
345
                return nodes == null ? new Node[0] : nodes;
346
            }
336
        }
347
        }
337
    }
348
    }
338
    
349
    
Lines 366-372 Link Here
366
    public Node[] getNodes(boolean optimalResult) {
377
    public Node[] getNodes(boolean optimalResult) {
367
        ChildrenArray arr;
378
        ChildrenArray arr;
368
        if (optimalResult) {
379
        if (optimalResult) {
369
            arr = getArray();
380
            arr = getArray(null);
370
            findChild(null);
381
            findChild(null);
371
        }
382
        }
372
        return getNodes();
383
        return getNodes();
Lines 419-440 Link Here
419
        return arr == null ? null : arr.nodes ();
430
        return arr == null ? null : arr.nodes ();
420
    }
431
    }
421
432
422
    /** Getter for list of nodes. Called from getNodes ().
433
    private static final Object LOCK = new Object ();
423
    * @return list of nodes associated with this object
424
    */
425
    final Node[] computeNodes () {
426
        return getArray ().nodes ();
427
    }
428
429
    /** Obtains references to array holder. If it does not exist, it is
434
    /** Obtains references to array holder. If it does not exist, it is
430
    * created.
435
    * created.
436
     *
437
     * @param cannotWorkBetter array of size 1 or null, will contain true, if 
438
     *    the getArray cannot be initialized (we are underread access
439
     *    and nother thread is responbile for initialization, in such case
440
     *    give up on computation of best result
431
    */
441
    */
432
    private ChildrenArray getArray () {
442
    private ChildrenArray getArray (boolean[] cannotWorkBetter) {
433
        ChildrenArray arr = (ChildrenArray)array.get ();
443
        ChildrenArray arr;
434
        if (arr == null) {
444
        boolean doInitialize = false;
435
            // create new array
445
        synchronized (LOCK) {
436
            arr = ChildrenArray.create (this);
446
             arr = (ChildrenArray)array.get ();
447
             if (arr == null) {
448
                 arr = new ChildrenArray ();
449
                 // register the array with the children
450
                 registerChildrenArray (arr, true);
451
                 doInitialize = true;
452
             }
453
        }
454
        
455
        if (doInitialize) {
456
            // this call can cause a lot of callbacks => be prepared
457
            // to handle them as clean as possible
458
            this.callAddNotify ();
459
460
            synchronized (LOCK) {
461
                // now attach to children, so when children == null => we are
462
                // not fully initialized!!!!
463
                arr.children = this;
464
                LOCK.notifyAll ();
465
            }
466
        } else {
467
            // otherwise, if not initialize yet (arr.children) wait 
468
            // for the initialization to finish, but only if we can wait
469
            
470
            // we are not in ReadAccess
471
            // Children.MUTEX.isReadAccess, if such call would be added to Mutex
472
            class MutexChecker implements Runnable {
473
                public boolean inReadAccess = true;
474
                public void run () {
475
                    inReadAccess = false;
476
                }
477
            }
478
            MutexChecker test = new MutexChecker();
479
            // the code will run either immediatally or after we leave readAccess
480
            // section
481
            Children.MUTEX.postWriteRequest(test);
482
483
            if (test.inReadAccess) {
484
                // fail, we are in read access
485
                if (cannotWorkBetter != null) {
486
                    cannotWorkBetter[0] = true;
487
                }
488
                return arr;
489
            }
490
            
491
            // otherwise we can wait
492
            synchronized (LOCK) {
493
                while (arr.children == null) {
494
                    try {
495
                        LOCK.wait ();
496
                    } catch (InterruptedException ex) {
497
                    }
498
                }
499
            }
437
        }
500
        }
501
                    
438
        return arr;
502
        return arr;
439
    }
503
    }
440
504
Lines 1000-1013 Link Here
1000
1064
1001
        public Collection nodes () {
1065
        public Collection nodes () {
1002
            // forces creation of the array
1066
            // forces creation of the array
1003
            ChildrenArray arr = getArray ();
1067
            ChildrenArray arr = getArray (null);
1004
1068
1005
            return arr.nodesFor (this);
1069
            return arr.nodesFor (this);
1006
        }
1070
        }
1007
1071
1008
        public void useNodes (Collection nodes) {
1072
        public void useNodes (Collection nodes) {
1009
            // forces creation of the array
1073
            // forces creation of the array
1010
            ChildrenArray arr = getArray ();
1074
            ChildrenArray arr = getArray (null);
1011
1075
1012
            arr.useNodes (this, nodes);
1076
            arr.useNodes (this, nodes);
1013
1077
Lines 1152-1158 Link Here
1152
        final void refreshImpl () {
1216
        final void refreshImpl () {
1153
            if ( isInitialized() ) {
1217
            if ( isInitialized() ) {
1154
                Array.this.refreshEntry (getNodesEntry ());
1218
                Array.this.refreshEntry (getNodesEntry ());
1155
                super.computeNodes ();
1219
                super.getArray (null).nodes ();
1156
            }
1220
            }
1157
            else if ( nodes != null ) {
1221
            else if ( nodes != null ) {
1158
                for( Iterator it = nodes.iterator(); it.hasNext(); ) {
1222
                for( Iterator it = nodes.iterator(); it.hasNext(); ) {
(-)src/org/openide/nodes/ChildrenArray.java (-21 / +2 lines)
Lines 22-35 Link Here
22
*/
22
*/
23
final class ChildrenArray extends NodeAdapter {
23
final class ChildrenArray extends NodeAdapter {
24
    /** children */
24
    /** children */
25
    private Children children;
25
    public  Children children;
26
    /** nodes associated */
26
    /** nodes associated */
27
    private Node[] nodes;
27
    private Node[] nodes;
28
    /** mapping from the (Children.Info, Collection (Node)) */
28
    /** mapping from the (Children.Info, Collection (Node)) */
29
    private WeakHashMap map;
29
    private WeakHashMap map;
30
30
31
    /** Creates new ChildrenArray */
31
    /** Creates new ChildrenArray */
32
    private ChildrenArray () {
32
    public ChildrenArray () {
33
    }
33
    }
34
    
34
    
35
    public Children getChildren () {
35
    public Children getChildren () {
Lines 40-64 Link Here
40
    */
40
    */
41
    protected void finalize () {
41
    protected void finalize () {
42
        children.finalizedChildrenArray ();
42
        children.finalizedChildrenArray ();
43
    }
44
45
    /** Create new instance of this object attached to the children.
46
    */
47
    public static ChildrenArray create (Children ch) {
48
        ChildrenArray a = new ChildrenArray ();
49
50
        // register the array with the children
51
        ch.registerChildrenArray (a, true);
52
53
        // this call can cause a lot of callbacks => be prepared
54
        // to handle them as clean as possible
55
        ch.callAddNotify ();
56
57
        // now attach to children, so when children == null => we are
58
        // not fully initialized!!!!
59
        a.children = ch;
60
61
        return a;
62
    }
43
    }
63
44
64
    /** Getter method to receive a set of computed nodes.
45
    /** Getter method to receive a set of computed nodes.
(-)src/org/openide/explorer/view/VisualizerNode.java (-1 / +3 lines)
Lines 165-171 Link Here
165
    public List getChildren () {
165
    public List getChildren () {
166
        VisualizerChildren ch = (VisualizerChildren)children.get ();
166
        VisualizerChildren ch = (VisualizerChildren)children.get ();
167
        if (ch == null && !node.isLeaf ()) {
167
        if (ch == null && !node.isLeaf ()) {
168
168
            // initialize the nodes children before we enter
169
            // the readAccess section
170
            Node[] tmpInit = node.getChildren ().getNodes ();
169
            // go into lock to ensure that no childrenAdded, childrenRemoved,
171
            // go into lock to ensure that no childrenAdded, childrenRemoved,
170
            // childrenReordered notifications occures and that is why we do
172
            // childrenReordered notifications occures and that is why we do
171
            // not loose any changes
173
            // not loose any changes
(-)test/unit/src/org/openide/nodes/ChildrenKeysTest.java (-1 / +120 lines)
Lines 124-134 Link Here
124
            }
124
            }
125
        }
125
        }
126
    }
126
    }
127
    
128
    public void testProperInitializationEvenIfInvokedFromMultipleThreadsBug30907SlowAddNotifyWithReadAccess () throws Exception {
129
        doBug30907 (true, true, 0, 2);
130
    }
131
    
132
    public void testProperInitializationEvenIfInvokedFromMultipleThreadsBug30907QuickAddNotifyWithReadAccess () throws Exception {
133
        doBug30907 (false, true, 0, 2);
134
    }
135
    
136
    public void testProperInitializationEvenIfInvokedFromMultipleThreadsBug30907QuickAddNotify () throws Exception {
137
        doBug30907 (false, false, 2, 2);
138
    }
139
    public void testProperInitializationEvenIfInvokedFromMultipleThreadsBug30907SlowAddNotify () throws Exception {
140
        doBug30907 (true, false, 2, 2);
141
    }
142
    
143
    public void doBug30907 (final boolean slowAddNotify, boolean readAccess, int mainCount, int threadCount) throws Exception {
144
        // we have to garbage called all objects otherwise they won't let us into 
145
        // read access 
146
        for (int i = 0; i < 10; i++) {
147
            System.gc ();
148
            System.runFinalization();
149
        }
150
        
151
        
152
        //
153
        // the purpose of this test is to create a livelock - execution never ends
154
        // as described in the bug
155
        //
156
        
157
        
158
        final Node node[] = { null };
159
        
160
        final Object LOCK = new Object ();
161
        
162
        class K extends Keys implements Runnable {
163
            private String[] arr;
164
            
165
            public K (String[] arr) {
166
                this.arr = arr;
167
            }
168
            
169
            public void addNotify () {
170
                if (slowAddNotify) {
171
                    // let the main thread run
172
                    synchronized (LOCK) {
173
                        LOCK.notify (); // to N1
174
                    }
175
176
                    // and wait a while it reaches getNodes
177
                    try {
178
                        Thread.sleep (1000);
179
                    } catch (InterruptedException ex) {
180
                        fail ("Exception");
181
                    }
182
                }
183
                
184
                setKeys (arr);
185
            }
186
            
187
            Node[] result;
188
            public void run () {
189
                // forces initialization
190
                Node[] arr = node[0].getChildren ().getNodes ();
191
192
                if (!slowAddNotify) {
193
                    // qucik addNotify => notify the main thread to run after the 
194
                    // finish of getNodes
195
                    synchronized (LOCK) {
196
                        LOCK.notify (); // to N1
197
                    }
198
                }
199
                
200
                synchronized (LOCK) {
201
                    result = arr;
202
                    LOCK.notify (); // to N2
203
                }
204
            }
205
        }
206
207
        K k = new K (new String[] { "1", "2" });
208
        node[0] = new FilterNode (new AbstractNode (k));
209
        
210
        Node[] result;
211
        synchronized (LOCK) {
212
            try {
213
                if (readAccess) {
214
                    Children.PR.enterReadAccess ();
215
                }
216
217
                Thread t = new Thread (k, "testProperInitializationEvenIfInvokedFromMultipleThreadsBug30907Thread");
218
                t.setDaemon(true);
219
                t.start ();
220
221
                if (!readAccess) {
222
                    LOCK.wait (); // from N1
223
                }
224
225
                result = node[0].getChildren ().getNodes ();
226
                assertNotNull("Get nodes cannot return null", result);
227
                assertEquals ("Returns proper value for children as it waits until addNotify finishes", mainCount, result.length);
228
            } finally {   
229
                if (readAccess) {
230
                    Children.PR.exitReadAccess ();
231
                }
232
            }
233
            
234
            if (readAccess) {
235
                LOCK.wait (); // from N1
236
            }
237
            
238
            // finish the work in thread
239
            while (k.result == null) {
240
                LOCK.wait (); // from N2
241
            }
242
        }
243
        
244
        assertEquals ("Two children there even in the initialization thread", threadCount, k.result.length);
245
    }
127
246
128
247
129
    /** Sample keys.
248
    /** Sample keys.
130
    */
249
    */
131
    private static final class Keys extends Children.Keys {
250
    private static class Keys extends Children.Keys {
132
        public Keys () {
251
        public Keys () {
133
        }
252
        }
134
        
253
        

Return to bug 30907