-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[llvm][Support] Add ExponentialBackoff helper #81206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
//===- llvm/Support/ExponentialBackoff.h ------------------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This file defines a helper class for implementing exponential backoff. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
#ifndef LLVM_EXPONENTIALBACKOFF_H | ||
#define LLVM_EXPONENTIALBACKOFF_H | ||
|
||
#include "llvm/ADT/STLExtras.h" | ||
#include "llvm/Support/Error.h" | ||
#include <chrono> | ||
#include <random> | ||
|
||
namespace llvm { | ||
|
||
/// A class to help implement exponential backoff. | ||
/// | ||
/// Example usage: | ||
/// \code | ||
/// ExponentialBackoff Backoff(10s); | ||
/// do { | ||
/// if (tryToDoSomething()) | ||
/// return ItWorked; | ||
/// } while (Backoff.waitForNextAttempt()); | ||
/// return Timeout; | ||
/// \endcode | ||
class ExponentialBackoff { | ||
public: | ||
using duration = std::chrono::steady_clock::duration; | ||
using time_point = std::chrono::steady_clock::time_point; | ||
|
||
/// \param Timeout the maximum wall time this should run for starting when | ||
/// this object is constructed. | ||
/// \param MinWait the minimum amount of time `waitForNextAttempt` will sleep | ||
/// for. | ||
/// \param MaxWait the maximum amount of time `waitForNextAttempt` will sleep | ||
/// for. | ||
ExponentialBackoff(duration Timeout, | ||
duration MinWait = std::chrono::milliseconds(10), | ||
duration MaxWait = std::chrono::milliseconds(500)) | ||
: MinWait(MinWait), MaxWait(MaxWait), | ||
EndTime(std::chrono::steady_clock::now() + Timeout) {} | ||
|
||
/// Blocks while waiting for the next attempt. | ||
/// \returns true if you should try again, false if the timeout has been | ||
/// reached. | ||
bool waitForNextAttempt(); | ||
|
||
private: | ||
duration MinWait; | ||
duration MaxWait; | ||
time_point EndTime; | ||
std::random_device RandDev; | ||
int64_t CurrentMultiplier = 1; | ||
}; | ||
|
||
} // end namespace llvm | ||
|
||
#endif // LLVM_EXPONENTIALBACKOFF_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
//===- llvm/Support/ExponentialBackoff.h ------------------------*- C++ -*-===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "llvm/Support/ExponentialBackoff.h" | ||
#include <thread> | ||
|
||
using namespace llvm; | ||
|
||
bool ExponentialBackoff::waitForNextAttempt() { | ||
auto Now = std::chrono::steady_clock::now(); | ||
if (Now >= EndTime) | ||
return false; | ||
|
||
duration CurMaxWait = std::min(MinWait * CurrentMultiplier, MaxWait); | ||
std::uniform_int_distribution<uint64_t> Dist(MinWait.count(), | ||
CurMaxWait.count()); | ||
// Use random_device directly instead of a PRNG as uniform_int_distribution | ||
// often only takes a few samples anyway. | ||
duration WaitDuration = std::min(duration(Dist(RandDev)), EndTime - Now); | ||
if (CurMaxWait < MaxWait) | ||
CurrentMultiplier *= 2; | ||
std::this_thread::sleep_for(WaitDuration); | ||
return true; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
//===- unittests/ExponentialBackoffTest.cpp -------------------------------===// | ||
// | ||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
// See https://llvm.org/LICENSE.txt for license information. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#include "llvm/Support/ExponentialBackoff.h" | ||
#include "gtest/gtest.h" | ||
#include <chrono> | ||
|
||
using namespace llvm; | ||
using namespace std::chrono_literals; | ||
|
||
namespace { | ||
|
||
TEST(ExponentialBackoffTest, Timeout) { | ||
auto Start = std::chrono::steady_clock::now(); | ||
// Use short enough times that this test runs quickly. | ||
ExponentialBackoff Backoff(100ms, 1ms, 10ms); | ||
do { | ||
} while (Backoff.waitForNextAttempt()); | ||
Comment on lines
+22
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could check the bounds of the waits in the loop? (like check that it's at least 1ms, less than 15 or something?) Though, yeah, any testing here will be either slow or brittle or both - so don't do it if it's just impractical to make it reliable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was concerned about edge cases with virtual machines or other weirdness. I locally checked and it seemed reasonable. Thanks. |
||
auto Duration = std::chrono::steady_clock::now() - Start; | ||
EXPECT_GE(Duration, 100ms); | ||
} | ||
|
||
// Testing individual wait duration is omitted as those tests would be | ||
// non-deterministic. | ||
|
||
} // end anonymous namespace |
Uh oh!
There was an error while loading. Please reload this page.