001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.modelimpl.csm.core;
043:
044: import java.util.*;
045: import org.netbeans.modules.cnd.api.model.CsmProject;
046: import org.netbeans.modules.cnd.apt.support.APTPreprocHandler;
047: import org.netbeans.modules.cnd.modelimpl.debug.Diagnostic;
048: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
049:
050: /**
051: * A queue that hold a list of files to parse.
052: * @author Vladimir Kvashin
053: */
054: public final class ParserQueue {
055:
056: public static class Entry {
057:
058: private FileImpl file;
059: private APTPreprocHandler.State ppState;
060: private Entry prev;
061: private Entry next;
062:
063: private Entry(FileImpl file, APTPreprocHandler.State ppState) {
064: if (TraceFlags.TRACE_PARSER_QUEUE) {
065: System.err.println("creating entry for "
066: + file.getAbsolutePath() + " as "
067: + tracePreprocState(ppState)); // NOI18N
068: }
069: this .file = file;
070: this .ppState = ppState;
071: }
072:
073: public FileImpl getFile() {
074: return file;
075: }
076:
077: public APTPreprocHandler.State getPreprocState() {
078: return ppState;
079: }
080:
081: @Override
082: public String toString() {
083: return toString(true);
084: }
085:
086: public String toString(boolean detailed) {
087: StringBuilder retValue = new StringBuilder();
088: retValue.append("ParserQueue.Entry " + file
089: + " of project " + file.getProject()); // NOI18N
090: if (detailed) {
091: retValue.append("\nwith PreprocState:\n" + ppState); // NOI18N
092: }
093: return retValue.toString();
094: }
095:
096: public void setPreprocStateIfNeed(
097: APTPreprocHandler.State ppState) {
098: // TODO: IZ#87204: AssertionError on _Bvector_base opening
099: // review why it could be null
100: // FIXUP: remove assert checks and update if statements to prevent NPE
101: // assert (ppState != null) : "why do pass null snapshot?";
102: // assert (this.ppState != null) : "if it was already included, where is the state?";
103:
104: if (TraceFlags.TRACE_PARSER_QUEUE) {
105: System.err.println("setPreprocStateIfNeed for "
106: + file.getAbsolutePath() + " as "
107: + tracePreprocState(ppState) + " with current "
108: + tracePreprocState(this .ppState)); // NOI18N
109: }
110: if (file.isNeedReparse(this .ppState, ppState)) {
111: this .ppState = ppState;
112: }
113: }
114: }
115:
116: /*package*/static String tracePreprocState(
117: APTPreprocHandler.State ppState) {
118: if (ppState == null) {
119: return "null"; // NOI18N
120: } else {
121: StringBuilder msg = new StringBuilder("["); // NOI18N
122: if (!ppState.isCleaned()) {
123: msg.append("not"); // NOI18N
124: }
125: msg.append(" cleaned, "); // NOI18N
126: if (!ppState.isValid()) {
127: msg.append("not"); // NOI18N
128: }
129: msg.append(" valid, "); // NOI18N
130: if (!ppState.isCompileContext()) {
131: msg.append("not"); // NOI18N
132: }
133: msg.append(" correct State]"); // NOI18N
134: return msg.toString();
135: }
136: }
137:
138: private static class Queue {
139:
140: private Map<FileImpl, Entry> storage = new HashMap<FileImpl, Entry>();
141: private Entry head;
142: private Entry tail;
143:
144: private void link(Entry e1, Entry e2) {
145: if (e1 != null) {
146: e1.next = e2;
147: }
148: if (e2 != null) {
149: e2.prev = e1;
150: }
151: }
152:
153: public void addFirst(Entry e) {
154: Entry old = storage.put(e.file, e);
155: assert old == null : "only one entry per file must be in queue "
156: + e.file;
157: link(e, head);
158: head = e;
159: if (tail == null) {
160: tail = head;
161: }
162: e.prev = null;
163: }
164:
165: public void addLast(Entry e) {
166: if (tail == null) {
167: assert head == null;
168: addFirst(e);
169: } else {
170: Entry old = storage.put(e.file, e);
171: assert old == null : "only one entry per file must be in queue "
172: + e.file;
173: link(tail, e);
174: tail = e;
175: }
176: e.next = null;
177: }
178:
179: public void remove(Entry e) {
180: Entry old = storage.remove(e.file);
181: assert old != null : "there were no entry in queue for file "
182: + e.file;
183: link(e.prev, e.next);
184: if (head == e) {
185: head = e.next;
186: }
187: if (tail == e) {
188: tail = e.prev;
189: }
190: // clear enthry in case someone holds a reference to it
191: e.prev = e.next = null;
192: }
193:
194: public void removeAll(Collection/*<FileImple>*/files) {
195: // for( Entry curr = head; curr != null; curr = curr.next ) {
196: // if( files.contains(curr.getFile()) ) {
197: // remove(curr);
198: // }
199: // }
200: Entry curr = head;
201: while (curr != null) {
202: Entry next = curr.next; // after removal curr.next will be null!
203: if (files.contains(curr.getFile())) {
204: remove(curr);
205: }
206: curr = next;
207: }
208: }
209:
210: public void clear() {
211: storage.clear();
212: head = tail = null;
213: }
214:
215: public Entry peek() {
216: return head;
217: }
218:
219: public boolean isEmpty() {
220: return head == null;
221: }
222:
223: public Entry find(FileImpl file) {
224: return storage.get(file);
225: // for( Entry curr = head; curr != null; curr = curr.next ) {
226: // if( curr.getFile() == file ) {
227: // return curr;
228: // }
229: // }
230: // return null;
231: }
232:
233: public Entry poll() {
234: Entry ret = head;
235: if (head != null) {
236: remove(head);
237: }
238: return ret;
239: }
240:
241: public String toString(boolean detailed) {
242: StringBuilder builder = new StringBuilder();
243: Entry cur = head;
244: while (cur != null) {
245: builder.append(cur.toString(detailed)).append("\n");
246: cur = cur.next;
247: }
248: return builder.toString();
249: }
250: }
251:
252: private static final class ProjectData {
253:
254: public Set/*<FileImpl>*/filesInQueue = new HashSet/*<FileImpl>*/();
255:
256: // there are no more simultaneously parsing files than threads, so LinkedList suites even better
257: public Collection/*<FileImpl>*/filesBeingParsed = new LinkedList/*<FileImpl>*/();
258:
259: public boolean notifyListeners;
260:
261: ProjectData(boolean notifyListeners) {
262: this .notifyListeners = notifyListeners;
263: }
264:
265: public boolean isEmpty() {
266: return filesInQueue.isEmpty() && filesBeingParsed.isEmpty();
267: }
268:
269: public int size() {
270: return filesInQueue.size();
271: }
272: }
273:
274: private static enum State {
275: ON, OFF, SUSPENDED
276: }
277:
278: private static ParserQueue instance = new ParserQueue();
279:
280: private Queue queue = new Queue();
281:
282: private State state;
283: private Object suspendLock = new String("suspendLock"); // NOI18N
284:
285: // do not need UIDs for ProjectBase in parsing data collection
286: private Map<ProjectBase, ProjectData> projectData = new HashMap<ProjectBase, ProjectData>();
287: private Map<CsmProject, Object> projectLocks = new HashMap<CsmProject, Object>();
288:
289: private Object lock = new Object();
290:
291: //private WeakList<CsmProgressListener> progressListeners = new WeakList<CsmProgressListener>();
292:
293: private Diagnostic.StopWatch stopWatch = TraceFlags.TIMING ? new Diagnostic.StopWatch(
294: false)
295: : null;
296:
297: private ParserQueue() {
298: }
299:
300: public static ParserQueue instance() {
301: return instance;
302: }
303:
304: /**
305: * Puts the given file at the end of the queue
306: * (In the case it isn't already enqueued;
307: * if it already is, does nothing)
308: */
309: // public void addLast(FileImpl file) {
310: // addLast(file, file.getPreprocState());
311: // }
312: // public void addLast(FileImpl file, APTPreprocHandler.State ppState, boolean onInclude) {
313: // if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("ParserQueue: addLast " + file.getName());
314: // synchronized ( lock ) {
315: // Set/*<FileImpl>*/ files = getProjectFiles(file.getProjectImpl());
316: // if( ! files.contains(file) ) {
317: // files.add(file);
318: // //queue.add(file);
319: // Entry entry = new Entry(file, ppState, onInclude);
320: // if( TraceFlags.TRACE_PARSER_QUEUE ) System.err.println("ParserQueue: added as Last with entry " + entry);
321: // queue.addLast(entry);
322: // lock.notifyAll();
323: // }
324: // }
325: // }
326: public void addLast(FileImpl file, APTPreprocHandler.State ppState) {
327: if (TraceFlags.TRACE_PARSER_QUEUE)
328: System.err.println("ParserQueue: addLast "
329: + file.getAbsolutePath());
330: synchronized (lock) {
331: if (state == State.OFF)
332: return;
333: if (!needEnqueue(file)) {
334: if (TraceFlags.TRACE_PARSER_QUEUE)
335: System.err
336: .println("ParserQueue: do not addLast for parsing or parsed "
337: + file.getAbsolutePath());
338: return;
339: }
340: Set/*<FileImpl>*/files = getProjectFiles(file
341: .getProjectImpl());
342: Entry entry = null;
343: if (files.contains(file)) {
344: entry = queue.find(file); //TODO: think over / profile, probably this line is expensive
345: if (entry != null) {
346: entry.setPreprocStateIfNeed(ppState);
347: }
348: } else {
349: assert (queue.find(file) == null) : "The queue should not contain the file "
350: + traceState4File(file, files);// NOI18N
351: files.add(file);
352: }
353: if (entry == null) {
354: entry = new Entry(file, ppState);
355: if (TraceFlags.TRACE_PARSER_QUEUE)
356: System.err
357: .println("ParserQueue: added as Last with entry "
358: + entry
359: .toString(TraceFlags.TRACE_PARSER_QUEUE_DETAILS));
360: queue.addLast(entry);
361: }
362: lock.notifyAll();
363: }
364: ProgressSupport.instance().fireFileInvalidated(file);
365: }
366:
367: private String traceState4File(FileImpl file,
368: Set/*<FileImpl>*/files) {
369: StringBuilder builder = new StringBuilder(" "); // NOI18N
370: builder.append(file);
371: builder.append("\n of project ").append(file.getProjectImpl()); // NOI18N
372: builder.append("\n content of projects files set:\n"); // NOI18N
373: builder.append(files);
374: builder.append("\nqueue content is:\n"); // NOI18N
375: builder.append(queue.toString(false));
376: builder.append("\nprojectData content is:\n"); // NOI18N
377: builder.append(projectData);
378: return builder.toString();
379: }
380:
381: /**
382: * If file isn't yet enqueued, places it at the beginning of the queue,
383: * otherwise moves it there
384: */
385: // public void addFirst(FileImpl file) {
386: // addFirst(file, file.getPreprocState(), false);
387: // }
388: public void addFirst(FileImpl file,
389: APTPreprocHandler.State ppState, boolean onInclude) {
390: if (TraceFlags.TRACE_PARSER_QUEUE)
391: System.err.println("ParserQueue: addFirst "
392: + file.getAbsolutePath());
393: synchronized (lock) {
394: if (state == State.OFF)
395: return;
396: if (!needEnqueue(file)) {
397: if (TraceFlags.TRACE_PARSER_QUEUE)
398: System.err
399: .println("ParserQueue: do not addFirst for parsing or parsed "
400: + file.getAbsolutePath());
401: return;
402: }
403: Set/*<FileImpl>*/files = getProjectFiles(file
404: .getProjectImpl());
405: Entry entry = null;
406: if (files.contains(file)) {
407: entry = queue.find(file); //TODO: think over / profile, probably this line is expensive
408: if (entry != null) {
409: queue.remove(entry);
410: entry.setPreprocStateIfNeed(ppState);
411: }
412: } else {
413: assert (queue.find(file) == null) : "The queue should not contain the file "
414: + traceState4File(file, files); // NOI18N
415: files.add(file);
416: }
417: if (entry == null) {
418: entry = new Entry(file, ppState);
419: }
420: if (TraceFlags.TRACE_PARSER_QUEUE)
421: System.err
422: .println("ParserQueue: added as First with entry "
423: + entry
424: .toString(TraceFlags.TRACE_PARSER_QUEUE_DETAILS));
425: queue.addFirst(entry);
426: lock.notifyAll();
427: }
428: ProgressSupport.instance().fireFileInvalidated(file);
429: }
430:
431: public void waitReady() throws InterruptedException {
432: if (TraceFlags.TRACE_PARSER_QUEUE)
433: System.err.println("ParserQueue: waitReady() ...");
434: synchronized (lock) {
435: while (queue.isEmpty() && state != State.OFF) {
436: lock.wait();
437: }
438: }
439: if (TraceFlags.TRACE_PARSER_QUEUE)
440: System.err.println("ParserQueue: waiting finished");
441: }
442:
443: public void suspend() {
444: if (TraceFlags.TRACE_PARSER_QUEUE)
445: System.err.println("ParserQueue: suspending");
446: synchronized (suspendLock) {
447: state = State.SUSPENDED;
448: }
449: }
450:
451: public void resume() {
452: if (TraceFlags.TRACE_PARSER_QUEUE)
453: System.err.println("ParserQueue: resuming");
454: synchronized (suspendLock) {
455: state = State.ON;
456: suspendLock.notifyAll();
457: }
458: }
459:
460: public Entry poll() throws InterruptedException {
461:
462: synchronized (suspendLock) {
463: while (state == State.SUSPENDED) {
464: if (TraceFlags.TRACE_PARSER_QUEUE)
465: System.err
466: .println("ParserQueue: waiting for resume");
467: suspendLock.wait();
468: }
469: }
470:
471: Entry e = null;
472:
473: ProjectBase project;
474: boolean lastFileInProject;
475: boolean notifyListeners;
476:
477: FileImpl file = null;
478:
479: synchronized (lock) {
480: e = queue.poll();
481: if (e == null) {
482: return null;
483: }
484: file = e.getFile();
485: project = file.getProjectImpl();
486: ProjectData data = getProjectData(project, true);
487: data.filesInQueue.remove(file);
488: data.filesBeingParsed.add(file);
489: lastFileInProject = data.filesInQueue.isEmpty()
490: && data.filesBeingParsed.isEmpty();
491: if (lastFileInProject) {
492: removeProjectData(project);
493: }
494: notifyListeners = lastFileInProject && data.notifyListeners;
495: if (TraceFlags.TIMING && stopWatch != null
496: && !stopWatch.isRunning()) {
497: stopWatch.start();
498: System.err
499: .println("=== Starting parser queue stopwatch");
500: }
501: }
502: // TODO: think over, whether this should be under if( notifyListeners
503: ProgressSupport.instance().fireFileParsingStarted(file);
504: if (lastFileInProject) {
505: project.onParseFinish();
506: if (notifyListeners) {
507: ProgressSupport.instance().fireProjectParsingFinished(
508: project);
509: }
510: }
511: if (TraceFlags.TRACE_PARSER_QUEUE)
512: System.err.println("ParserQueue: polling "
513: + e.getFile().getAbsolutePath());
514: return e;
515: }
516:
517: public void remove(FileImpl file) {
518:
519: ProjectBase project;
520: boolean lastFileInProject = false;
521: boolean notifyListeners = false;
522:
523: synchronized (lock) {
524: project = file.getProjectImpl();
525: ProjectData data = getProjectData(project, true);
526: if (data.filesInQueue.contains(file)) {
527: //queue.remove(file); //TODO: think over / profile, probably this line is expensive
528: Entry e = queue.find(file);//TODO: think over / profile, probably this line is expensive
529: if (e != null) {
530: queue.remove(e);
531: }
532: data.filesInQueue.remove(file);
533: lastFileInProject = data.filesInQueue.isEmpty()
534: && data.filesBeingParsed.isEmpty();
535: if (lastFileInProject) {
536: removeProjectData(project);
537: }
538: notifyListeners = lastFileInProject
539: && data.notifyListeners;
540: }
541: }
542:
543: if (lastFileInProject) {
544: project.onParseFinish();
545: if (notifyListeners) {
546: ProgressSupport.instance().fireProjectParsingFinished(
547: project);
548: }
549: }
550: }
551:
552: public void shutdown() {
553: if (TraceFlags.TRACE_PARSER_QUEUE)
554: System.err.println("ParserQueue: clearing");
555: Collection<ProjectBase> copiedProjects = null;
556: synchronized (lock) {
557: state = State.OFF;
558: queue.clear();
559: copiedProjects = new ArrayList<ProjectBase>(projectData
560: .keySet());
561: lock.notifyAll();
562: }
563: for (ProjectBase prj : copiedProjects) {
564: ProgressSupport.instance().fireProjectParsingFinished(prj);
565: }
566: }
567:
568: public void startup() {
569: state = State.ON;
570: }
571:
572: public void removeAll(ProjectBase project) {
573: ProjectData data;
574: boolean lastFileInProject;
575: synchronized (lock) {
576: data = getProjectData(project, true);
577: queue.removeAll(data.filesInQueue);
578: data.filesInQueue.clear();
579: lastFileInProject = data.filesBeingParsed.isEmpty();
580: if (lastFileInProject) {
581: removeProjectData(project);
582: }
583: }
584: if (lastFileInProject) {
585: project.onParseFinish();
586: if (data.notifyListeners) {
587: ProgressSupport.instance().fireProjectParsingFinished(
588: project);
589: }
590: }
591: }
592:
593: /**
594: * Determines whether any files of the given project are now being parsed
595: * @return true if any files of the project are being parsed, otherwise false
596: */
597: public boolean isParsing(ProjectBase project) {
598: synchronized (lock) {
599: ProjectData data = getProjectData(project, false);
600: if (data != null) {
601: return !data.filesBeingParsed.isEmpty();
602: }
603: }
604: return false;
605: }
606:
607: public boolean hasFiles(ProjectBase project, FileImpl skipFile) {
608: return hasFiles(project, skipFile, true);
609: }
610:
611: private boolean hasFiles(ProjectBase project, FileImpl skipFile,
612: boolean create) {
613: synchronized (lock) {
614: ProjectData data = getProjectData(project, create);
615: if (data == null || data.isEmpty()) {
616: // nothing in queue and nothing in progress => no files
617: return false;
618: } else {
619: if (skipFile == null) {
620: // not empty, but nothing to skip => has files
621: return true;
622: } else {
623: if (data.filesBeingParsed.contains(skipFile)
624: || data.filesInQueue.contains(skipFile)) {
625: return data.filesBeingParsed.size()
626: + data.filesInQueue.size() > 1;
627: }
628: return !data.isEmpty();
629: }
630: }
631: }
632: }
633:
634: private Set/*<FileImpl>*/getProjectFiles(ProjectBase project) {
635: return getProjectData(project, true).filesInQueue;
636: }
637:
638: private ProjectData getProjectData(ProjectBase project,
639: boolean create) {
640: // must be in synchronized( lock ) block
641: synchronized (lock) {
642: ProjectBase key = project;
643: ProjectData data = projectData.get(key);
644: if (data == null && create) {
645: data = new ProjectData(false);
646: projectData.put(key, data);
647: }
648: return data;
649: }
650: }
651:
652: private void removeProjectData(ProjectBase project) {
653: // must be in synchronized( lock ) block
654: synchronized (lock) {
655: projectData.remove(project);
656: }
657: }
658:
659: private boolean needEnqueue(FileImpl file) {
660: // we know, that each file is parsed only once =>
661: // let's speed up work with queue ~75% by skipping such files
662: // Also check that file project was not closed
663: return !file.isParsed() && !file.getProjectImpl().isDisposing();
664: }
665:
666: public void onStartAddingProjectFiles(ProjectBase project) {
667: getProjectData(project, true).notifyListeners = true;
668: ProgressSupport.instance().fireProjectParsingStarted(project);
669: }
670:
671: public void onEndAddingProjectFiles(ProjectBase project) {
672: int cnt = getProjectFiles(project).size();
673: ProgressSupport.instance()
674: .fireProjectFilesCounted(project, cnt);
675: if (cnt == 0) {
676: ProgressSupport.instance().fireProjectParsingFinished(
677: project);
678: }
679: }
680:
681: /*package*/void onFileParsingFinished(FileImpl file) {
682: boolean lastFileInProject;
683: boolean idle = false;
684: ProjectBase project;
685: ProjectData data;
686: synchronized (lock) {
687: project = file.getProjectImpl();
688: data = getProjectData(project, true);
689: data.filesBeingParsed.remove(file);
690: lastFileInProject = data.filesInQueue.isEmpty()
691: && data.filesBeingParsed.isEmpty();
692: if (lastFileInProject) {
693: removeProjectData(project);
694: idle = projectData.isEmpty();
695: // this work only for a single project
696: // but on the other hand in the case of multiple projects such measuring will never work
697: // since project files might be shuffled in queue
698: if (TraceFlags.TIMING && stopWatch != null
699: && stopWatch.isRunning()) {
700: stopWatch
701: .stopAndReport("=== Stopping parser queue stopwatch: \t"); // NOI18N
702: }
703: }
704: }
705: ProgressSupport.instance().fireFileParsingFinished(file);
706: if (lastFileInProject) {
707: if (TraceFlags.TRACE_CLOSE_PROJECT)
708: System.err.println("Last file in project "
709: + project.getName());
710: project.onParseFinish();
711: if (data.notifyListeners) {
712: ProgressSupport.instance().fireProjectParsingFinished(
713: project);
714: }
715: if (idle) {
716: ProgressSupport.instance().fireIdle();
717: }
718: // notify all "wait" empty listeners
719: notifyWaitEmpty(project);
720: }
721: }
722:
723: private void notifyWaitEmpty(ProjectBase project) {
724: Object prjWaitEmptyLock;
725: synchronized (projectLocks) {
726: prjWaitEmptyLock = projectLocks.remove(project);
727: }
728: if (prjWaitEmptyLock != null) {
729: synchronized (prjWaitEmptyLock) {
730: prjWaitEmptyLock.notifyAll();
731: }
732: }
733: }
734:
735: /*package*/void waitEmpty(ProjectBase project) {
736: if (TraceFlags.TRACE_CLOSE_PROJECT)
737: System.err.println("Waiting Empty Project "
738: + project.getName());
739: while (hasFiles(project, null, false)) {
740: if (TraceFlags.TRACE_CLOSE_PROJECT)
741: System.err.println("Waiting Empty Project 2 "
742: + project.getName());
743: Object prjWaitEmptyLock;
744: synchronized (projectLocks) {
745: prjWaitEmptyLock = projectLocks.get(project);
746: if (prjWaitEmptyLock == null) {
747: prjWaitEmptyLock = new String(project.getName()
748: .toString());
749: projectLocks.put(project, prjWaitEmptyLock);
750: }
751: }
752: synchronized (prjWaitEmptyLock) {
753: try {
754: prjWaitEmptyLock.wait();
755: } catch (InterruptedException ex) {
756: // nothing
757: }
758: }
759: }
760: if (TraceFlags.TRACE_CLOSE_PROJECT)
761: System.err.println("Finished waiting on Empty Project "
762: + project.getName());
763: }
764:
765: public long getStopWatchTime() {
766: return TraceFlags.TIMING ? stopWatch.getTime() : -1;
767: }
768: }
|