diff --git a/wpiutil/src/main/native/include/support/priority_condition_variable.h b/wpiutil/src/main/native/include/support/priority_condition_variable.h deleted file mode 100644 index 017f7456e4..0000000000 --- a/wpiutil/src/main/native/include/support/priority_condition_variable.h +++ /dev/null @@ -1,19 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -#pragma once - -// clang-format off -#ifdef _MSC_VER -#pragma message "warning: support/priority_condition_variable.h is deprecated; include wpi/priority_condition_variable.h instead" -#else -#warning "support/priority_condition_variable.h is deprecated; include wpi/priority_condition_variable.h instead" -#endif - -// clang-format on - -#include "wpi/priority_condition_variable.h" diff --git a/wpiutil/src/main/native/include/wpi/condition_variable.h b/wpiutil/src/main/native/include/wpi/condition_variable.h index 23ce90dd6e..1599e192e7 100644 --- a/wpiutil/src/main/native/include/wpi/condition_variable.h +++ b/wpiutil/src/main/native/include/wpi/condition_variable.h @@ -9,12 +9,12 @@ #include -#include "priority_condition_variable.h" +#include "wpi/priority_mutex.h" namespace wpi { -#ifdef WPI_HAVE_PRIORITY_CONDITION_VARIABLE -using condition_variable = priority_condition_variable; +#if defined(__linux__) && defined(WPI_HAVE_PRIORITY_MUTEX) +using condition_variable = ::std::condition_variable_any; #else using condition_variable = ::std::condition_variable; #endif diff --git a/wpiutil/src/main/native/include/wpi/priority_condition_variable.h b/wpiutil/src/main/native/include/wpi/priority_condition_variable.h deleted file mode 100644 index 01f044ee01..0000000000 --- a/wpiutil/src/main/native/include/wpi/priority_condition_variable.h +++ /dev/null @@ -1,122 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -#pragma once - -#ifdef __linux__ -#include -#endif - -#include -#include -#include - -#include "priority_mutex.h" - -namespace wpi { - -#if defined(__linux__) && defined(WPI_HAVE_PRIORITY_MUTEX) - -#define WPI_HAVE_PRIORITY_CONDITION_VARIABLE 1 - -class priority_condition_variable { - typedef std::chrono::system_clock clock; - - public: - typedef pthread_cond_t* native_handle_type; - - priority_condition_variable() noexcept = default; - ~priority_condition_variable() noexcept { pthread_cond_destroy(&m_cond); } - - priority_condition_variable(const priority_condition_variable&) = delete; - priority_condition_variable& operator=(const priority_condition_variable&) = - delete; - - void notify_one() noexcept { pthread_cond_signal(&m_cond); } - - void notify_all() noexcept { pthread_cond_broadcast(&m_cond); } - - void wait(std::unique_lock& lock) noexcept { - int e = pthread_cond_wait(&m_cond, lock.mutex()->native_handle()); - if (e) std::terminate(); - } - - template - void wait(std::unique_lock& lock, Predicate p) { - while (!p()) { - wait(lock); - } - } - - template - std::cv_status wait_until( - std::unique_lock& lock, - const std::chrono::time_point& atime) { - return wait_until_impl(lock, atime); - } - - template - std::cv_status wait_until( - std::unique_lock& lock, - const std::chrono::time_point& atime) { - const typename Clock::time_point c_entry = Clock::now(); - const clock::time_point s_entry = clock::now(); - const auto delta = atime - c_entry; - const auto s_atime = s_entry + delta; - - return wait_until_impl(lock, s_atime); - } - - template - bool wait_until(std::unique_lock& lock, - const std::chrono::time_point& atime, - Predicate p) { - while (!p()) { - if (wait_until(lock, atime) == std::cv_status::timeout) { - return p(); - } - } - return true; - } - - template - std::cv_status wait_for(std::unique_lock& lock, - const std::chrono::duration& rtime) { - return wait_until(lock, clock::now() + rtime); - } - - template - bool wait_for(std::unique_lock& lock, - const std::chrono::duration& rtime, Predicate p) { - return wait_until(lock, clock::now() + rtime, std::move(p)); - } - - native_handle_type native_handle() { return &m_cond; } - - private: - pthread_cond_t m_cond = PTHREAD_COND_INITIALIZER; - - template - std::cv_status wait_until_impl( - std::unique_lock& lock, - const std::chrono::time_point& atime) { - auto s = std::chrono::time_point_cast(atime); - auto ns = std::chrono::duration_cast(atime - s); - - struct timespec ts = { - static_cast(s.time_since_epoch().count()), - static_cast(ns.count())}; // NOLINT(runtime/int) - - pthread_cond_timedwait(&m_cond, lock.mutex()->native_handle(), &ts); - - return (clock::now() < atime ? std::cv_status::no_timeout - : std::cv_status::timeout); - } -}; -#endif - -} // namespace wpi diff --git a/wpiutil/src/test/native/cpp/priority_condition_variable_test.cpp b/wpiutil/src/test/native/cpp/priority_condition_variable_test.cpp deleted file mode 100644 index dc10bdb11c..0000000000 --- a/wpiutil/src/test/native/cpp/priority_condition_variable_test.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -#include -#include - -#include -#include -#include -#include -#include - -#include "gtest/gtest.h" - -namespace wpi { - -#ifdef WPI_HAVE_PRIORITY_CONDITION_VARIABLE - -// Tests that the condition variable class which we wrote ourselves actually -// does work. -class ConditionVariableTest : public ::testing::Test { - protected: - typedef std::unique_lock priority_lock; - - // Condition variable to test. - priority_condition_variable m_cond; - - // Mutex to pass to condition variable when waiting. - priority_mutex m_mutex; - - // flags for testing when threads are completed. - std::atomic m_done1{false}, m_done2{false}; - // Threads to use for testing. We want multiple threads to ensure that it - // behaves correctly when multiple processes are waiting on a signal. - std::thread m_watcher1, m_watcher2; - - // Information for when running with predicates. - std::atomic m_pred_var{false}; - - void ShortSleep(uint32_t time = 10) { - std::this_thread::sleep_for(std::chrono::milliseconds(time)); - } - - // Start up the given threads with a wait function. The wait function should - // call some version of m_cond.wait and should take as an argument a reference - // to an std::atomic which it will set to true when it is ready to have - // join called on it. - template - void StartThreads(Function wait) { - m_watcher1 = std::thread(wait, std::ref(m_done1)); - m_watcher2 = std::thread(wait, std::ref(m_done2)); - - // Wait briefly to let the lock be unlocked. - ShortSleep(); - bool locked = m_mutex.try_lock(); - if (locked) m_mutex.unlock(); - EXPECT_TRUE(locked) << "The condition variable failed to unlock the lock."; - } - - void NotifyAll() { m_cond.notify_all(); } - void NotifyOne() { m_cond.notify_one(); } - - // Test that all the threads are notified by a notify_all() call. - void NotifyAllTest() { - NotifyAll(); - // Wait briefly to let the lock be re-locked. - ShortSleep(); - EXPECT_TRUE(m_done1) << "watcher1 failed to be notified."; - EXPECT_TRUE(m_done2) << "watcher2 failed to be notified."; - } - - // For use when testing predicates. First tries signalling the threads with - // the predicate set to false (and ensures that they do not activate) and then - // tests with the predicate set to true. - void PredicateTest() { - m_pred_var = false; - NotifyAll(); - ShortSleep(); - EXPECT_FALSE(m_done1) << "watcher1 didn't pay attention to its predicate."; - EXPECT_FALSE(m_done2) << "watcher2 didn't pay attention to its predicate."; - m_pred_var = true; - NotifyAllTest(); - } - - // Used by the WaitFor and WaitUntil tests to test that, without a predicate, - // the timeout works properly. - void WaitTimeTest(bool wait_for) { - std::atomic timed_out{true}; - auto wait_until = [this, &timed_out, wait_for](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - if (wait_for) { - auto wait_time = std::chrono::milliseconds(100); - timed_out = m_cond.wait_for(lock, wait_time) == std::cv_status::timeout; - } else { - auto wait_time = - std::chrono::system_clock::now() + std::chrono::milliseconds(100); - timed_out = - m_cond.wait_until(lock, wait_time) == std::cv_status::timeout; - } - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - // First, test without timing out. - timed_out = true; - StartThreads(wait_until); - - NotifyAllTest(); - EXPECT_FALSE(timed_out) << "The watcher should not have timed out."; - - TearDown(); - - // Next, test and time out. - timed_out = false; - StartThreads(wait_until); - - ShortSleep(110); - - EXPECT_TRUE(m_done1) << "watcher1 should have timed out."; - EXPECT_TRUE(m_done2) << "watcher2 should have timed out."; - EXPECT_TRUE(timed_out) << "The watcher should have timed out."; - } - - // For use with tests that have a timeout and a predicate. - void WaitTimePredicateTest(bool wait_for) { - // The condition_variable return value from the wait_for or wait_until - // function should in the case of having a predicate, by a boolean. If the - // predicate is true, then the return value will always be true. If the - // condition times out and, at the time of the timeout, the predicate is - // false, the return value will be false. - std::atomic retval{true}; - auto predicate = [this]() -> bool { return m_pred_var; }; - auto wait_until = [this, &retval, predicate, - wait_for](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - if (wait_for) { - auto wait_time = std::chrono::milliseconds(100); - retval = m_cond.wait_for(lock, wait_time, predicate); - } else { - auto wait_time = - std::chrono::system_clock::now() + std::chrono::milliseconds(100); - retval = m_cond.wait_until(lock, wait_time, predicate); - } - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - // Test without timing out and with the predicate set to true. - retval = true; - m_pred_var = true; - StartThreads(wait_until); - - NotifyAllTest(); - EXPECT_TRUE(retval) << "The watcher should not have timed out."; - - TearDown(); - - // Test with timing out and with the predicate set to true. - retval = false; - m_pred_var = false; - StartThreads(wait_until); - - ShortSleep(110); - - EXPECT_TRUE(m_done1) << "watcher1 should have finished."; - EXPECT_TRUE(m_done2) << "watcher2 should have finished."; - EXPECT_FALSE(retval) << "The watcher should have timed out."; - - TearDown(); - - // Test without timing out and run the PredicateTest(). - retval = false; - StartThreads(wait_until); - - PredicateTest(); - EXPECT_TRUE(retval) << "The return value should have been true."; - - TearDown(); - - // Test with timing out and the predicate set to true while we are waiting - // for the condition variable to time out. - retval = true; - StartThreads(wait_until); - ShortSleep(); - m_pred_var = true; - ShortSleep(110); - EXPECT_TRUE(retval) << "The return value should have been true."; - } - - virtual void TearDown() { - // If a thread has not completed, then continuing will cause the tests to - // hang forever and could cause issues. If we don't call detach, then - // std::terminate is called and all threads are terminated. - // Detaching is non-optimal, but should allow the rest of the tests to run - // before anything drastic occurs. - if (m_done1) - m_watcher1.join(); - else - m_watcher1.detach(); - if (m_done2) - m_watcher2.join(); - else - m_watcher2.detach(); - } -}; - -TEST_F(ConditionVariableTest, NotifyAll) { - auto wait = [this](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - m_cond.wait(lock); - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - StartThreads(wait); - - NotifyAllTest(); -} - -TEST_F(ConditionVariableTest, NotifyOne) { - auto wait = [this](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - m_cond.wait(lock); - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - StartThreads(wait); - - NotifyOne(); - // Wait briefly to let things settle. - ShortSleep(); - EXPECT_TRUE(m_done1 ^ m_done2) << "Only one thread should've been notified."; - NotifyOne(); - ShortSleep(); - EXPECT_TRUE(m_done2 && m_done2) << "Both threads should've been notified."; -} - -TEST_F(ConditionVariableTest, WaitWithPredicate) { - auto predicate = [this]() -> bool { return m_pred_var; }; - auto wait_predicate = [this, predicate](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - m_cond.wait(lock, predicate); - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - StartThreads(wait_predicate); - - PredicateTest(); -} - -TEST_F(ConditionVariableTest, WaitUntil) { WaitTimeTest(false); } - -TEST_F(ConditionVariableTest, WaitUntilWithPredicate) { - WaitTimePredicateTest(false); -} - -TEST_F(ConditionVariableTest, WaitFor) { WaitTimeTest(true); } - -TEST_F(ConditionVariableTest, WaitForWithPredicate) { - WaitTimePredicateTest(true); -} - -TEST_F(ConditionVariableTest, NativeHandle) { - auto wait = [this](std::atomic& done) { - priority_lock lock(m_mutex); - done = false; - m_cond.wait(lock); - EXPECT_TRUE(lock.owns_lock()) - << "The condition variable should have reacquired the lock."; - done = true; - }; - - StartThreads(wait); - - pthread_cond_t* native_handle = m_cond.native_handle(); - pthread_cond_broadcast(native_handle); - ShortSleep(); - EXPECT_TRUE(m_done1) << "watcher1 failed to be notified."; - EXPECT_TRUE(m_done2) << "watcher2 failed to be notified."; -} - -#endif // WPI_HAVE_PRIORITY_CONDITION_VARIABLE - -} // namespace wpi