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.ReentrantLock;
025import java.util.function.Consumer;
026
027public class AsyncCompletionService<V> {
028
029    private final Executor executor;
030    private final boolean ordered;
031    private final PriorityQueue<Task> queue = new PriorityQueue<>();
032    private final AtomicLong nextId = new AtomicLong();
033    private final AtomicLong index = new AtomicLong();
034    private final ReentrantLock lock;
035    private final Condition available;
036
037    public AsyncCompletionService(Executor executor, boolean ordered) {
038        this(executor, ordered, null);
039    }
040
041    public AsyncCompletionService(Executor executor, boolean ordered, ReentrantLock lock) {
042        this.executor = executor;
043        this.ordered = ordered;
044        this.lock = lock != null ? lock : new ReentrantLock();
045        this.available = this.lock.newCondition();
046    }
047
048    public ReentrantLock getLock() {
049        return lock;
050    }
051
052    public void submit(Consumer<Consumer<V>> runner) {
053        Task f = new Task(nextId.getAndIncrement(), runner);
054        this.executor.execute(f);
055    }
056
057    public void skip() {
058        index.incrementAndGet();
059    }
060
061    public V pollUnordered() {
062        final ReentrantLock lock = this.lock;
063        lock.lock();
064        try {
065            Task t = queue.poll();
066            return t != null ? t.result : null;
067        } finally {
068            lock.unlock();
069        }
070    }
071
072    public V poll() {
073        final ReentrantLock lock = this.lock;
074        lock.lock();
075        try {
076            Task t = queue.peek();
077            if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) {
078                queue.poll();
079                return t.result;
080            } else {
081                return null;
082            }
083        } finally {
084            lock.unlock();
085        }
086    }
087
088    public V poll(long timeout, TimeUnit unit) throws InterruptedException {
089        long nanos = unit.toNanos(timeout);
090        final ReentrantLock lock = this.lock;
091        lock.lockInterruptibly();
092        try {
093            for (;;) {
094                Task t = queue.peek();
095                if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) {
096                    queue.poll();
097                    return t.result;
098                }
099                if (nanos <= 0) {
100                    return null;
101                } else {
102                    nanos = available.awaitNanos(nanos);
103                }
104            }
105        } finally {
106            lock.unlock();
107        }
108    }
109
110    public V take() throws InterruptedException {
111        final ReentrantLock lock = this.lock;
112        lock.lockInterruptibly();
113        try {
114            for (;;) {
115                Task t = queue.peek();
116                if (t != null && (!ordered || index.compareAndSet(t.id, t.id + 1))) {
117                    queue.poll();
118                    return t.result;
119                }
120                available.await();
121            }
122        } finally {
123            lock.unlock();
124        }
125    }
126
127    private void complete(Task task) {
128        final ReentrantLock lock = this.lock;
129        lock.lock();
130        try {
131            queue.add(task);
132            available.signalAll();
133        } finally {
134            lock.unlock();
135        }
136    }
137
138    private class Task implements Runnable, Comparable<Task> {
139        private final long id;
140        private final Consumer<Consumer<V>> runner;
141        private V result;
142
143        Task(long id, Consumer<Consumer<V>> runner) {
144            this.id = id;
145            this.runner = runner;
146        }
147
148        @Override
149        public void run() {
150            runner.accept(this::setResult);
151        }
152
153        protected void setResult(V result) {
154            this.result = result;
155            complete(this);
156        }
157
158        @Override
159        public int compareTo(Task other) {
160            return Long.compare(this.id, other.id);
161        }
162
163        @Override
164        public String toString() {
165            return "SubmitOrderedFutureTask[" + this.id + "]";
166        }
167    }
168}