unilink  0.4.3
A simple C++ library for unified async communication
reconnect_policy.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2025 Jinwoo Sung
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #pragma once
18 
19 #include <algorithm>
20 #include <chrono>
21 #include <cmath>
22 #include <functional>
23 #include <memory>
24 #include <mutex>
25 #include <random>
26 #include <thread>
27 
29 
30 namespace unilink {
31 
36  bool retry{false};
37  std::chrono::milliseconds delay{0};
38 };
39 
46 using ReconnectPolicy = std::function<ReconnectDecision(const diagnostics::ErrorInfo&, uint32_t)>;
47 
54 inline ReconnectPolicy FixedInterval(std::chrono::milliseconds delay) {
55  return [delay](const diagnostics::ErrorInfo& error_info, uint32_t) -> ReconnectDecision {
56  if (!error_info.retryable) {
57  return {false, std::chrono::milliseconds(0)};
58  }
59  return {true, delay};
60  };
61 }
62 
72 inline ReconnectPolicy ExponentialBackoff(std::chrono::milliseconds min_delay, std::chrono::milliseconds max_delay,
73  double factor = 2.0, bool jitter = true) {
74  struct ProtectedRng {
75  std::mt19937 rng;
76  std::mutex mtx;
77 
78  ProtectedRng() {
79  auto seed = static_cast<unsigned int>(std::chrono::high_resolution_clock::now().time_since_epoch().count());
80  rng.seed(seed);
81  }
82  };
83 
84  std::shared_ptr<ProtectedRng> shared_rng;
85  if (jitter) {
86  shared_rng = std::make_shared<ProtectedRng>();
87  }
88 
89  return [min_delay, max_delay, factor, shared_rng](const diagnostics::ErrorInfo& error_info,
90  uint32_t attempt_count) -> ReconnectDecision {
91  if (!error_info.retryable) {
92  return {false, std::chrono::milliseconds(0)};
93  }
94 
95  double calculated = static_cast<double>(min_delay.count()) * std::pow(factor, attempt_count);
96  double cap = static_cast<double>(max_delay.count());
97 
98  // Clamp to max_delay
99  double delay_ms = std::min(calculated, cap);
100 
101  if (shared_rng) {
102  std::lock_guard<std::mutex> lock(shared_rng->mtx);
103  // Full Jitter: random between 0 and calculated delay
104  std::uniform_real_distribution<> dist(0.0, delay_ms);
105  delay_ms = dist(shared_rng->rng);
106  }
107 
108  return {true, std::chrono::milliseconds(static_cast<long long>(delay_ms))};
109  };
110 }
111 
112 } // namespace unilink