-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull out the sleeping logic from chronocontroller and test it works
- Loading branch information
1 parent
5827c5e
commit f8710b6
Showing
7 changed files
with
320 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/* | ||
* MIT License | ||
* | ||
* Copyright (c) 2023 NUClear Contributors | ||
* | ||
* This file is part of the NUClear codebase. | ||
* See https://github.com/Fastcode/NUClear for further info. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the | ||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the | ||
* Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
#include "Sleeper.hpp" | ||
|
||
#include <chrono> | ||
|
||
#if defined(_WIN32) | ||
|
||
#include <chrono> | ||
#include <cstdint> | ||
|
||
#include "platform.hpp" | ||
|
||
namespace NUClear { | ||
namespace util { | ||
|
||
// Windows requires a waitable timer to sleep for a precise amount of time | ||
class SleeperImpl { | ||
public: | ||
SleeperImpl() : timer(::CreateWaitableTimer(nullptr, TRUE, nullptr)) {} | ||
::HANDLE timer; | ||
}; | ||
|
||
Sleeper::~Sleeper() { | ||
::CloseHandle(sleeper->timer); | ||
} | ||
|
||
void Sleeper::idle_sleep(const std::chrono::nanoseconds& ns) { | ||
::LARGE_INTEGER ft; | ||
// TODO if ns is negative make it 0 as otherwise it'll become absolute time | ||
// Negative for relative time, positive for absolute time | ||
// Measures in 100ns increments so divide by 100 | ||
ft.QuadPart = -static_cast<int64_t>(ns.count() / 100); | ||
|
||
::SetWaitableTimer(impl->timer, &ft, 0, nullptr, nullptr, 0); | ||
::WaitForSingleObject(impl->timer, INFINITE); | ||
} | ||
|
||
} // namespace util | ||
} // namespace NUClear | ||
|
||
#else | ||
|
||
#include <cerrno> | ||
#include <cstdint> | ||
#include <ctime> | ||
|
||
namespace NUClear { | ||
namespace util { | ||
|
||
// No specific implementation for precise sleep on linux | ||
class SleeperImpl {}; | ||
|
||
// Sleep using nanosleep on linux | ||
void Sleeper::idle_sleep(const std::chrono::nanoseconds& ns) { | ||
if (ns <= std::chrono::nanoseconds(0)) { | ||
return; | ||
} | ||
timespec ts{}; | ||
ts.tv_sec = std::chrono::duration_cast<std::chrono::seconds>(ns).count(); | ||
ts.tv_nsec = (ns - std::chrono::seconds(ts.tv_sec)).count(); | ||
|
||
while (::nanosleep(&ts, &ts) == -1 && errno == EINTR) { | ||
} | ||
} | ||
|
||
} // namespace util | ||
} // namespace NUClear | ||
|
||
#endif | ||
|
||
namespace NUClear { | ||
namespace util { | ||
|
||
Sleeper::Sleeper() : sleeper(std::make_unique<SleeperImpl>()) {} | ||
|
||
// This must be in the .cpp file as we need the full definition of SleeperImpl | ||
Sleeper::~Sleeper() = default; | ||
Sleeper::Sleeper(Sleeper&&) = default; | ||
Sleeper& Sleeper::operator=(Sleeper&&) = default; | ||
|
||
void NUClear::util::Sleeper::sleep_for(const std::chrono::nanoseconds& duration) { | ||
sleep_until(std::chrono::steady_clock::now() + duration); | ||
} | ||
|
||
void NUClear::util::Sleeper::sleep_until(const std::chrono::steady_clock::time_point& target) { | ||
using namespace std::chrono; | ||
|
||
for (auto start = std::chrono::steady_clock::now(); start < target; start = std::chrono::steady_clock::now()) { | ||
// If we can accurately sleep for the target amount of time then do so | ||
if (target - start >= sleep_accuracy) { | ||
// Sleep as accurately as we think we can | ||
auto target_sleep_time = target - start - sleep_accuracy; | ||
idle_sleep(target_sleep_time); | ||
auto end = std::chrono::steady_clock::now(); | ||
|
||
// Update the idle sleep accuracy estimate using Welford's method | ||
auto actual_sleep_time = end - start; | ||
|
||
double sleep_error = | ||
duration_cast<duration<double, std::nano>>(actual_sleep_time - target_sleep_time).count(); | ||
double delta = sleep_error - mean; | ||
|
||
count = count + 1; | ||
mean = mean + (delta / count); | ||
double delta2 = sleep_error - mean; | ||
m2 = m2 + delta * delta2; | ||
|
||
// Sleep accuracy with 3 standard deviations of the mean for a 99.7% confidence interval | ||
sleep_accuracy = nanoseconds(std::lround(std::sqrt(m2 / count) * 3.0)); | ||
} | ||
} | ||
} | ||
|
||
} // namespace util | ||
} // namespace NUClear |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* | ||
* MIT License | ||
* | ||
* Copyright (c) 2014 NUClear Contributors | ||
* | ||
* This file is part of the NUClear codebase. | ||
* See https://github.com/Fastcode/NUClear for further info. | ||
* | ||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated | ||
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the | ||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to | ||
* permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
* | ||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the | ||
* Software. | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | ||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
|
||
#ifndef NUCLEAR_UTIL_SLEEPER_HPP | ||
#define NUCLEAR_UTIL_SLEEPER_HPP | ||
|
||
#include <chrono> | ||
|
||
namespace NUClear { | ||
namespace util { | ||
|
||
class SleeperImpl; | ||
|
||
/** | ||
* A class that provides platform independent precise sleep functionality. | ||
* | ||
* The Sleeper class allows for sleeping for a specified duration of time. | ||
* It will use the most accurate method available on the platform to sleep for the specified duration. | ||
* It will then spin the CPU for the remaining time to ensure that the sleep is as accurate as possible. | ||
*/ | ||
class Sleeper { | ||
public: | ||
Sleeper(); | ||
~Sleeper(); | ||
Sleeper(Sleeper&&); | ||
Sleeper& operator=(Sleeper&&); | ||
|
||
// No copying due to the unique_ptr | ||
Sleeper(const Sleeper&) = delete; | ||
Sleeper& operator=(const Sleeper&) = delete; | ||
|
||
/** | ||
* Sleep for the specified duration. | ||
* | ||
* @param duration The duration to sleep for. | ||
*/ | ||
void sleep_for(const std::chrono::nanoseconds& duration); | ||
|
||
/** | ||
* Sleep until the specified time point. | ||
* | ||
* @param target The time point to sleep until. | ||
*/ | ||
void sleep_until(const std::chrono::steady_clock::time_point& target); | ||
|
||
private: | ||
/** | ||
* Sleeps by putting the thread to sleep for the specified duration. | ||
* | ||
* @param ns The duration to sleep for. | ||
*/ | ||
void idle_sleep(const std::chrono::nanoseconds& ns); | ||
|
||
/// The platform specific implementation of the Sleeper. | ||
std::unique_ptr<SleeperImpl> sleeper; | ||
|
||
/// Welfords method for calculating the mean and variance of the sleep function. | ||
int count = 0; | ||
double mean = 0; | ||
double m2 = 0; | ||
|
||
/// The estimated accuracy of the sleep function. | ||
std::chrono::nanoseconds sleep_accuracy{0}; | ||
}; | ||
|
||
} // namespace util | ||
} // namespace NUClear | ||
|
||
#endif // NUCLEAR_UTIL_SLEEPER_HPP |
Oops, something went wrong.