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.concurrent.Callable; 020import java.util.concurrent.CompletionService; 021import java.util.concurrent.DelayQueue; 022import java.util.concurrent.Delayed; 023import java.util.concurrent.Executor; 024import java.util.concurrent.Future; 025import java.util.concurrent.FutureTask; 026import java.util.concurrent.TimeUnit; 027import java.util.concurrent.atomic.AtomicInteger; 028 029/** 030 * A {@link java.util.concurrent.CompletionService} that orders the completed tasks in the same order as they where 031 * submitted. 032 * 033 * @deprecated use {@link AsyncCompletionService} 034 */ 035@Deprecated 036public class SubmitOrderedCompletionService<V> implements CompletionService<V> { 037 038 private final Executor executor; 039 040 // the idea to order the completed task in the same order as they where submitted is to leverage 041 // the delay queue. With the delay queue we can control the order by the getDelay and compareTo methods 042 // where we can order the tasks in the same order as they where submitted. 043 private final DelayQueue<SubmitOrderFutureTask> completionQueue = new DelayQueue<>(); 044 045 // id is the unique id that determines the order in which tasks was submitted (incrementing) 046 private final AtomicInteger id = new AtomicInteger(); 047 // index is the index of the next id that should expire and thus be ready to take from the delayed queue 048 private final AtomicInteger index = new AtomicInteger(); 049 050 private class SubmitOrderFutureTask extends FutureTask<V> implements Delayed { 051 052 // the id this task was assigned 053 private final long id; 054 055 SubmitOrderFutureTask(long id, Callable<V> voidCallable) { 056 super(voidCallable); 057 this.id = id; 058 } 059 060 SubmitOrderFutureTask(long id, Runnable runnable, V result) { 061 super(runnable, result); 062 this.id = id; 063 } 064 065 @Override 066 public long getDelay(TimeUnit unit) { 067 // if the answer is 0 then this task is ready to be taken 068 long answer = id - index.get(); 069 if (answer <= 0) { 070 return answer; 071 } 072 // okay this task is not ready yet, and we don't really know when it would be 073 // so we have to return a delay value of one time unit 074 if (TimeUnit.NANOSECONDS == unit) { 075 // okay this is too fast so use a little more delay to avoid CPU burning cycles 076 // To avoid align with java 11 impl of 077 // "java.util.concurrent.locks.AbstractQueuedSynchronizer.SPIN_FOR_TIMEOUT_THRESHOLD", otherwise 078 // no sleep with very high CPU usage 079 answer = 1001L; 080 } else { 081 answer = unit.convert(1, unit); 082 } 083 return answer; 084 } 085 086 @Override 087 @SuppressWarnings("unchecked") 088 public int compareTo(Delayed o) { 089 SubmitOrderFutureTask other = (SubmitOrderFutureTask) o; 090 return (int) (this.id - other.id); 091 } 092 093 @Override 094 protected void done() { 095 // when we are done add to the completion queue 096 completionQueue.add(this); 097 } 098 099 @Override 100 public String toString() { 101 // output using zero-based index 102 return "SubmitOrderedFutureTask[" + (id - 1) + "]"; 103 } 104 } 105 106 public SubmitOrderedCompletionService(Executor executor) { 107 this.executor = executor; 108 } 109 110 @Override 111 public Future<V> submit(Callable<V> task) { 112 if (task == null) { 113 throw new IllegalArgumentException("Task must be provided"); 114 } 115 SubmitOrderFutureTask f = new SubmitOrderFutureTask(id.incrementAndGet(), task); 116 executor.execute(f); 117 return f; 118 } 119 120 @Override 121 public Future<V> submit(Runnable task, Object result) { 122 if (task == null) { 123 throw new IllegalArgumentException("Task must be provided"); 124 } 125 SubmitOrderFutureTask f = new SubmitOrderFutureTask(id.incrementAndGet(), task, null); 126 executor.execute(f); 127 return f; 128 } 129 130 @Override 131 public Future<V> take() throws InterruptedException { 132 index.incrementAndGet(); 133 return completionQueue.take(); 134 } 135 136 @Override 137 public Future<V> poll() { 138 index.incrementAndGet(); 139 Future<V> answer = completionQueue.poll(); 140 if (answer == null) { 141 // decrease counter if we didnt get any data 142 index.decrementAndGet(); 143 } 144 return answer; 145 } 146 147 @Override 148 public Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException { 149 index.incrementAndGet(); 150 Future<V> answer = completionQueue.poll(timeout, unit); 151 if (answer == null) { 152 // decrease counter if we didnt get any data 153 index.decrementAndGet(); 154 } 155 return answer; 156 } 157 158 /** 159 * Marks the current task as timeout, which allows you to poll the next tasks which may already have been completed. 160 */ 161 public void timeoutTask() { 162 index.incrementAndGet(); 163 } 164 165}