Chromium Code Reviews| 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 |