1a55bd86fSTrond Norbye/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2a55bd86fSTrond Norbye/*
3a55bd86fSTrond Norbye *     Copyright 2015 Couchbase, Inc.
4a55bd86fSTrond Norbye *
5a55bd86fSTrond Norbye *   Licensed under the Apache License, Version 2.0 (the "License");
6a55bd86fSTrond Norbye *   you may not use this file except in compliance with the License.
7a55bd86fSTrond Norbye *   You may obtain a copy of the License at
8a55bd86fSTrond Norbye *
9a55bd86fSTrond Norbye *       http://www.apache.org/licenses/LICENSE-2.0
10a55bd86fSTrond Norbye *
11a55bd86fSTrond Norbye *   Unless required by applicable law or agreed to in writing, software
12a55bd86fSTrond Norbye *   distributed under the License is distributed on an "AS IS" BASIS,
13a55bd86fSTrond Norbye *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14a55bd86fSTrond Norbye *   See the License for the specific language governing permissions and
15a55bd86fSTrond Norbye *   limitations under the License.
16a55bd86fSTrond Norbye */
17a55bd86fSTrond Norbye#pragma once
18a55bd86fSTrond Norbye
19a55bd86fSTrond Norbye#include <atomic>
20a55bd86fSTrond Norbye
2124c0a690STrond Norbyenamespace cb {
22a55bd86fSTrond Norbye
2324c0a690STrond Norbye/**
2424c0a690STrond Norbye * The RelaxedAtomic class wraps std::atomic<> and operates with
2524c0a690STrond Norbye * relaxed memory ordering.
2624c0a690STrond Norbye */
2724c0a690STrond Norbyetemplate <typename T>
2824c0a690STrond Norbyeclass RelaxedAtomic {
2924c0a690STrond Norbyepublic:
3024c0a690STrond Norbye    RelaxedAtomic() {
3124c0a690STrond Norbye        store(0);
3224c0a690STrond Norbye    }
3324c0a690STrond Norbye
3424c0a690STrond Norbye    RelaxedAtomic(const T& initial) {
3524c0a690STrond Norbye        store(initial);
3624c0a690STrond Norbye    }
3724c0a690STrond Norbye
3824c0a690STrond Norbye    explicit RelaxedAtomic(const RelaxedAtomic& other) {
3924c0a690STrond Norbye        store(other.load());
4024c0a690STrond Norbye    }
4124c0a690STrond Norbye
4224c0a690STrond Norbye    operator T() const {
4324c0a690STrond Norbye        return load();
4424c0a690STrond Norbye    }
4524c0a690STrond Norbye
4624c0a690STrond Norbye    T load() const {
4724c0a690STrond Norbye        return value.load(std::memory_order_relaxed);
4824c0a690STrond Norbye    }
4924c0a690STrond Norbye
5024c0a690STrond Norbye    void store(T desired) {
5124c0a690STrond Norbye        value.store(desired, std::memory_order_relaxed);
5224c0a690STrond Norbye    }
5324c0a690STrond Norbye
5424c0a690STrond Norbye    T fetch_add(T arg) {
5524c0a690STrond Norbye        return value.fetch_add(arg, std::memory_order_relaxed);
5624c0a690STrond Norbye    }
5724c0a690STrond Norbye
5824c0a690STrond Norbye    T fetch_sub(T arg) {
5924c0a690STrond Norbye        return value.fetch_sub(arg, std::memory_order_relaxed);
6024c0a690STrond Norbye    }
6124c0a690STrond Norbye
6224c0a690STrond Norbye    RelaxedAtomic& operator=(const RelaxedAtomic& rhs) {
6324c0a690STrond Norbye        store(rhs.load());
6424c0a690STrond Norbye        return *this;
6524c0a690STrond Norbye    }
6624c0a690STrond Norbye
6724c0a690STrond Norbye    RelaxedAtomic& operator+=(const T rhs) {
6824c0a690STrond Norbye        fetch_add(rhs);
6924c0a690STrond Norbye        return *this;
7024c0a690STrond Norbye    }
7124c0a690STrond Norbye
7224c0a690STrond Norbye    RelaxedAtomic& operator+=(const RelaxedAtomic& rhs) {
7324c0a690STrond Norbye        fetch_add(rhs.load());
7424c0a690STrond Norbye        return *this;
7524c0a690STrond Norbye    }
7624c0a690STrond Norbye
7724c0a690STrond Norbye    RelaxedAtomic& operator-=(const T rhs) {
7824c0a690STrond Norbye        fetch_sub(rhs);
7924c0a690STrond Norbye        return *this;
8024c0a690STrond Norbye    }
8124c0a690STrond Norbye
8224c0a690STrond Norbye    RelaxedAtomic& operator-=(const RelaxedAtomic& rhs) {
8324c0a690STrond Norbye        fetch_sub(rhs.load());
8424c0a690STrond Norbye        return *this;
8524c0a690STrond Norbye    }
8624c0a690STrond Norbye
8724c0a690STrond Norbye    T operator++() {
8824c0a690STrond Norbye        return fetch_add(1) + 1;
8924c0a690STrond Norbye    }
9024c0a690STrond Norbye
9124c0a690STrond Norbye    T operator++(int) {
9224c0a690STrond Norbye        return fetch_add(1);
9324c0a690STrond Norbye    }
9424c0a690STrond Norbye
9524c0a690STrond Norbye    T operator--() {
9624c0a690STrond Norbye        return fetch_sub(1) - 1;
9724c0a690STrond Norbye    }
9824c0a690STrond Norbye
9924c0a690STrond Norbye    T operator--(int) {
10024c0a690STrond Norbye        return fetch_sub(1);
10124c0a690STrond Norbye    }
10224c0a690STrond Norbye
10324c0a690STrond Norbye    RelaxedAtomic& operator=(T val) {
10424c0a690STrond Norbye        store(val);
10524c0a690STrond Norbye        return *this;
10624c0a690STrond Norbye    }
10724c0a690STrond Norbye
10824c0a690STrond Norbye    void reset() {
10924c0a690STrond Norbye        store(0);
11024c0a690STrond Norbye    }
11124c0a690STrond Norbye
11224c0a690STrond Norbye    void setIfGreater(const T& val) {
11324c0a690STrond Norbye        do {
11424c0a690STrond Norbye            T currval = load();
11524c0a690STrond Norbye            if (val > currval) {
11624c0a690STrond Norbye                if (value.compare_exchange_weak(
11724c0a690STrond Norbye                            currval, val, std::memory_order_relaxed)) {
1188b9a714bSTim Bradgate                    break;
1198b9a714bSTim Bradgate                }
12024c0a690STrond Norbye            } else {
12124c0a690STrond Norbye                break;
12224c0a690STrond Norbye            }
12324c0a690STrond Norbye        } while (true);
12424c0a690STrond Norbye    }
12524c0a690STrond Norbye
12624c0a690STrond Norbye    void setIfGreater(const RelaxedAtomic& val) {
12724c0a690STrond Norbye        setIfGreater(val.load());
12824c0a690STrond Norbye    }
12924c0a690STrond Norbye
13024c0a690STrond Norbye    void setIfSmaller(const T& val) {
13124c0a690STrond Norbye        do {
13224c0a690STrond Norbye            T currval = load();
13324c0a690STrond Norbye            if (val < currval) {
13424c0a690STrond Norbye                if (value.compare_exchange_weak(
13524c0a690STrond Norbye                            currval, val, std::memory_order_relaxed)) {
1368b9a714bSTim Bradgate                    break;
1378b9a714bSTim Bradgate                }
13824c0a690STrond Norbye            } else {
13924c0a690STrond Norbye                break;
14024c0a690STrond Norbye            }
14124c0a690STrond Norbye        } while (true);
14224c0a690STrond Norbye    }
14324c0a690STrond Norbye
14424c0a690STrond Norbye    void setIfSmaller(const RelaxedAtomic& val) {
14524c0a690STrond Norbye        setIfSmaller(val.load());
14624c0a690STrond Norbye    }
14724c0a690STrond Norbye
14824c0a690STrond Norbye    /**
14924c0a690STrond Norbye     * Implementation similar to fetch_add. This function is needed as not
15024c0a690STrond Norbye     * all implementations of RelaxedAtomic are able to make use of the
15124c0a690STrond Norbye     * operator overload methods if they are not of integral type. In the
15224c0a690STrond Norbye     * first case, using the operator overload is preferred as it is more
15324c0a690STrond Norbye     * optimized, this should only be used if that is not possible.
15424c0a690STrond Norbye     * @param val The value to add to that of the one stored
15524c0a690STrond Norbye     */
15624c0a690STrond Norbye    void setAdd(const T& val) {
15724c0a690STrond Norbye        do {
15824c0a690STrond Norbye            T currval = load();
15924c0a690STrond Norbye            if (value.compare_exchange_weak(
16024c0a690STrond Norbye                        currval, currval + val, std::memory_order_relaxed)) {
16124c0a690STrond Norbye                break;
16224c0a690STrond Norbye            }
16324c0a690STrond Norbye        } while (true);
16424c0a690STrond Norbye    }
16524c0a690STrond Norbye
16624c0a690STrond Norbye    /**
16724c0a690STrond Norbye     * Implementation similar to fetch_add. This function is needed as not
16824c0a690STrond Norbye     * all implementations of RelaxedAtomic are able to make use of the
16924c0a690STrond Norbye     * operator overload methods if they are not of integral type. In the
17024c0a690STrond Norbye     * first case, using the operator overload is preferred as it is more
17124c0a690STrond Norbye     * optimized, this should only be used if that is not possible.
17224c0a690STrond Norbye     * @param val The RelaxedAtomic containing the value to add to that of
17324c0a690STrond Norbye     * the one stored
17424c0a690STrond Norbye     */
17524c0a690STrond Norbye    void setAdd(const RelaxedAtomic& val) {
17624c0a690STrond Norbye        setAdd(val.load());
17724c0a690STrond Norbye    }
17824c0a690STrond Norbye
17924c0a690STrond Norbye    /**
18024c0a690STrond Norbye     * Implementation similar to fetch_sub. This function is needed as not
18124c0a690STrond Norbye     * all implementations of RelaxedAtomic are able to make use of the
18224c0a690STrond Norbye     * operator overload methods if they are not of integral type. In the
18324c0a690STrond Norbye     * first case, using the operator overload is preferred as it is more
18424c0a690STrond Norbye     * optimized, this should only be used if that is not possible.
18524c0a690STrond Norbye     * @param val The value to subtract to that of the one stored
18624c0a690STrond Norbye     */
18724c0a690STrond Norbye    void setSub(const T& val) {
18824c0a690STrond Norbye        do {
18924c0a690STrond Norbye            T currval = load();
19024c0a690STrond Norbye            if (value.compare_exchange_weak(
19124c0a690STrond Norbye                        currval, currval - val, std::memory_order_relaxed)) {
19224c0a690STrond Norbye                break;
19324c0a690STrond Norbye            }
19424c0a690STrond Norbye        } while (true);
19524c0a690STrond Norbye    }
19624c0a690STrond Norbye
19724c0a690STrond Norbye    /**
19824c0a690STrond Norbye     * Implementation similar to fetch_sub. This function is needed as not
19924c0a690STrond Norbye     * all implementations of RelaxedAtomic are able to make use of the
20024c0a690STrond Norbye     * operator overload methods if they are not of integral type. In the
20124c0a690STrond Norbye     * first case, using the operator overload is preferred as it is more
20224c0a690STrond Norbye     * optimized, this should only be used if that is not possible.
20324c0a690STrond Norbye     * @param val The RelaxedAtomic containing the value to subtract to that
20424c0a690STrond Norbye     * of the one stored
20524c0a690STrond Norbye     */
20624c0a690STrond Norbye    void setSub(const RelaxedAtomic& val) {
20724c0a690STrond Norbye        setSub(val.load());
20824c0a690STrond Norbye    }
20924c0a690STrond Norbye
21024c0a690STrond Norbye    bool compare_exchange_weak(T& expected, T desired) {
21124c0a690STrond Norbye        return value.compare_exchange_weak(expected,
21224c0a690STrond Norbye                                           desired,
21324c0a690STrond Norbye                                           std::memory_order_release,
21424c0a690STrond Norbye                                           std::memory_order_relaxed);
21524c0a690STrond Norbye    }
21624c0a690STrond Norbye
21724c0a690STrond Norbye    T exchange(T desired) {
21824c0a690STrond Norbye        return value.exchange(desired, std::memory_order_relaxed);
21924c0a690STrond Norbye    }
22024c0a690STrond Norbye
22124c0a690STrond Norbyeprivate:
22224c0a690STrond Norbye    std::atomic<T> value;
22324c0a690STrond Norbye};
22424c0a690STrond Norbye} // namespace cb
225