blob: 14f8ea39821dbfe9d6a9e29bd601ea50f9ed9eb2 [file] [log] [blame]
/*
* Copyright (c) 2013, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.internal.index.operation;
import com.google.common.collect.Lists;
import com.google.dart.engine.source.Source;
import com.google.dart.engine.utilities.translation.DartOmit;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* Instances of the {@link OperationQueue} represent a queue of operations against the index that
* are waiting to be performed.
*
* @coverage dart.engine.index
*/
@DartOmit
public class OperationQueue {
/**
* The non-query operations that are waiting to be performed.
*/
private final LinkedList<IndexOperation> nonQueryOperations = Lists.newLinkedList();
/**
* The query operations that are waiting to be performed.
*/
private final LinkedList<IndexOperation> queryOperations = Lists.newLinkedList();
/**
* {@code true} if query operations should be returned by {@link #dequeue(long)} or {code false}
* if not.
*/
private boolean processQueries = true;
/**
* Initialize a newly created operation queue to be empty.
*/
public OperationQueue() {
super();
}
/**
* If this queue is not empty, then remove the next operation from the head of this queue and
* return it. If this queue is empty (see {@link #setProcessQueries(boolean)}, then the behavior
* of this method depends on the value of the argument. If the argument is less than or equal to
* zero (<code>0</code>), then {@code null} will be returned immediately. If the argument is
* greater than zero, then this method will wait until at least one operation has been added to
* this queue or until the given amount of time has passed. If, at the end of that time, this
* queue is empty, then {@code null} will be returned. If this queue is not empty, then the first
* operation will be removed and returned.
* <p>
* Note that {@code null} can be returned, even if a positive timeout is given.
* <p>
* Note too that this method's timeout is not treated the same way as the timeout value used for
* {@link Object#wait(long)}. In particular, it is not possible to cause this method to wait for
* an indefinite period of time.
*
* @param timeout the maximum number of milliseconds to wait for an operation to be available
* before giving up and returning {@code null}
* @return the operation that was removed from the queue
* @throws InterruptedException if the thread on which this method is running was interrupted
* while it was waiting for an operation to be added to the queue
*/
public IndexOperation dequeue(long timeout) throws InterruptedException {
synchronized (nonQueryOperations) {
if (nonQueryOperations.isEmpty() && (!processQueries || queryOperations.isEmpty())) {
if (timeout <= 0L) {
return null;
}
waitForOperationAvailable(timeout);
}
if (!nonQueryOperations.isEmpty()) {
return nonQueryOperations.removeFirst();
}
if (processQueries && !queryOperations.isEmpty()) {
return queryOperations.removeFirst();
}
return null;
}
}
/**
* Add the given operation to the tail of this queue.
*
* @param operation the operation to be added to the queue
*/
public void enqueue(IndexOperation operation) {
synchronized (nonQueryOperations) {
if (operation instanceof ClearOperation) {
queryOperations.clear();
nonQueryOperations.clear();
}
if (operation instanceof RemoveSourceOperation) {
Source source = ((RemoveSourceOperation) operation).getSource();
removeForSource(source, nonQueryOperations);
removeForSource(source, queryOperations);
}
if (operation.isQuery()) {
queryOperations.add(operation);
} else {
nonQueryOperations.add(operation);
}
notifyOperationAvailable();
}
}
/**
* Return a list containing all of the operations that are currently on the queue. Modifying this
* list will not affect the state of the queue.
*
* @return all of the operations that are currently on the queue
*/
public List<IndexOperation> getOperations() {
List<IndexOperation> operations = Lists.newArrayList();
synchronized (nonQueryOperations) {
operations.addAll(nonQueryOperations);
operations.addAll(queryOperations);
}
return operations;
}
/**
* Set whether the receiver's {@link #dequeue(long)} method should return query operations.
*
* @param processQueries {@code true} if the receiver's {@link #dequeue(long)} method should
* return query operations or {@code false} if query operations should be queued but not
* returned by the receiver's {@link #dequeue(long)} method until this method is called
* with a value of {@code true}.
*/
public void setProcessQueries(boolean processQueries) {
synchronized (nonQueryOperations) {
if (this.processQueries != processQueries) {
this.processQueries = processQueries;
if (processQueries && !queryOperations.isEmpty()) {
notifyOperationAvailable();
}
}
}
}
/**
* Return the number of operations on the queue.
*
* @return the number of operations on the queue
*/
public int size() {
synchronized (nonQueryOperations) {
return nonQueryOperations.size() + queryOperations.size();
}
}
private void notifyOperationAvailable() {
nonQueryOperations.notifyAll();
}
/**
* Removes operations that should be removed when given {@link Source} is removed.
*/
private void removeForSource(Source source, LinkedList<IndexOperation> operations) {
for (Iterator<IndexOperation> iter = operations.listIterator(); iter.hasNext();) {
IndexOperation indexOperation = iter.next();
if (indexOperation.removeWhenSourceRemoved(source)) {
iter.remove();
}
}
}
private void waitForOperationAvailable(long timeout) throws InterruptedException {
nonQueryOperations.wait(timeout);
}
}