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 |
*/ |