001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2018 nobark (tools4j), Marco Terzer, Anton Anufriev 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.tools4j.nobark.queue; 025 026import java.util.Objects; 027import java.util.function.BiConsumer; 028import java.util.function.Function; 029 030/** 031 * A conflation queue variant that allows exchanging of elements between the consumer and the producer. Naturally the 032 * producer already sends values to the consumer; with an exchange queue the consumer can pass values back to the 033 * producer when polling values. This can for instance be used to reduce garbage by circulating and reusing values 034 * between producer and consumer. 035 * 036 * @param <K> the type of the conflation key 037 * @param <V> the type of values in the queue 038 */ 039public interface ExchangeConflationQueue<K,V> extends ConflationQueue<K,V> { 040 @Override 041 Appender<K,V> appender(); 042 043 @Override 044 ExchangePoller<K,V> poller(); 045 046 /** 047 * Poller object used by the consumer to poll values; exchange poller adds functionality for exchanging values with 048 * the producer during poll operations. 049 * 050 * @param <K> the type of the conflation key 051 * @param <V> the type of values in the queue 052 */ 053 @FunctionalInterface 054 interface ExchangePoller<K, V> extends Poller<K,V> { 055 /** 056 * Polls the queue passing an unused value in exchange to the queue. The given consumer is invoked with 057 * conflation key and polled value if the queue was non-empty and the value is returned. If the queue was 058 * empty, null is returned. The exchange value will at some point be returned by an enqueue operation with the 059 * same conflation key and can be reused by the producer. 060 * 061 * @param consumer consumer for conflation key and polled value 062 * @param exchange a value offered in exchange for the polled value, to be returned by an enqueue operation 063 * @return the polled value, or null if the queue was empty 064 */ 065 V poll(BiConsumer<? super K, ? super V> consumer, V exchange); 066 067 /** 068 * Polls the queue passing an unused value in exchange to the queue. The given consumer is invoked with 069 * conflation key and polled value if the queue was non-empty and the value is returned. If the queue was 070 * empty, null is returned. 071 * 072 * @param consumer consumer for conflation key and polled value 073 * @return the polled value, or null if the queue was empty 074 */ 075 @Override 076 default V poll(final BiConsumer<? super K, ? super V> consumer) { 077 return poll(consumer, null); 078 } 079 } 080 081 /** 082 * Returns an {@link ExchangeConflationQueue} whose appender is guaranteed to never return null on enqueue. If the 083 * unsafe queue returns null on enqueue e.g. because the queue is empty and no exchange values can be retrieved yet, 084 * the specified cqFactory is used to create a value. 085 * 086 * @param unsafeQueue the unsafe queue possibly returning null values on enqueue 087 * @param valueFactory the value cqFactory used if the unsafe queue returned null when a value was enqueued 088 * @param <K> the type of the conflation key 089 * @param <V> the type of values in the queue 090 * @return a null-safe version of the queue that never returns null values when enqueuing values 091 */ 092 static <K,V> ExchangeConflationQueue<K, V> nullSafe(final ExchangeConflationQueue<K,V> unsafeQueue, 093 final Function<? super K, ? extends V> valueFactory) { 094 Objects.requireNonNull(unsafeQueue); 095 Objects.requireNonNull(valueFactory); 096 return new ExchangeConflationQueue<K, V>() { 097 final ThreadLocal<Appender<K,V>> appender = ThreadLocal.withInitial(() -> nullSafe(unsafeQueue.appender(), valueFactory)); 098 @Override 099 public Appender<K, V> appender() { 100 return appender.get(); 101 } 102 103 @Override 104 public ExchangePoller<K, V> poller() { 105 return unsafeQueue.poller(); 106 } 107 108 @Override 109 public int size() { 110 return unsafeQueue.size(); 111 } 112 }; 113 } 114 115 /** 116 * Returns an {@link Appender} whose {@link Appender#enqueue(Object, Object) enqueue(..)} method is guaranteed to 117 * never return null. If the unsafe appender returns null on enqueue e.g. because the queue is empty and no 118 * exchange values can be retrieved yet, the specified cqFactory is used to create a value. 119 * 120 * @param unsafeAppender the unsafe appender possibly returning null values on enqueue 121 * @param valueFactory the value cqFactory used if the unsafe enqueuing operation returned null 122 * @param <K> the type of the conflation key 123 * @param <V> the type of values in the queue 124 * @return a null-safe version of the appender that never returns null values when enqueuing values 125 */ 126 static <K,V> Appender<K, V> nullSafe(final Appender<K,V> unsafeAppender, 127 final Function<? super K, ? extends V> valueFactory) { 128 Objects.requireNonNull(unsafeAppender); 129 Objects.requireNonNull(valueFactory); 130 return (k, v) -> { 131 final V value = unsafeAppender.enqueue(k, v); 132 return value == null ? 133 Objects.requireNonNull(valueFactory.apply(k), "value cqFactory returned null") : 134 value; 135 }; 136 } 137}