/* * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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 org.jctools.queues.blocking; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentLinkedQueue; import org.jctools.queues.MpmcArrayQueue; import org.jctools.queues.MpscArrayQueue; import org.jctools.queues.MpscCompoundQueue; import org.jctools.queues.MpscLinkedQueue7; import org.jctools.queues.MpscLinkedQueue8; import org.jctools.queues.SpmcArrayQueue; import org.jctools.queues.SpscArrayQueue; import org.jctools.queues.SpscLinkedQueue; import org.jctools.queues.spec.ConcurrentQueueSpec; import org.jctools.queues.spec.Ordering; import org.jctools.util.CompilationResult; import org.jctools.util.SimpleCompiler; import org.jctools.util.Template; import org.jctools.util.UnsafeAccess; /** * The queue factory produces {@link java.util.Queue} instances based on a best fit to the {@link ConcurrentQueueSpec}. * This allows minimal dependencies between user code and the queue implementations and gives users a way to express * their requirements on a higher level. * * @author nitsanw * */ public class BlockingQueueFactory { private static Map<Class, Class> blockingQueueCache = Collections .synchronizedMap(new HashMap<Class, Class>()); public static class BlockingModel { public String blockingQueueClassName; public String queueClassName; public String TakeStrategy; public String PutStrategy; } public static <E> BlockingQueue<E> newBlockingQueue(ConcurrentQueueSpec qs) { Class takeStratClass = (qs.consumers == 1) ? ScParkTakeStrategy.class : McParkTakeStrategy.class; Class putStratClass = YieldPutStrategy.class; return newBlockingQueue(qs, takeStratClass, putStratClass); } public static <E> BlockingQueue<E> newBlockingQueue(ConcurrentQueueSpec qs, Class<? extends TakeStrategy> takeStratClass, Class<? extends PutStrategy> putStratClass) { // Check if strategies are compatible with QueueSpec boolean isTakeStratOK = false; boolean isPutStratOK = false; try { isTakeStratOK = takeStratClass.newInstance().supportsSpec(qs); isPutStratOK = putStratClass.newInstance().supportsSpec(qs); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Error instantiating strategy"); } catch (InstantiationException e) { throw new IllegalArgumentException("Error instantiating strategy"); } if (!isTakeStratOK) { throw new IllegalArgumentException("The take strategy is not compatible with the Queue Specs"); } if (!isPutStratOK) { throw new IllegalArgumentException("The put strategy is not compatible with the Queue Specs"); } if (qs.isBounded()) { // SPSC if (qs.isSpsc()) { return getBlockingQueueFrom(SpscArrayQueue.class, takeStratClass, putStratClass, qs.capacity); } // MPSC else if (qs.isMpsc()) { if (qs.ordering != Ordering.NONE) { return getBlockingQueueFrom(MpscArrayQueue.class, takeStratClass, putStratClass, qs.capacity); } else { return getBlockingQueueFrom(MpscCompoundQueue.class, takeStratClass, putStratClass, qs.capacity); } } // SPMC else if (qs.isSpmc()) { return getBlockingQueueFrom(SpmcArrayQueue.class, takeStratClass, putStratClass, qs.capacity); } // MPMC else if (qs.isMpmc()) { return getBlockingQueueFrom(MpmcArrayQueue.class, takeStratClass, putStratClass, qs.capacity); } // Default bounded blocking return new ArrayBlockingQueue<E>(qs.capacity); } else { // SPSC if (qs.isSpsc()) { return getBlockingQueueFrom(SpscLinkedQueue.class, takeStratClass, putStratClass, -1); } // MPSC else if (qs.isMpsc()) { if (UnsafeAccess.SUPPORTS_GET_AND_SET) { return getBlockingQueueFrom(MpscLinkedQueue8.class, takeStratClass, putStratClass, -1); } else { return getBlockingQueueFrom(MpscLinkedQueue7.class, takeStratClass, putStratClass, -1); } } // Default unbounded blocking : CLQ based return getBlockingQueueFrom(ConcurrentLinkedQueue.class, takeStratClass, putStratClass, -1); } } private static <E> BlockingQueue<E> getBlockingQueueFrom(Class<? extends Queue> queueClass, Class<? extends TakeStrategy> takeStrat, Class<? extends PutStrategy> putStrat, int capacity) { // Build model for template filling BlockingModel model = new BlockingModel(); model.queueClassName = queueClass.getSimpleName(); model.blockingQueueClassName = model.queueClassName + "Blocking"; model.TakeStrategy = takeStrat.getSimpleName(); model.PutStrategy = putStrat.getSimpleName(); // Check for the Queue in cache Class blockingClass = null; // blockingQueueCache.get(queueClass); // Can't use cache right now because of // capacity hardcoded in class if (blockingClass == null) { // Load and fill template Template blockingTemplate = Template .fromFile(BlockingQueueFactory.class, "TemplateBlocking.java"); String blockingQueueClassFile = blockingTemplate.render(model); // System.out.println(blockingQueueClassFile); // Compile result SimpleCompiler compiler = new SimpleCompiler(); CompilationResult result = compiler.compile(model.blockingQueueClassName, blockingQueueClassFile); if (result.isSuccessful()) { try { // Load class blockingClass = result.getClassLoader().loadClass("org.jctools.queues.blocking."+model.blockingQueueClassName); } catch (Exception e) { throw new IllegalStateException(e); } // Store class in cache for later re-use blockingQueueCache.put(queueClass, blockingClass); } else { System.out.println(result.getDiagnostics()); return null; } } // Instantiate new Blocking queue BlockingQueue<E> q = null; try { q = (BlockingQueue<E>) blockingClass.getConstructor(Integer.TYPE).newInstance(capacity); } catch (Exception e) { throw new IllegalStateException(e); } return q; } }