001/**
002 * The MIT License (MIT)
003 *
004 * Copyright (c) 2019 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.function.Supplier;
027
028/**
029 * A listener for an {@link ConflationQueue.Appender appender} of a {@link ConflationQueue} for instance useful to
030 * record performance metrics related to enqueue operations.  The listener or a supplier thereof is usually passed to
031 * the constructor of a conflation queue.  Note that listeners must be thread safe if used in a multi-producer
032 * environment;  best practice is to use separate listener instances for each producer thread e.g. by using
033 * {@link #threadLocal(Supplier)} or {@link #threadLocalSupplier(Supplier)}.
034 *
035 * @param <K> the type of the conflation key
036 * @param <V> the type of values in the queue
037 */
038@FunctionalInterface
039public interface AppenderListener<K,V> {
040    /**
041     * The type of conflation that occurred when an element was enqueued.
042     */
043    enum Conflation {
044        /**
045         * No conflation occurred, that is, no other value with the same conflation key exists in the queue at the
046         * time of enqueueing the value.
047         */
048        UNCONFLATED,
049        /**
050         * Conflation occurred and the existing old value present in the queue was evicted and replaced by the newly
051         * enqueued value.
052         */
053        EVICTED,
054        /**
055         * Conflation occurred and the existing old element present in the queue was merged with the newly enqueued
056         * element via a {@link Merger};  the result of the merge operation is the value in the queue now.
057         */
058        MERGED
059    }
060
061    /**
062     * Called whenever a value is {@link ConflationQueue.Appender#enqueue(Object, Object) enqueued}.
063     *
064     * @param queue         the conflation queue, sometimes useful to record queue size metrics, never null
065     * @param key           the conflation key, never null
066     * @param newValue      the value that has been enqueued (the merged value if conflation==MERGED), never null
067     * @param oldValue      the old value that is replaced, unless conflation=UNCONFLATED in which case old value is
068     *                      either null or the exchange value in case of an {@link ExchangeConflationQueue}
069     * @param conflation    the type of conflation that has occurred, if any
070     */
071    void enqueued(ConflationQueue<? extends K, ? extends V> queue, K key, V newValue, V oldValue, Conflation conflation);
072
073    /**
074     * Constant for a no-op listener.
075     */
076    AppenderListener<Object,Object> NOOP = (q, k, v, o, c) -> {};
077
078    /**
079     * Creates an appender listener that delegates to thread-local listener instances created on demand by the given
080     * supplier.  This listener can be passed to the constructors of {@link AtomicConflationQueue} which use a single
081     * appender instance for all producer threads.
082     *
083     * @param listenerSupplier  the supplier acting as factory for per-thread listener instances
084     * @param <K> the type of the conflation key
085     * @param <V> the type of values in the queue
086     * @return a listener that delegates to thread-local listener instances
087     */
088    static <K,V> AppenderListener<K,V> threadLocal(final Supplier<? extends AppenderListener<K,V>> listenerSupplier) {
089        final ThreadLocal<AppenderListener<K,V>> threadLocal = ThreadLocal.withInitial(listenerSupplier);
090        return (q,k,v,o,c) -> threadLocal.get().enqueued(q,k,v,o,c);
091    }
092
093    /**
094     * Creates a thread local supplier that creates a new instance once for every caller thread.  This listener can be
095     * passed to the constructors of {@link EvictConflationQueue} and {@link MergeConflationQueue} which use a per
096     * thread appender instances;  the returned supplier will create a new thread-local listener instance for every
097     * appender instance that is created.
098     *
099     * @param listenerSupplier  the supplier acting as factory for per-thread listener instances
100     * @param <L> the appender listener (sub-)type
101     * @return a supplier creating thread-local listener instances
102     */
103    static <L extends AppenderListener<?,?>> Supplier<L> threadLocalSupplier(final Supplier<L> listenerSupplier) {
104        return ThreadLocal.withInitial(listenerSupplier)::get;
105    }
106}