001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.util.concurrent; 018 019import java.util.PriorityQueue; 020import java.util.concurrent.Executor; 021import java.util.concurrent.TimeUnit; 022import java.util.concurrent.atomic.AtomicLong; 023import java.util.concurrent.locks.Condition; 024import java.util.concurrent.locks.Lock; 025import java.util.concurrent.locks.ReentrantLock; 026import java.util.function.Consumer; 027 028public class AsyncCompletionService<V> { 029 030 private final Executor executor; 031 private final boolean ordered; 032 private final PriorityQueue<Task> queue = new PriorityQueue<>(); 033 private final AtomicLong nextId = new AtomicLong(); 034 private final AtomicLong index = new AtomicLong(); 035 private final ReentrantLock lock; 036 private final Condition available; 037 038 public AsyncCompletionService(Executor executor, boolean ordered) { 039 this(executor, ordered, null); 040 } 041 042 public AsyncCompletionService(Executor executor, boolean ordered, ReentrantLock lock) { 043 this.executor = executor; 044 this.ordered = ordered; 045 this.lock = lock != null ? lock : new ReentrantLock(); 046 this.available = this.lock.newCondition(); 047 } 048 049 public ReentrantLock getLock() { 050 return lock; 051 } 052 053 public void submit(Consumer<Consumer<V>> runner) { 054 Task f = new Task(nextId.getAndIncrement(), runner); 055 this.executor.execute(f); 056 } 057 058 public void skip() { 059 index.incrementAndGet(); 060 } 061 062 public V pollUnordered() { 063 final ReentrantLock lock = this.lock; 064 lock.lock(); 065 try { 066 Task t = queue.poll(); 067 return t != null ? t.result : null; 068 } finally { 069 lock.unlock(); 070 } 071 } 072 073 public V poll() { 074 final ReentrantLock lock = this.lock; 075 lock.lock(); 076 try { 077 Task t = queue.peek(); 078 if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) { 079 queue.poll(); 080 return t.result; 081 } else { 082 return null; 083 } 084 } finally { 085 lock.unlock(); 086 } 087 } 088 089 public V poll(long timeout, TimeUnit unit) throws InterruptedException { 090 long nanos = unit.toNanos(timeout); 091 final ReentrantLock lock = this.lock; 092 lock.lockInterruptibly(); 093 try { 094 for (;;) { 095 Task t = queue.peek(); 096 if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) { 097 queue.poll(); 098 return t.result; 099 } 100 if (nanos <= 0) { 101 return null; 102 } else { 103 nanos = available.awaitNanos(nanos); 104 } 105 } 106 } finally { 107 lock.unlock(); 108 } 109 } 110 111 public V take() throws InterruptedException { 112 final ReentrantLock lock = this.lock; 113 lock.lockInterruptibly(); 114 try { 115 for (;;) { 116 Task t = queue.peek(); 117 if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) { 118 queue.poll(); 119 return t.result; 120 } 121 available.await(); 122 } 123 } finally { 124 lock.unlock(); 125 } 126 } 127 128 private void complete(Task task) { 129 final ReentrantLock lock = this.lock; 130 lock.lock(); 131 try { 132 queue.add(task); 133 available.signalAll(); 134 } finally { 135 lock.unlock(); 136 } 137 } 138 139 private class Task implements Runnable, Comparable<Task> { 140 private final long id; 141 private final Consumer<Consumer<V>> runner; 142 private V result; 143 144 Task(long id, Consumer<Consumer<V>> runner) { 145 this.id = id; 146 this.runner = runner; 147 } 148 149 @Override 150 public void run() { 151 runner.accept(this::setResult); 152 } 153 154 protected void setResult(V result) { 155 this.result = result; 156 complete(this); 157 } 158 159 public int compareTo(Task other) { 160 return Long.compare(this.id, other.id); 161 } 162 163 public String toString() { 164 return "SubmitOrderedFutureTask[" + this.id + "]"; 165 } 166 } 167}