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.
059         * <p>
060         * If polling was successful, the exchange value will at some point be returned by an enqueue operation with
061         * the same conflation key and can be reused by the producer.  The caller retains ownership of the exchange
062         * value if polling failed and null was returned.
063         *
064         * @param consumer consumer for conflation key and polled value
065         * @param  exchange a value offered in exchange for the polled value, to be returned by an enqueue operation
066         * @return the polled value, or null if the queue was empty
067         */
068        V poll(BiConsumer<? super K, ? super V> consumer, V exchange);
069
070        /**
071         * Polls the queue passing an unused value in exchange to the queue.  Returns the polled value if any value was
072         * present in the queue, or null if the queue was empty.
073         * <p>
074         * If polling was successful, the exchange value will at some point be returned by an enqueue operation with
075         * the same conflation key and can be reused by the producer.  The caller retains ownership of the exchange
076         * value if polling failed and null was returned.
077         *
078         * @param  exchange a value offered in exchange for the polled value, to be returned by an enqueue operation
079         * @return the polled value, or null if the queue was empty
080         */
081        default V poll(final V exchange) {
082            return poll((k,v) -> {}, exchange);
083        }
084
085        @Override
086        default V poll(final BiConsumer<? super K, ? super V> consumer) {
087            return poll(consumer, null);
088        }
089    }
090
091    /**
092     * Returns an {@link ExchangeConflationQueue} whose appender is guaranteed to never return null on enqueue.  If the
093     * unsafe queue returns null on enqueue e.g. because the queue is empty and no exchange values can be retrieved yet,
094     * the specified factory is used to create a value.
095     *
096     * @param unsafeQueue   the unsafe queue possibly returning null values on enqueue
097     * @param valueFactory  the value factory used if the unsafe queue returned null when a value was enqueued
098     * @param <K> the type of the conflation key
099     * @param <V> the type of values in the queue
100     * @return a null-safe version of the queue that never returns null values when enqueuing values
101     */
102    static <K,V> ExchangeConflationQueue<K, V> nullSafe(final ExchangeConflationQueue<K,V> unsafeQueue,
103                                                        final Function<? super K, ? extends V> valueFactory) {
104        Objects.requireNonNull(unsafeQueue);
105        Objects.requireNonNull(valueFactory);
106        return new ExchangeConflationQueue<K, V>() {
107            final ThreadLocal<Appender<K,V>> appender = ThreadLocal.withInitial(() -> nullSafe(unsafeQueue.appender(), valueFactory));
108            @Override
109            public Appender<K, V> appender() {
110                return appender.get();
111            }
112
113            @Override
114            public ExchangePoller<K, V> poller() {
115                return unsafeQueue.poller();
116            }
117
118            @Override
119            public int size() {
120                return unsafeQueue.size();
121            }
122        };
123    }
124
125    /**
126     * Returns an {@link Appender} whose {@link Appender#enqueue(Object, Object) enqueue(..)} method is guaranteed to
127     * never return null.  If the unsafe appender returns null on enqueue e.g. because the queue is empty and no
128     * exchange values can be retrieved yet, the specified factory is used to create a value.
129     *
130     * @param unsafeAppender    the unsafe appender possibly returning null values on enqueue
131     * @param valueFactory      the value factory used if the unsafe enqueuing operation returned null
132     * @param <K> the type of the conflation key
133     * @param <V> the type of values in the queue
134     * @return a null-safe version of the appender that never returns null values when enqueuing values
135     */
136    static <K,V> Appender<K, V> nullSafe(final Appender<K,V> unsafeAppender,
137                                         final Function<? super K, ? extends V> valueFactory) {
138        Objects.requireNonNull(unsafeAppender);
139        Objects.requireNonNull(valueFactory);
140        return (k, v) -> {
141            final V value = unsafeAppender.enqueue(k, v);
142            return value == null ?
143                    Objects.requireNonNull(valueFactory.apply(k), "value factory returned null") :
144                    value;
145        };
146    }
147}