1 // Copyright 2021 Jean Pierre Cimalando
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 // SPDX-License-Identifier: Apache-2.0
19 #include <type_traits>
25 //------------------------------------------------------------------------------
26 // sync_bitset64: A lock-free synchronized bitset of size 64
28 // This is implemented by a single qword on 64-bit machine, otherwise a pair of
29 // dwords on 32-bit machine, which is to ensure lock-freedom.
31 // This bitset is synchronized but not atomic; a thread might see a
32 // partially updated set after a modification. Conceptually, it can be seen as
33 // atomic on individual bit flips (that is, masking operations do not race).
35 class sync_bitset64_single
;
36 class sync_bitset64_dual
;
38 // could use std::atomic::is_always_lock_free on C++17 and up
39 using sync_bitset64
= std::conditional
<
40 sizeof(intptr_t) <= 4, sync_bitset64_dual
, sync_bitset64_single
>::type
;
42 //------------------------------------------------------------------------------
43 class sync_bitset64_single
{
47 return bits_
.load(std::memory_order_relaxed
);
50 void store(uint64_t value
)
52 bits_
.store(value
, std::memory_order_relaxed
);
55 uint64_t exchange(uint64_t value
)
57 return bits_
.exchange(value
, std::memory_order_relaxed
);
60 uint64_t fetch_or(uint64_t value
)
62 return bits_
.fetch_or(value
, std::memory_order_relaxed
);
65 uint64_t fetch_and(uint64_t value
)
67 return bits_
.fetch_and(value
, std::memory_order_relaxed
);
70 uint64_t fetch_xor(uint64_t value
)
72 return bits_
.fetch_xor(value
, std::memory_order_relaxed
);
75 void operator|=(uint64_t value
)
80 void operator&=(uint64_t value
)
85 void operator^=(uint64_t value
)
91 std::atomic
<uint64_t> bits_
{0};
94 //------------------------------------------------------------------------------
95 class sync_bitset64_dual
{
99 return join(lobits_
.load(std::memory_order_relaxed
),
100 hibits_
.load(std::memory_order_relaxed
));
103 void store(uint64_t value
)
105 lobits_
.store(lo(value
), std::memory_order_relaxed
);
106 hibits_
.store(hi(value
), std::memory_order_relaxed
);
109 uint64_t exchange(uint64_t value
)
111 return join(lobits_
.exchange(lo(value
), std::memory_order_relaxed
),
112 hibits_
.exchange(hi(value
), std::memory_order_relaxed
));
115 uint64_t fetch_or(uint64_t value
)
117 return join(lobits_
.fetch_or(lo(value
), std::memory_order_relaxed
),
118 hibits_
.fetch_or(hi(value
), std::memory_order_relaxed
));
121 uint64_t fetch_and(uint64_t value
)
123 return join(lobits_
.fetch_and(lo(value
), std::memory_order_relaxed
),
124 hibits_
.fetch_and(hi(value
), std::memory_order_relaxed
));
127 uint64_t fetch_xor(uint64_t value
)
129 return join(lobits_
.fetch_xor(lo(value
), std::memory_order_relaxed
),
130 hibits_
.fetch_xor(hi(value
), std::memory_order_relaxed
));
133 void operator|=(uint64_t value
)
138 void operator&=(uint64_t value
)
143 void operator^=(uint64_t value
)
149 static constexpr uint64_t join(uint32_t lo
, uint32_t hi
)
151 return (uint64_t)lo
| ((uint64_t)hi
<< 32);
154 static constexpr uint32_t lo(uint64_t value
)
156 return (uint32_t)(value
& 0xFFFFFFFFu
);
159 static constexpr uint32_t hi(uint64_t value
)
161 return (uint32_t)(value
>> 32);
165 std::atomic
<uint32_t> lobits_
{0};
166 std::atomic
<uint32_t> hibits_
{0};