001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2011-2013 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import mondrian.olap.Util;
013
014import java.util.concurrent.ConcurrentHashMap;
015
016/**
017 * A limited Map implementation which supports waiting for a value
018 * to be available when calling get(). Intended for use with
019 * producer/consumer queues, where a producer thread puts a value into
020 * the collection with a separate thread waiting to get that value.
021 * Currently used by the Actor implementations in
022 * <code>SegmentCacheManager</code> and <code>MonitorImpl</code>.
023 *
024 * <p><b>Thread safety</b>. BlockingHashMap is thread safe.  The class
025 * delegates all get and put operations to a ConcurrentHashMap. </p>
026 *
027 * @param <K> request (key) type
028 * @param <V> response (value) type
029 */
030public class BlockingHashMap<K, V> {
031    private final ConcurrentHashMap<K, SlotFuture<V>> map;
032
033    /**
034     * Creates a BlockingHashMap with given capacity.
035     *
036     * @param capacity Capacity
037     */
038    public BlockingHashMap(int capacity) {
039        map = new ConcurrentHashMap<K, SlotFuture<V>>(capacity);
040    }
041
042    /**
043     * Places a (request, response) pair onto the map.
044     *
045     * @param k key
046     * @param v value
047     */
048    public void put(K k, V v) {
049        map.putIfAbsent(k, new SlotFuture<V>());
050        map.get(k).put(v);
051    }
052
053    /**
054     * Retrieves the response from the map matching the given key,
055     * blocking until it is received.
056     *
057     * @param k key
058     * @return value
059     * @throws InterruptedException if interrupted while waiting
060     */
061    public V get(K k) throws InterruptedException {
062        map.putIfAbsent(k, new SlotFuture<V>());
063        V v = Util.safeGet(
064            map.get(k),
065            "Waiting to retrieve a value from BlockingHashMap.");
066        map.remove(k);
067        return v;
068    }
069}