summaryrefslogtreecommitdiffstats
path: root/src/common/atomic_ops.h
blob: c809e69639f63a37e1cabf7b242356619d78de42 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_types.h"

#if _MSC_VER
#include <intrin.h>
#else
#include <cstring>
#endif

namespace Common {

#if _MSC_VER

template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile T* pointer, T value, T expected);
template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile T* pointer, T value, T expected, T& actual);

template [[nodiscard]] inline bool AtomicCompareAndSwap<u8>(volatile u8* pointer, u8 value,
                                                            u8 expected) {
    const u8 result =
        _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
    return result == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u16>(volatile u16* pointer, u16 value,
                                                             u16 expected) {
    const u16 result =
        _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
    return result == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u32>(volatile u32* pointer, u32 value,
                                                             u32 expected) {
    const u32 result =
        _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
    return result == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u64>(volatile u64* pointer, u64 value,
                                                             u64 expected) {
    const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
                                                     value, expected);
    return result == expected;
}

[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
    return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
                                          value[0],
                                          reinterpret_cast<__int64*>(expected.data())) != 0;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u8>(volatile u8* pointer, u8 value,
                                                            u8 expected, u8& actual) {
    actual =
        _InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
    return actual == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u16>(volatile u16* pointer, u16 value,
                                                             u16 expected, u16& actual) {
    actual =
        _InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
    return actual == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u32>(volatile u32* pointer, u32 value,
                                                             u32 expected, u32& actual) {
    actual =
        _InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
    return actual == expected;
}

template [[nodiscard]] inline bool AtomicCompareAndSwap<u64>(volatile u64* pointer, u64 value,
                                                             u64 expected, u64& actual) {
    actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
                                           expected);
    return actual == expected;
}

[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
                                               u128& actual) {
    const bool result =
        _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
                                       value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
    actual = expected;
    return result;
}

[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
    u128 result{};
    _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
                                   result[0], reinterpret_cast<__int64*>(result.data()));
    return result;
}

#else

template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile T* pointer, T value, T expected) {
    return __sync_bool_compare_and_swap(pointer, expected, value);
}

[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
    unsigned __int128 value_a;
    unsigned __int128 expected_a;
    std::memcpy(&value_a, value.data(), sizeof(u128));
    std::memcpy(&expected_a, expected.data(), sizeof(u128));
    return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
}

template <typename T>
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile T* pointer, T value, T expected,
                                               T& actual) {
    actual = __sync_val_compare_and_swap(pointer, expected, value);
    return actual == expected;
}

[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
                                               u128& actual) {
    unsigned __int128 value_a;
    unsigned __int128 expected_a;
    unsigned __int128 actual_a;
    std::memcpy(&value_a, value.data(), sizeof(u128));
    std::memcpy(&expected_a, expected.data(), sizeof(u128));
    actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
    std::memcpy(actual.data(), &actual_a, sizeof(u128));
    return actual_a == expected_a;
}

[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
    unsigned __int128 zeros_a = 0;
    unsigned __int128 result_a =
        __sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);

    u128 result;
    std::memcpy(result.data(), &result_a, sizeof(u128));
    return result;
}

#endif

} // namespace Common