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

org.voltcore.utils.COWMap Maven / Gradle / Ivy

/* This file is part of VoltDB.
 * Copyright (C) 2008-2020 VoltDB Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with VoltDB.  If not, see .
 */
package org.voltcore.utils;

import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicReference;

import com.google_voltpatches.common.base.Preconditions;
import com.google_voltpatches.common.collect.ForwardingMap;
import com.google_voltpatches.common.collect.ImmutableMap;
import com.google_voltpatches.common.collect.ImmutableMap.Builder;

/**
 * Key set, value set, and entry set are all immutable as are their iterators.
 * Otherwise behaves as you would expect.
 */
public class COWMap  extends ForwardingMap implements ConcurrentMap {
    private final AtomicReference> m_map;

    public COWMap() {
        m_map = new AtomicReference>(new Builder().build());
    }

    public COWMap(Map map) {
        if (map == null) {
            throw new IllegalArgumentException("Wrapped map cannot be null");
        }
        m_map = new AtomicReference>(new Builder().putAll(map).build());
    }

    @Override
    public V put(K key, V value) {
        while (true) {
            ImmutableMap original = m_map.get();
            Builder builder = new Builder();
            V oldValue = null;
            boolean replaced = false;
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    oldValue = entry.getValue();
                    builder.put(key, value);
                    replaced = true;
                } else {
                    builder.put(entry);
                }
            }
            if (!replaced) {
                builder.put(key, value);
            }
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                return oldValue;
            }
        }
    }

    @Override
    public V remove(Object key) {
        Preconditions.checkNotNull(key);
        while (true) {
            ImmutableMap original = m_map.get();
            Builder builder = new Builder();
            V oldValue = null;
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    oldValue = entry.getValue();
                } else {
                    builder.put(entry);
                }
            }
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original,copy)) {
                return oldValue;
            }
        }
    }

    @Override
    public void putAll(Map m) {
        while (true) {
            ImmutableMap original = m_map.get();
            Builder builder = new Builder();
            for (Map.Entry entry : original.entrySet()) {
                if (!m.containsKey(entry.getKey())) {
                    builder.put(entry);
                }
            }
            builder.putAll(m);
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                return;
            }
        }
    }

    @Override
    public void clear() {
        m_map.set(new Builder().build());
    }

    @Override
    protected Map delegate() {
        return m_map.get();
    }

    @Override
    public V putIfAbsent(K key, V value) {
        V existingValue;
        while ((existingValue = get(key)) == null) {
            ImmutableMap original = m_map.get();
            if ((existingValue = original.get(key)) != null) break;

            Builder builder = new Builder();
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    throw new RuntimeException("Shouldn't happen already checked");
                } else {
                    builder.put(entry);
                }
            }
            builder.put(key, value);
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                break;
            }
        }
        return existingValue;
    }

    @Override
    public V get(Object key) {
        Preconditions.checkNotNull(key);
        return delegate().get(key);
    }

    @Override
    public boolean containsKey(Object key) {
        Preconditions.checkNotNull(key);
        return delegate().containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        Preconditions.checkNotNull(value);
        return delegate().containsValue(value);
    }

    @Override
    public boolean remove(Object key, Object value) {
        Preconditions.checkNotNull(key);
        if (value == null) return false;
        while (true) {
            ImmutableMap original = m_map.get();
            V existingValue = original.get(key);
            if (existingValue == null) break;
            if (!existingValue.equals(value)) break;

            Builder builder = new Builder();
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    continue;
                } else {
                    builder.put(entry);
                }
            }
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        Preconditions.checkNotNull(key);
        Preconditions.checkNotNull(oldValue);
        Preconditions.checkNotNull(newValue);
        while (true) {
            ImmutableMap original = m_map.get();
            V existingValue = original.get(key);
            if (existingValue == null) break;
            if (!existingValue.equals(oldValue)) break;

            Builder builder = new Builder();
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    continue;
                } else {
                    builder.put(entry);
                }
            }
            builder.put(key, newValue);
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public V replace(K key, V value) {
        Preconditions.checkNotNull(key);
        Preconditions.checkNotNull(value);
        while (true) {
            ImmutableMap original = m_map.get();
            V existingValue = original.get(key);
            if (existingValue == null) break;

            Builder builder = new Builder();
            for (Map.Entry entry : original.entrySet()) {
                if (entry.getKey().equals(key)) {
                    continue;
                } else {
                    builder.put(entry);
                }
            }
            builder.put(key, value);
            ImmutableMap copy = builder.build();
            if (m_map.compareAndSet(original, copy)) {
                return existingValue;
            }
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy