All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.daisy.common.priority.UpdatablePriorityBlockingQueue Maven / Gradle / Ivy

The newest version!
package org.daisy.common.priority;


import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ForwardingBlockingQueue;
import com.google.common.util.concurrent.Monitor;

/**
 * This class adds a layer to a forwarded {@link java.util.concurrent.PriorityBlockingQueue} that allows to update
 * the order based on a priority or via the provided methods.
 *
 * 

Updating the priorities

*
    *
  • {@link #update(Function function)}: Updates the priorities by applying the given function sequentially to the different priorities. The function has update the {@link PrioritizableRunnable} by reference. This function is threadsafe
  • *
  • {@link #swap(PrioritizableRunnable runnable)} allows to swap two elements in the queue
  • * *
* * The internal design might seem a bit over-engineered but there are some reasons for that. ThreadPoolExecutorSevice needs a queue which has to be * of type Runnable. Our needs are a bit more complex than that, so we have to "hide" the actual internal type of our * objects a give a Runnable view to the superclass. This forces some unchecked type conversions that are safe ONLY when interacting with PrioritizableRunnables. * * Also we have to cope with the possibility of swapping elements in the queue. This is done more or less elegantly by the tuple SwappingPriority and WrappingPriorityQueue, which allow to hide the element wrapping process and return priorities of other elements as if they were its own. * This class is threadsafe * @version 1.0 */ public class UpdatablePriorityBlockingQueue extends ForwardingBlockingQueue { /** * The forwarded priority queue */ private WrappingPriorityQueue,SwappingPriority> delegate; /** * Monitor that controls {@link #take()} and other blocking aspects of this class */ private Monitor monitor = new Monitor(); /** * Condition when take stops blocking. There is something in the queue * and there is no updating taking place. */ private Monitor.Guard canTake = new Monitor.Guard(monitor) { @Override public boolean isSatisfied() { return delegate.size() > 0 && !updating.get(); } }; /** * Condition for adding elements to this queue. Just makes sure * that no updating processing is being carried out. */ private Monitor.Guard canAdd = new Monitor.Guard(monitor) { @Override public boolean isSatisfied() { return !updating.get(); } }; /** * Unwraps the SwappingPriority by returning the delegate */ private Function, PrioritizableRunnable> unwrapFunction = new Function, PrioritizableRunnable>() { @Override public PrioritizableRunnable apply(SwappingPriority arg) { return arg.getDelegate(); } }; /** * Wraps the PrioritizableRunnable into a SwappingPriority */ private Function,SwappingPriority > wrapFunction = new Function,SwappingPriority>() { @Override public SwappingPriority apply(PrioritizableRunnable arg) { return new SwappingPriority(arg); } }; /** * flag controlling the updating processes */ AtomicBoolean updating = new AtomicBoolean(false); /** * Creates a new UpdatablePriorityBlockingQueue, it has infiniteinfiniteinfiniteinfiniteinfinite * capacity. */ public UpdatablePriorityBlockingQueue() { this.delegate = this.buildQueue(); } /** * @return a brand new queue, it does not set it as delegate */ private WrappingPriorityQueue,SwappingPriority> buildQueue() { PriorityBlockingQueue> queue= new PriorityBlockingQueue>(20, new PrioritizableComparator()); return new WrappingPriorityQueue,SwappingPriority>(queue,wrapFunction,unwrapFunction); } /** * Updates the concurrent mechanisms when * a updating process is starting. */ private void enterUpdate() { this.monitor.enter(); this.updating.set(true); } /** * Updates the concurrent mechanisms when * a updating process is finishing and re-inserts all * the elements in the queue. */ private void leaveUpdate() { this.doUpdate(); this.updating.set(false); this.monitor.leave(); } /** * Copies the delegate, cleans it and reinserts all the elements. * This has to be done in order to reorder the runnables according * to their priority. */ private void doUpdate() { //re-add the elements of the queue to re-sort them Collection> aux = Lists.newLinkedList(this.delegate.wrapped()); this.delegate.clear(); this.delegate.addAllBypass(aux); } /** * Tries to find the runnable among the elements in the queue. */ protected Optional> tryFind(final PrioritizableRunnable runnable){ return Iterables.tryFind(this.delegate.wrapped(), new Predicate>() { @Override public boolean apply(SwappingPriority e) { return e.getDelegate().equals(runnable); }}); } /** * Swap the priorities of both PrioritizableRunnable objects to change the * order in the queue */ public synchronized void swap(PrioritizableRunnable runnable1,PrioritizableRunnable runnable2) { this.enterUpdate();//in monitor Optional> node1=this.tryFind(runnable1); Optional> node2=this.tryFind(runnable2); //one of them doesn't exist, get out if (!node1.isPresent() || !node2.isPresent()){ this.leaveUpdate();//out monitor return; } //swap the overriders node1.get().swapWith(node2.get()); this.leaveUpdate();//out monitor } /** * Applies the function to all the elements in the queue. The function * must change the objects by reference. Once the function has been applied * the queue is reordered. * This function is threadsafe * @param function */ public synchronized void update(Function, Void> function) { this.enterUpdate(); for (PrioritizableRunnable runnable : this.delegate) { function.apply(runnable); } this.leaveUpdate(); } /** * Returns the runnables as an immutable {@link java.util.Collection} maintaining the order given by the priority */ public Collection> asOrderedCollection() { List> list= Lists.newLinkedList(this.delegate.wrapped()); Collections.sort(list, new PrioritizableComparator()); return ImmutableList.copyOf(Collections2.transform(list,this.unwrapFunction)); } /** * Returns the runnables as an immutable {@link java.util.Collection} without maintaining the order given by the priority */ public Collection> asCollection() { return ImmutableList.copyOf(this.delegate.unwrap()); } /** * Returns this forwarding class delegate * @return */ @Override @SuppressWarnings({ "unchecked" }) //controlled environment not (terribly) dangerous protected BlockingQueue delegate() { return (BlockingQueue) (BlockingQueue) this.delegate; } /** * See {@link java.util.concurrent.BlockingQueue#offer()}, This method may block if the queue * is being updated. */ @SuppressWarnings("unchecked") @Override public synchronized boolean offer(Runnable o) { boolean res; try { monitor.enterWhen(canAdd); } catch (InterruptedException e) { monitor.leave(); throw new RuntimeException(e); } //System.out.println("offer: entered monitor"); res = this.delegate.offer((PrioritizableRunnable) o); //System.out.println("offer: left monitor"); monitor.leave(); return res; } /** * See {@link java.util.concurrent.BlockingQueue#add()}, This method may block if the queue * is being updated. */ @SuppressWarnings("unchecked") @Override public synchronized boolean add(Runnable element) { boolean res; try { monitor.enterWhen(canAdd); } catch (InterruptedException e) { monitor.leave(); throw new RuntimeException(e); } res=this.delegate.add((PrioritizableRunnable)element); monitor.leave(); return res; } /** * See {@link java.util.concurrent.BlockingQueue#take}, it also waits for any updating * operations to finish. */ @Override public Runnable take() throws InterruptedException { //int num=this.takes.incrementAndGet(); //System.out.println("Take("+num+"): before mon"); monitor.enterWhen(canTake); //System.out.println("Take("+num+"): entered monitor"); Runnable res=this.delegate.poll(); monitor.leave(); //System.out.println("Take("+num+"): left monitor"); return res; } } /** * This class allows to return the priority of another PrioritizableRunnable while forwarding the rest * of the calls to another object. */ class SwappingPriority extends ForwardingPrioritableRunnable { PrioritizableRunnable overrider; public SwappingPriority(PrioritizableRunnable delegate,PrioritizableRunnable overrider) { super(delegate); this.overrider=overrider; } public SwappingPriority(PrioritizableRunnable delegate) { super(delegate); this.overrider=delegate; } @Override public double getPriority() { return this.overrider.getPriority(); } /** * @return the overrider */ public PrioritizableRunnable getOverrider() { return overrider; } /** * @param overrider the overrider to set */ public void setOverrider(PrioritizableRunnable overrider) { this.overrider = overrider; } /** * Swap priorities */ public void swapWith(SwappingPriority other){ PrioritizableRunnable aux=this.getOverrider(); this.setOverrider(other.getOverrider()); other.setOverrider(aux); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy