/* * 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); } }