All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.staslev.concurrent.AtomicMapOperationsForImmutableValues Maven / Gradle / Ivy

package com.github.staslev.concurrent;

import java.util.concurrent.ConcurrentMap;

/**
 *
 * Provides atomic non blocking operations on maps with (conceptually) immutable values, i.e., they will be replaced
 * with new instances upon updating their value, rather than mutating the exiting value in place.
 *
 */
public class AtomicMapOperationsForImmutableValues {

  AtomicMapOperationsForImmutableValues() {
  }

  private  boolean atomicallyInsertNewEntry(final ConcurrentMap map, final K key, final V previousValueOrNull, final V nextValue) {

    boolean newKeyInserted;

    try {
      newKeyInserted = previousValueOrNull == null && map.putIfAbsent(key, nextValue) == null;
    } catch (AssertionError assertionError) {
      // a defensive catch against an assertion error occasionally raised by the NonBlockingHashMap class
      // should probably be looked into sometime, and ideally removed.
      newKeyInserted = false;
    }

    return newKeyInserted;
  }

  private  boolean atomicallyUpdateExistingEntry(final ConcurrentMap map, final K key, final V previousValue, final V nextValue) {

    boolean exitingBucketIncreased;

    try {
      exitingBucketIncreased = previousValue != null && map.replace(key, previousValue, nextValue);
    } catch (AssertionError assertionError) {
      // a defensive catch against an assertion error occasionally raised by the NonBlockingHashMap class
      // should probably be looked into sometime, and ideally removed.
      exitingBucketIncreased = false;
    }

    return exitingBucketIncreased;
  }

  /**
   * Atomically sets the value of key to the result of applying the oldOrNullToNewValueTransformer
   * REGARDLESS of whether key WAS, OR WAS NOT, present in the map.
   *
   * @param map The map to perform the operation on.
   * @param key The key whose value is to be put or transformed.
   * @param oldOrNullToNewValueTransformer The transform function to be applied in order to produce values for the given key.
   *                                       This function WILL BE PASSED NULL in case the given key was not present in the map,
   *                                       in which case it is expected to produce an initial value. Should be thread safe.
   * @param  The type of the keys.
   * @param  The type of the values.
   */
  public  void putOrTransform(final ConcurrentMap map, final K key,
      final NonBlockingOperations.Transformer oldOrNullToNewValueTransformer) {

    V previousValueOrNull;
    V nextValue;

    do {
      previousValueOrNull = map.get(key);
      nextValue = oldOrNullToNewValueTransformer.transform(previousValueOrNull);
    } while (!atomicallyUpdateExistingEntry(map, key, previousValueOrNull, nextValue)
        && !atomicallyInsertNewEntry(map, key, previousValueOrNull, nextValue));
  }

  /**
   * Atomically sets the value of key to the result of applying the aggregator
   * REGARDLESS of whether key WAS, OR WAS NOT, present in the map.
   *
   * @param map The map to perform the operation on.
   * @param key The key whose value is to be put or transformed.
   * @param aggregator The aggregator responsible for producing a transformed (aggregated) value given new input. Should be thread safe.
   * @param input The input passed to the aggregator to produce a value. 
   * @param  The type of the keys.
   * @param  The type of the values.
   * @param  The type of the input.
   */
  public  void putOrAggregate(final ConcurrentMap map, final K key, final NonBlockingOperations.Aggregator aggregator, T input) {

    V previousValueOrNull;
    V nextValue;

    do {
      previousValueOrNull = map.get(key);
      nextValue = aggregator.aggregate(input, previousValueOrNull);
    } while (!atomicallyUpdateExistingEntry(map, key, previousValueOrNull, nextValue)
        && !atomicallyInsertNewEntry(map, key, previousValueOrNull, nextValue));
  }

  /**
   * In case key IS ALREADY PRESENT in the map, atomically sets its value to the value produced by applying the aggregator.
   * In case the given key was not present in the map, no action will take place.
   *
   * @param map The map to perform the operation on.
   * @param key The key whose value is to be put or transformed, in case the given key already present in the map.
   * @param aggregator The aggregator responsible for producing a transformed (aggregated) value given new input. Should be thread safe.
   * @param input The input passed to the aggregator to produce a value.
   * @param  The type of the keys.
   * @param  The type of the values.
   * @param  The type of the input.
   * @return true if the value of the given key was transformed, otherwise (if key was not present), false.
   */
  public  boolean aggregateIfPresent(final ConcurrentMap map, final K key, final NonBlockingOperations.Aggregator aggregator,
      T input) {

    V previousValue;
    V nextValue;

    do {
      previousValue = map.get(key);
      if (previousValue != null) {
        nextValue = aggregator.aggregate(input, previousValue);
      } else {
        return false;
      }
    } while (!atomicallyUpdateExistingEntry(map, key, previousValue, nextValue));

    return true;
  }

  /**
   * In case key IS ALREADY PRESENT in the map, atomically sets its value to the value produced by applying the oldOrNullToNewValueTransformer.
   * In case the given key was not present in the map, no action will take place.
   *
   * @param map The map to perform the operation on.
   * @param key The key whose value is to be put or transformed, in case the given key already present in the map.
   * @param nonNullOldToNewValueTransformer The transform function to be applied on an EXISTING value corresponding to the given key.
   *                                        This function WILL NOT BE PASSED NULL values. Should be thread safe.
   * @param  The type of the keys.
   * @param  The type of the values.
   * @return true if the value of the given key was transformed, otherwise (if key was not present), false.
   */
  public  boolean transformIfPresent(final ConcurrentMap map, final K key,
      final NonBlockingOperations.Transformer nonNullOldToNewValueTransformer) {

    V previousValue;
    V nextValue;

    do {
      previousValue = map.get(key);
      if (previousValue != null) {
        nextValue = nonNullOldToNewValueTransformer.transform(previousValue);
      } else {
        return false;
      }
    } while (!atomicallyUpdateExistingEntry(map, key, previousValue, nextValue));

    return true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy