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}