2 * Copyright 2015-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/futures/Barrier.h>
19 namespace folly { namespace futures {
21 Barrier::Barrier(uint32_t n)
23 controlBlock_(allocateControlBlock()) { }
26 auto block = controlBlock_.load(std::memory_order_relaxed);
27 auto prev = block->valueAndReaderCount.load(std::memory_order_relaxed);
28 DCHECK_EQ(prev >> kReaderShift, 0u);
29 auto val = prev & kValueMask;
30 auto p = promises(block);
32 for (uint32_t i = 0; i < val; ++i) {
34 folly::make_exception_wrapper<std::runtime_error>("Barrier destroyed"));
37 freeControlBlock(controlBlock_);
40 auto Barrier::allocateControlBlock() -> ControlBlock* {
41 auto block = static_cast<ControlBlock*>(malloc(controlBlockSize(size_)));
43 throw std::bad_alloc();
45 block->valueAndReaderCount = 0;
47 auto p = promises(block);
50 for (i = 0; i < size_; ++i) {
51 new (p + i) BoolPromise();
55 p[i - 1].~BoolPromise();
63 void Barrier::freeControlBlock(ControlBlock* block) {
64 auto p = promises(block);
65 for (uint32_t i = size_; i != 0; --i) {
66 p[i - 1].~BoolPromise();
71 folly::Future<bool> Barrier::wait() {
72 // Load the current control block first. As we know there is at least
73 // one thread in the current epoch (us), this means that the value is
74 // < size_, so controlBlock_ can't change until we bump the value below.
75 auto block = controlBlock_.load(std::memory_order_acquire);
76 auto p = promises(block);
78 // Bump the value and record ourselves as reader.
79 // This ensures that block stays allocated, as the reader count is > 0.
80 auto prev = block->valueAndReaderCount.fetch_add(kReader + 1,
81 std::memory_order_acquire);
83 auto prevValue = static_cast<uint32_t>(prev & kValueMask);
84 DCHECK_LT(prevValue, size_);
85 auto future = p[prevValue].getFuture();
87 if (prevValue + 1 == size_) {
88 // Need to reset the barrier before fulfilling any futures. This is
89 // when the epoch is flipped to the next.
90 controlBlock_.store(allocateControlBlock(), std::memory_order_release);
93 for (uint32_t i = 1; i < size_; ++i) {
98 // Free the control block if we're the last reader at max value.
99 prev = block->valueAndReaderCount.fetch_sub(kReader,
100 std::memory_order_acq_rel);
101 if (prev == (kReader | uint64_t(size_))) {
102 freeControlBlock(block);
108 } // namespace futures