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

com.swirlds.fchashmap.FCHashMap Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.5
Show newest version
/*
 * Copyright (C) 2019-2024 Hedera Hashgraph, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.swirlds.fchashmap;

import static java.util.Objects.requireNonNull;

import com.swirlds.common.FastCopyable;
import com.swirlds.common.exceptions.ReferenceCountException;
import com.swirlds.fchashmap.internal.FCHashMapEntrySet;
import com.swirlds.fchashmap.internal.FCHashMapFamily;
import com.swirlds.fchashmap.internal.Mutation;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.AbstractMap;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 

* A map that with {@link java.util.HashMap HashMap} like O(1) performance that provides {@link FastCopyable} * semantics. *

* *

* All operations are thread safe if performed simultaneously on different copies of the map. *

* *

* It is always thread safe to read unreleased immutable copies (as long as not done concurrently with the deletion of * that copy). *

* *

* It is thread safe safe to read and write simultaneously to the mutable copy of the map. {@link #size} may return * incorrect results if executed concurrently with an operation that modifies the size. *

* *

* The following operations are not thread safe: *

* *
    *
  • calling {@link #copy()} concurrently with write operations
  • *
  • calling {@link #release()} concurrently with read or write operations
  • *
* * @param the type of the key * @param the type of the value */ public class FCHashMap extends AbstractMap implements FastCopyable { /** * split the binary tree at this depth relative to the root of the binary tree. The tree will be split into * 2^split-factor subtrees, and each subtree will be eligible to be handled on a separate thread. */ public static final int REBUILD_SPLIT_FACTOR = 7; /** * rebuilding the FCHashMap in a MerkleMap, use this many threads to rebuild the tree. */ public static final int REBUILD_THREAD_COUNT = 24; /** * When a copy of an FCHashMap is made, that copy is in the same family as the original. A sequence of copies form a * single family. The FCHashMapFamily object manages the data shared between copies in a family, as well as managing * the lifecycle and eventual deletion of data when it is no longer needed by any un-deleted copy in the family. */ final FCHashMapFamily family; /** * Monotonically increasing version number that is incremented every time copy() is called on the mutable copy. */ private final long version; /** * Is this object a mutable object? */ private boolean immutable; /** * The current size of the map. */ private final AtomicInteger size; /** * Tracks if this particular object has been deleted. */ private final AtomicBoolean released = new AtomicBoolean(false); /** * Create a new FCHashMap. */ public FCHashMap() { this(0); } /** * Create a new FCHashMap. * * @param capacity the initial capacity of the map */ public FCHashMap(final int capacity) { family = new FCHashMapFamily<>(capacity); version = 0; immutable = false; size = new AtomicInteger(0); } /** * Copy constructor. * * @param that the map to copy */ private FCHashMap(final FCHashMap that) { this.family = that.family; this.version = family.copyMap(); size = new AtomicInteger(that.size.get()); immutable = false; } /** * Get the {@link FCHashMapFamily} that this map belongs to. */ FCHashMapFamily getFamily() { return family; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public synchronized FCHashMap copy() { throwIfImmutable(); throwIfDestroyed(); try { return new FCHashMap<>(this); } finally { this.immutable = true; } } /** * {@inheritDoc} */ @Override public boolean isImmutable() { return this.immutable; } /** *

* Use this to clean up resources held by this copy. Failure to call delete on a copy before it is garbage collected * will result in a memory leak. *

* *

* Not thread safe. Must not be called at the same time another thread is attempting to read from this copy. *

*/ @Override public synchronized boolean release() { final boolean previouslyReleased = released.getAndSet(true); if (previouslyReleased) { throw new ReferenceCountException("this object has already been released"); } family.releaseMap(version); return true; } /** * Check to see if this copy has been deleted. */ @Override public boolean isDestroyed() { return released.get(); } /** * {@inheritDoc} */ @Override public int size() { return size.get(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public boolean containsKey(final Object key) { final Mutation mutation = family.getMutation(version, (K) key); return mutation != null && mutation.getValue() != null; } /** *

* Directly inject a value into the map. This method designed for rapid initialization of an FCHashMap, and should * only be used on maps being initialized. Should not be called after the map has been copied or modified by any * method other than this method.. *

* *

* This method does not update the size of the data structure. After all injections have been completed, call * {@link #initialResize()}. *

* *

* It is thread safe to use this method concurrently. *

*/ public void initialInjection(final K key, final V value) { family.getData().put(key, new Mutation<>(version, value, null)); } /** * This method MUST be called if the FCHashMap has been initialized using {@link #initialInjection(Object, Object)}. * If called, must be called before any copies of the map are made or any modifications are made by any method other * than {@link #initialInjection(Object, Object)}. */ public void initialResize() { size.set(family.getData().size()); } /** * Returns the version of the copy. * * @return the version of the copy */ public long getVersion() { return version; } /** * Not thread safe on an immutable copy of the map if it is possible that another thread may have deleted the map * copy. Map deletion and reads against the map must be externally synchronized. The function hasBeenDeleted() can * be used to check to see if the copy has been deleted. */ @SuppressWarnings("unchecked") @Override public V get(final Object key) { if (key == null) { throw new NullPointerException("Null keys are not allowed"); } final Mutation mutation = family.getMutation(version, (K) key); return mutation == null ? null : mutation.getValue(); } /** *

* Get a value that is safe to directly modify. If value has been modified this round then return it. If value was * modified in a previous round, call {@link FastCopyable#copy()} on it, insert it into the map, and return it. If * the value is null, then return null. *

* *

* It is not necessary to manually re-insert the returned value back into the map. *

* *

* This method is only permitted to be used on maps that contain values that implement {@link FastCopyable}. Using * this method on maps that contain values that do not implement {@link FastCopyable} will result in undefined * behavior. *

* * @param key the key * @return a {@link ModifiableValue} that contains a value is safe to directly modify, or null if the key is not in * the map */ public ModifiableValue getForModify(final K key) { throwIfImmutable(); return family.getForModify(key); } /** * {@inheritDoc} * * @throws NullPointerException if the key or value is null */ @Override public V put(@NonNull final K key, @NonNull final V value) { requireNonNull(key, "key must not be null"); requireNonNull(value, "value must not be null"); throwIfImmutable(); return family.mutate(key, value, size); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public V remove(@NonNull final Object key) { requireNonNull(key, "key must not be null"); throwIfImmutable(); return family.mutate((K) key, null, size); } /** * {@inheritDoc} */ @Override public void clear() { for (final K k : keySet()) { remove(k); } } /** * {@inheritDoc} */ @Override public Set> entrySet() { return new FCHashMapEntrySet<>(this, family); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy