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

org.openjdk.jcstress.samples.ConcurrencySample_02_ConcurrentHashMap Maven / Gradle / Ivy

There is a newer version: 0.11
Show newest version
/*
 * Copyright (c) 2016, Red Hat Inc.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.openjdk.jcstress.samples;

import org.openjdk.jcstress.annotations.*;
import org.openjdk.jcstress.infra.results.LL_Result;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static org.openjdk.jcstress.annotations.Expect.*;

public class ConcurrencySample_02_ConcurrentHashMap {

    /*
      ----------------------------------------------------------------------------------------------------------

        This test demonstrates the operation atomicity tests, taking
        ConcurrentHashMap-backed Multimap as the example.
    */

    public static class Multimap {
        Map> map = new ConcurrentHashMap<>();

        /*
            Contains a nasty race on putting the list into the map.
         */
        void addBroken(String key, String val) {
            List list = map.get(key);
            if (list == null) {
                list = Collections.synchronizedList(new ArrayList<>());
                map.put(key, list);
            }
            list.add(val);
        }

        /*
            Solves the race with putIfAbsent.
         */
        void addCorrect(String key, String val) {
            List list = map.get(key);
            if (list == null) {
                list = Collections.synchronizedList(new ArrayList<>());
                List exist = map.putIfAbsent(key, list);
                if (exist != null) {
                    list = exist;
                }
            }
            list.add(val);
        }

        /*
            Solves the race with computeIfAbsent.
         */
        void addCorrect8(String key, String val) {
            List list = map.computeIfAbsent(key,
                    k -> Collections.synchronizedList(new ArrayList<>()));
            list.add(val);
        }

        String poll(String key, int idx) {
            List list = map.get(key);
            return (list.size() > idx) ? list.get(idx) : null;
        }
    }

    /*
       ----------------------------------------------------------------------------------------------------------

        Broken Multimap is broken, it contains a race.

              [OK] org.openjdk.jcstress.samples.ConcurrencySample_02_ConcurrentHashMap.BrokenMultimap
            (JVM args: [-server])
          Observed state   Occurrences              Expectation  Interpretation
                Bar, Baz     5,635,469               ACCEPTABLE  Both updates.
               Bar, null     1,691,183   ACCEPTABLE_INTERESTING  One update lost.
                Baz, Bar     5,821,971               ACCEPTABLE  Both updates.
               Baz, null     1,690,007   ACCEPTABLE_INTERESTING  One update lost.
     */

    @JCStressTest
    @Outcome(id = { "Bar, null", "Baz, null" }, expect = ACCEPTABLE_INTERESTING, desc = "One update lost.")
    @Outcome(id = { "Bar, Baz", "Baz, Bar"},    expect = ACCEPTABLE, desc = "Both updates.")
    @State
    public static class BrokenMultimap extends Multimap {
        @Actor
        public void actor1() {
            addBroken("Foo", "Bar");
        }

        @Actor
        public void actor2() {
            addBroken("Foo", "Baz");
        }

        @Arbiter
        public void arbiter(LL_Result s) {
            s.r1 = poll("Foo", 0);
            s.r2 = poll("Foo", 1);
        }
    }

    /*
       ----------------------------------------------------------------------------------------------------------

        putIfAbsent-style multimap does atomic updates.

              [OK] org.openjdk.jcstress.samples.ConcurrencySample_02_ConcurrentHashMap.CorrectMultimap
            (JVM args: [-server])
          Observed state   Occurrences   Expectation  Interpretation
                Bar, Baz     6,948,547    ACCEPTABLE  Both updates.
               Bar, null             0     FORBIDDEN  One update lost.
                Baz, Bar     7,360,653    ACCEPTABLE  Both updates.
               Baz, null             0     FORBIDDEN  One update lost.
     */


    @JCStressTest
    @Outcome(id = { "Bar, null", "Baz, null" }, expect = FORBIDDEN, desc = "One update lost.")
    @Outcome(id = { "Bar, Baz", "Baz, Bar"},    expect = ACCEPTABLE, desc = "Both updates.")
    @State
    public static class CorrectMultimap extends Multimap {
        @Actor
        public void actor1() {
            addCorrect("Foo", "Bar");
        }

        @Actor
        public void actor2() {
            addCorrect("Foo", "Baz");
        }

        @Arbiter
        public void arbiter(LL_Result s) {
            s.r1 = poll("Foo", 0);
            s.r2 = poll("Foo", 1);
        }
    }




    /*
       ----------------------------------------------------------------------------------------------------------

        computeIfAbsent-style multimap does atomic updates.

              [OK] org.openjdk.jcstress.samples.ConcurrencySample_02_ConcurrentHashMap.CorrectJDK8Multimap
            (JVM args: [-server])
          Observed state   Occurrences   Expectation  Interpretation
                Bar, Baz     6,250,933    ACCEPTABLE  Both updates.
               Bar, null             0     FORBIDDEN  One update lost.
                Baz, Bar     6,412,677    ACCEPTABLE  Both updates.
               Baz, null             0     FORBIDDEN  One update lost.
     */

    @JCStressTest
    @Outcome(id = { "Bar, null", "Baz, null" }, expect = FORBIDDEN, desc = "One update lost.")
    @Outcome(id = { "Bar, Baz", "Baz, Bar"},    expect = ACCEPTABLE, desc = "Both updates.")
    @State
    public static class CorrectJDK8Multimap extends Multimap {
        @Actor
        public void actor1() {
            addCorrect8("Foo", "Bar");
        }

        @Actor
        public void actor2() {
            addCorrect8("Foo", "Baz");
        }

        @Arbiter
        public void arbiter(LL_Result s) {
            s.r1 = poll("Foo", 0);
            s.r2 = poll("Foo", 1);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy