/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/Likely.h>
#include <folly/detail/Futex.h>
#include <folly/portability/Asm.h>
+#include <folly/synchronization/WaitOptions.h>
#include <glog/logging.h>
/// instruction to the critical path of posters.
///
/// Wait options:
-/// The subclass WaitOptions contains optional per call setting for
-/// pre-block spin duration: Calls to wait(), try_wait_until(), and
-/// try_wait_for() block only after the passage of the pre-block
-/// period. The default pre-block duration is 10 microseconds. The
-/// pre block option is applicable only if MayBlock is true.
+/// WaitOptions contains optional per call setting for spin-max duration:
+/// Calls to wait(), try_wait_until(), and try_wait_for() block only after the
+/// passage of the spin-max period. The default spin-max duration is 10 usec.
+/// The spin-max option is applicable only if MayBlock is true.
///
/// Functions:
/// bool ready():
/// std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
/// ASSERT_FALSE(f.try_wait_until(
/// std::chrono::steady_clock::now() + std::chrono::microseconds(1),
-/// f.wait_options().pre_block(std::chrono::microseconds(1))));
+/// f.wait_options().spin_max(std::chrono::microseconds(1))));
/// f.post();
/// f.post();
/// f.wait();
-/// f.wait(f.wait_options().pre_block(std::chrono::nanoseconds(100)));
+/// f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100)));
/// ASSERT_TRUE(f.try_wait());
/// ASSERT_TRUE(f.try_wait_until(
/// std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
};
public:
- /** WaitOptions */
-
- class WaitOptions {
- std::chrono::nanoseconds dur_{std::chrono::microseconds(10)};
-
- public:
- FOLLY_ALWAYS_INLINE
- std::chrono::nanoseconds pre_block() const {
- return dur_;
- }
- FOLLY_ALWAYS_INLINE
- WaitOptions& pre_block(std::chrono::nanoseconds dur) {
- dur_ = dur;
- return *this;
- }
- };
-
FOLLY_ALWAYS_INLINE static WaitOptions wait_options() {
return {};
}
if (MayBlock) {
auto tnow = Clock::now();
if (tnow < tbegin) {
- // backward time discontinuity in Clock, revise pre_block starting point
+ // backward time discontinuity in Clock, revise spin_max starting point
tbegin = tnow;
}
auto dur = std::chrono::duration_cast<Duration>(tnow - tbegin);
- if (dur >= opt.pre_block()) {
+ if (dur >= opt.spin_max()) {
if (before == NOTREADY) {
if (!state_.compare_exchange_strong(
before,
--- /dev/null
+/*
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/synchronization/WaitOptions.h>
+
+namespace folly {
+
+constexpr std::chrono::nanoseconds WaitOptions::Defaults::spin_max;
+
+} // namespace folly
--- /dev/null
+/*
+ * Copyright 2018-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <chrono>
+
+#include <folly/CPortability.h>
+
+namespace folly {
+
+/// WaitOptions
+///
+/// Various synchronization primitives as well as various concurrent data
+/// structures built using them have operations which might wait. This type
+/// represents a set of options for controlling such waiting.
+class WaitOptions {
+ public:
+ struct Defaults {
+ /// spin_max
+ ///
+ /// If multiple threads are actively using a synchronization primitive,
+ /// whether indirectly via a higher-level concurrent data structure or
+ /// directly, where the synchronization primitive has an operation which
+ /// waits and another operation which wakes the waiter, it is common for
+ /// wait and wake events to happen almost at the same time. In this state,
+ /// we lose big 50% of the time if the wait blocks immediately.
+ ///
+ /// We can improve our chances of being waked immediately, before blocking,
+ /// by spinning for a short duration, although we have to balance this
+ /// against the extra cpu utilization, latency reduction, power consumption,
+ /// and priority inversion effect if we end up blocking anyway.
+ ///
+ /// We use a default maximum of 10 usec of spinning. As partial consolation,
+ /// since spinning as implemented in folly uses the pause instruction where
+ /// available, we give a small speed boost to the colocated hyperthread.
+ ///
+ /// On circa-2013 devbox hardware, it costs about 7 usec to FUTEX_WAIT and
+ /// then be awoken. Spins on this hw take about 7 nsec, where all but 0.5
+ /// nsec is the pause instruction.
+ static constexpr std::chrono::nanoseconds spin_max =
+ std::chrono::microseconds(10);
+ };
+
+ std::chrono::nanoseconds spin_max() const {
+ return spin_max_;
+ }
+ WaitOptions& spin_max(std::chrono::nanoseconds dur) {
+ spin_max_ = dur;
+ return *this;
+ }
+
+ private:
+ std::chrono::nanoseconds spin_max_ = Defaults::spin_max;
+};
+
+} // namespace folly
/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2017-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
ASSERT_FALSE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1),
- f.wait_options().pre_block(std::chrono::microseconds(1))));
+ f.wait_options().spin_max(std::chrono::microseconds(1))));
f.post();
f.post();
f.wait();
- f.wait(f.wait_options().pre_block(std::chrono::nanoseconds(100)));
+ f.wait(f.wait_options().spin_max(std::chrono::nanoseconds(100)));
ASSERT_TRUE(f.ready());
ASSERT_TRUE(f.try_wait());
ASSERT_TRUE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
ASSERT_FALSE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1),
- f.wait_options().pre_block(std::chrono::microseconds(0))));
+ f.wait_options().spin_max(std::chrono::microseconds(0))));
waited.fetch_add(1);
while (!go_wait.load()) {
/* spin */;
std::chrono::steady_clock::now() + std::chrono::microseconds(1)));
ASSERT_TRUE(f.try_wait_until(
std::chrono::steady_clock::now() + std::chrono::microseconds(1),
- f.wait_options().pre_block(std::chrono::microseconds(0))));
+ f.wait_options().spin_max(std::chrono::microseconds(0))));
f.wait();
});
}