| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library operation.queue; |
| |
| import 'dart:collection'; |
| |
| import 'package:analysis_server/src/analysis_server.dart'; |
| import 'package:analysis_server/src/operation/operation.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/source.dart'; |
| |
| /** |
| * A queue of operations in an [AnalysisServer]. |
| */ |
| class ServerOperationQueue { |
| final List<Queue<ServerOperation>> _queues = <Queue<ServerOperation>>[]; |
| |
| ServerOperationQueue() { |
| for (int i = 0; i < ServerOperationPriority.COUNT; i++) { |
| var queue = new DoubleLinkedQueue<ServerOperation>(); |
| _queues.add(queue); |
| } |
| } |
| |
| /** |
| * Returns `true` if there are no queued [ServerOperation]s. |
| */ |
| bool get isEmpty { |
| return _queues.every((queue) => queue.isEmpty); |
| } |
| |
| /** |
| * Adds the given operation to this queue. The exact position in the queue |
| * depends on the priority of the given operation relative to the priorities |
| * of the other operations in the queue. |
| */ |
| void add(ServerOperation operation) { |
| int queueIndex = operation.priority.ordinal; |
| Queue<ServerOperation> queue = _queues[queueIndex]; |
| // try to merge into an existing operation |
| for (ServerOperation existingOperation in queue) { |
| if (existingOperation is MergeableOperation && |
| existingOperation.merge(operation)) { |
| return; |
| } |
| } |
| // add it |
| queue.addLast(operation); |
| } |
| |
| /** |
| * Removes all elements in the queue. |
| */ |
| void clear() { |
| for (Queue<ServerOperation> queue in _queues) { |
| queue.clear(); |
| } |
| } |
| |
| /** |
| * The given [context] has been removed, so all pending operations that refer |
| * to it should be removed from the queue. |
| */ |
| void contextRemoved(AnalysisContext context) { |
| for (Queue<ServerOperation> queue in _queues) { |
| queue.removeWhere((operation) => operation.context == context); |
| } |
| } |
| |
| /** |
| * Return the next operation to perform, or `null` if the queue is empty. |
| * This method does not change the queue. |
| */ |
| ServerOperation peek() { |
| for (Queue<ServerOperation> queue in _queues) { |
| if (!queue.isEmpty) { |
| return queue.first; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Reschedules queued operations according their current priorities. |
| */ |
| void reschedule() { |
| // prepare all operations |
| List<ServerOperation> operations = <ServerOperation>[]; |
| for (Queue<ServerOperation> queue in _queues) { |
| operations.addAll(queue); |
| queue.clear(); |
| } |
| // add all operations |
| operations.forEach(add); |
| } |
| |
| /** |
| * The given [source] if about to changed. |
| */ |
| void sourceAboutToChange(Source source) { |
| for (Queue<ServerOperation> queue in _queues) { |
| queue.removeWhere((operation) { |
| if (operation is SourceSensitiveOperation) { |
| return operation.shouldBeDiscardedOnSourceChange(source); |
| } |
| return false; |
| }); |
| } |
| } |
| |
| /** |
| * Returns the next operation to perform or `null` if empty. |
| */ |
| ServerOperation take() { |
| for (Queue<ServerOperation> queue in _queues) { |
| if (!queue.isEmpty) { |
| return queue.removeFirst(); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns an operation that satisfies the given [test] or `null`. |
| */ |
| ServerOperation takeIf(bool test(ServerOperation operation)) { |
| for (Queue<ServerOperation> queue in _queues) { |
| for (ServerOperation operation in queue) { |
| if (test(operation)) { |
| queue.remove(operation); |
| return operation; |
| } |
| } |
| } |
| return null; |
| } |
| } |