1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2017 Couchbase, Inc.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#pragma once
19
20#include <atomic>
21#include <chrono>
22
23namespace cb {
24/* Wrapper class around std::atomic. It provides atomic operations
25 * for std::chrono durations by operating on the underlying value
26 * obtained from the duration's count().
27 * Defaults to relaxed memory ordering, suitable for statistics.
28 */
29template <std::memory_order MemoryOrder =
30                  std::memory_order::memory_order_relaxed>
31class AtomicDuration {
32public:
33    AtomicDuration() {
34        store(std::chrono::steady_clock::duration::zero());
35    }
36
37    AtomicDuration(std::chrono::steady_clock::duration initial) {
38        store(initial);
39    }
40
41    explicit AtomicDuration(const AtomicDuration& other) {
42        store(other.load());
43    }
44
45    operator std::chrono::steady_clock::duration() const {
46        return load();
47    }
48
49    std::chrono::steady_clock::duration load() const {
50        return std::chrono::steady_clock::duration(value.load(MemoryOrder));
51    }
52
53    void store(std::chrono::steady_clock::duration desired) {
54        value.store(desired.count(), MemoryOrder);
55    }
56
57    std::chrono::steady_clock::duration fetch_add(
58            std::chrono::steady_clock::duration arg) {
59        return std::chrono::steady_clock::duration(
60                value.fetch_add(arg.count(), MemoryOrder));
61    }
62
63    std::chrono::steady_clock::duration fetch_sub(
64            std::chrono::steady_clock::duration arg) {
65        return std::chrono::steady_clock::duration(
66                value.fetch_sub(arg.count(), MemoryOrder));
67    }
68
69    AtomicDuration& operator=(std::chrono::steady_clock::duration val) {
70        store(val);
71        return *this;
72    }
73
74    AtomicDuration& operator+=(std::chrono::steady_clock::duration rhs) {
75        fetch_add(rhs);
76        return *this;
77    }
78
79    AtomicDuration& operator-=(std::chrono::steady_clock::duration rhs) {
80        fetch_sub(rhs);
81        return *this;
82    }
83
84    std::chrono::steady_clock::duration operator++() {
85        return fetch_add(std::chrono::steady_clock::duration(1)) +
86               std::chrono::steady_clock::duration(1);
87    }
88
89    std::chrono::steady_clock::duration operator++(int) {
90        return fetch_add(std::chrono::steady_clock::duration(1));
91    }
92
93    std::chrono::steady_clock::duration operator--() {
94        return fetch_sub(std::chrono::steady_clock::duration(1)) -
95               std::chrono::steady_clock::duration(1);
96    }
97
98    std::chrono::steady_clock::duration operator--(int) {
99        return fetch_sub(std::chrono::steady_clock::duration(1));
100    }
101
102private:
103    std::atomic<std::chrono::steady_clock::duration::rep> value;
104};
105} // namespace cb
106