Index: pkg/analyzer/lib/src/task/driver.dart |
diff --git a/pkg/analyzer/lib/src/task/driver.dart b/pkg/analyzer/lib/src/task/driver.dart |
index ab8ab9dc706e98f47490ba4994842eee9b4446d8..9aba0f2877721616b46319256030099a40c955a4 100644 |
--- a/pkg/analyzer/lib/src/task/driver.dart |
+++ b/pkg/analyzer/lib/src/task/driver.dart |
@@ -8,7 +8,9 @@ import 'dart:async'; |
import 'dart:collection'; |
import 'package:analyzer/src/context/cache.dart'; |
-import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask; |
+import 'package:analyzer/src/context/context.dart' show AnalysisContextImpl; |
+import 'package:analyzer/src/generated/engine.dart' |
+ hide AnalysisTask, AnalysisContextImpl; |
import 'package:analyzer/src/generated/java_engine.dart'; |
import 'package:analyzer/src/generated/resolver.dart'; |
import 'package:analyzer/src/task/inputs.dart'; |
@@ -43,6 +45,13 @@ class AnalysisDriver { |
WorkOrder currentWorkOrder; |
/** |
+ * Indicates whether any tasks are currently being performed (or building |
+ * their inputs). In debug builds, we use this to ensure that tasks don't |
+ * try to make use of the task manager in reentrant fashion. |
+ */ |
+ bool isTaskRunning = false; |
+ |
+ /** |
* The controller that is notified when a task is started. |
*/ |
StreamController<AnalysisTask> _onTaskStartedController; |
@@ -207,34 +216,40 @@ class AnalysisDriver { |
* Return the performed [AnalysisTask]. |
*/ |
AnalysisTask performWorkItem(WorkItem item) { |
- if (item.exception != null) { |
- // Mark all of the results that the task would have computed as being in |
- // ERROR with the exception recorded on the work item. |
- CacheEntry targetEntry = context.getCacheEntry(item.target); |
- targetEntry.setErrorState(item.exception, item.descriptor.results); |
- return null; |
- } |
- // Otherwise, perform the task. |
- AnalysisTask task = item.buildTask(); |
- _onTaskStartedController.add(task); |
- task.perform(); |
- CacheEntry entry = context.getCacheEntry(task.target); |
- if (task.caughtException == null) { |
- List<TargetedResult> dependedOn = item.inputTargetedResults.toList(); |
- Map<ResultDescriptor, dynamic> outputs = task.outputs; |
- for (ResultDescriptor result in task.descriptor.results) { |
- // TODO(brianwilkerson) We could check here that a value was produced |
- // and throw an exception if not (unless we want to allow null values). |
- entry.setValue(result, outputs[result], dependedOn); |
+ try { |
+ assert(!isTaskRunning); |
Brian Wilkerson
2015/05/14 17:36:37
This should be before the 'try' so that we don't c
Paul Berry
2015/05/14 19:40:43
Done.
|
+ isTaskRunning = true; |
+ if (item.exception != null) { |
+ // Mark all of the results that the task would have computed as being in |
+ // ERROR with the exception recorded on the work item. |
+ CacheEntry targetEntry = context.getCacheEntry(item.target); |
+ targetEntry.setErrorState(item.exception, item.descriptor.results); |
+ return null; |
} |
- for (WorkManager manager in workManagers) { |
- manager.resultsComputed(task.target, outputs); |
+ // Otherwise, perform the task. |
+ AnalysisTask task = item.buildTask(); |
+ _onTaskStartedController.add(task); |
+ task.perform(); |
+ CacheEntry entry = context.getCacheEntry(task.target); |
+ if (task.caughtException == null) { |
+ List<TargetedResult> dependedOn = item.inputTargetedResults.toList(); |
+ Map<ResultDescriptor, dynamic> outputs = task.outputs; |
+ for (ResultDescriptor result in task.descriptor.results) { |
+ // TODO(brianwilkerson) We could check here that a value was produced |
+ // and throw an exception if not (unless we want to allow null values). |
+ entry.setValue(result, outputs[result], dependedOn); |
+ } |
+ for (WorkManager manager in workManagers) { |
+ manager.resultsComputed(task.target, outputs); |
+ } |
+ } else { |
+ entry.setErrorState(task.caughtException, item.descriptor.results); |
} |
- } else { |
- entry.setErrorState(task.caughtException, item.descriptor.results); |
+ _onTaskCompletedController.add(task); |
+ return task; |
+ } finally { |
+ isTaskRunning = false; |
} |
- _onTaskCompletedController.add(task); |
- return task; |
} |
/** |
@@ -363,44 +378,51 @@ class WorkItem { |
* tasks' results should be marked as being in ERROR. |
*/ |
WorkItem gatherInputs(TaskManager taskManager) { |
- while (builder != null) { |
- AnalysisTarget inputTarget = builder.currentTarget; |
- ResultDescriptor inputResult = builder.currentResult; |
- inputTargetedResults.add(new TargetedResult(inputTarget, inputResult)); |
- CacheEntry inputEntry = context.getCacheEntry(inputTarget); |
- CacheState inputState = inputEntry.getState(inputResult); |
- if (inputState == CacheState.ERROR) { |
- exception = inputEntry.exception; |
- return null; |
- } else if (inputState == CacheState.IN_PROCESS) { |
- // |
- // TODO(brianwilkerson) Implement this case. |
- // |
- // One possibility would be to return a WorkItem that would perform a |
- // no-op task in order to cause us to come back to this work item on the |
- // next iteration. It would be more efficient, in general, to push this |
- // input onto a waiting list and proceed to the next input so that work |
- // could proceed, but given that the only result that can currently be |
- // IN_PROCESS is CONTENT, I don't know that it's worth the extra effort |
- // to implement the general solution at this point. |
- // |
- } else if (inputState != CacheState.VALID) { |
- try { |
- TaskDescriptor descriptor = |
- taskManager.findTask(inputTarget, inputResult); |
- return new WorkItem(context, inputTarget, descriptor); |
- } on AnalysisException catch (exception, stackTrace) { |
- this.exception = new CaughtException(exception, stackTrace); |
+ AnalysisDriver driver = (context as AnalysisContextImpl).driver; |
Brian Wilkerson
2015/05/14 17:36:37
We should eventually add the accessor to InternalA
Paul Berry
2015/05/14 19:40:43
After taking Konstantin's suggestion below the cas
|
+ try { |
+ assert(!driver.isTaskRunning); |
Brian Wilkerson
2015/05/14 17:36:38
Ditto.
scheglov
2015/05/14 17:44:05
Do we need to check it here?
AFAIK AnalysisContex
Paul Berry
2015/05/14 19:40:43
That's a good point. Moving the checks into the A
Paul Berry
2015/05/14 19:40:43
Done.
|
+ driver.isTaskRunning = true; |
+ while (builder != null) { |
+ AnalysisTarget inputTarget = builder.currentTarget; |
+ ResultDescriptor inputResult = builder.currentResult; |
+ inputTargetedResults.add(new TargetedResult(inputTarget, inputResult)); |
+ CacheEntry inputEntry = context.getCacheEntry(inputTarget); |
+ CacheState inputState = inputEntry.getState(inputResult); |
+ if (inputState == CacheState.ERROR) { |
+ exception = inputEntry.exception; |
return null; |
+ } else if (inputState == CacheState.IN_PROCESS) { |
+ // |
+ // TODO(brianwilkerson) Implement this case. |
+ // |
+ // One possibility would be to return a WorkItem that would perform a |
+ // no-op task in order to cause us to come back to this work item on the |
+ // next iteration. It would be more efficient, in general, to push this |
+ // input onto a waiting list and proceed to the next input so that work |
+ // could proceed, but given that the only result that can currently be |
+ // IN_PROCESS is CONTENT, I don't know that it's worth the extra effort |
+ // to implement the general solution at this point. |
+ // |
+ } else if (inputState != CacheState.VALID) { |
+ try { |
+ TaskDescriptor descriptor = |
+ taskManager.findTask(inputTarget, inputResult); |
+ return new WorkItem(context, inputTarget, descriptor); |
+ } on AnalysisException catch (exception, stackTrace) { |
+ this.exception = new CaughtException(exception, stackTrace); |
+ return null; |
+ } |
+ } |
+ builder.currentValue = inputEntry.getValue(inputResult); |
+ if (!builder.moveNext()) { |
+ inputs = builder.inputValue; |
+ builder = null; |
} |
} |
- builder.currentValue = inputEntry.getValue(inputResult); |
- if (!builder.moveNext()) { |
- inputs = builder.inputValue; |
- builder = null; |
- } |
+ return null; |
+ } finally { |
+ driver.isTaskRunning = false; |
} |
- return null; |
} |
@override |