OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cmath> | |
9 #include <limits> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/memory/ptr_util.h" | |
13 #include "base/numerics/safe_conversions.h" | |
14 #include "base/rand_util.h" | |
15 #include "base/strings/stringprintf.h" | |
16 #include "components/proximity_auth/logging/logging.h" | |
17 | |
18 namespace proximity_auth { | |
19 | |
20 namespace { | |
21 | |
22 // Returns a human readable string given a |time_delta|. | |
23 std::string TimeDeltaToString(const base::TimeDelta& time_delta) { | |
24 if (time_delta.InDays() > 0) | |
25 return base::StringPrintf("%d days", time_delta.InDays()); | |
26 | |
27 if (time_delta.InHours() > 0) | |
28 return base::StringPrintf("%d hours", time_delta.InHours()); | |
29 | |
30 if (time_delta.InMinutes() > 0) | |
31 return base::StringPrintf("%d minutes", time_delta.InMinutes()); | |
32 | |
33 return base::StringPrintf("%d seconds", | |
34 base::saturated_cast<int>(time_delta.InSeconds())); | |
35 } | |
36 | |
37 } // namespace | |
38 | |
39 SyncSchedulerImpl::SyncSchedulerImpl(Delegate* delegate, | |
40 base::TimeDelta refresh_period, | |
41 base::TimeDelta base_recovery_period, | |
42 double max_jitter_ratio, | |
43 const std::string& scheduler_name) | |
44 : delegate_(delegate), | |
45 refresh_period_(refresh_period), | |
46 base_recovery_period_(base_recovery_period), | |
47 max_jitter_ratio_(max_jitter_ratio), | |
48 scheduler_name_(scheduler_name), | |
49 strategy_(Strategy::PERIODIC_REFRESH), | |
50 sync_state_(SyncState::NOT_STARTED), | |
51 failure_count_(0), | |
52 weak_ptr_factory_(this) { | |
53 } | |
54 | |
55 SyncSchedulerImpl::~SyncSchedulerImpl() { | |
56 } | |
57 | |
58 void SyncSchedulerImpl::Start( | |
59 const base::TimeDelta& elapsed_time_since_last_sync, | |
60 Strategy strategy) { | |
61 strategy_ = strategy; | |
62 sync_state_ = SyncState::WAITING_FOR_REFRESH; | |
63 // We reset the failure backoff when the scheduler is started again, as the | |
64 // configuration that caused the previous attempts to fail most likely won't | |
65 // be present after a restart. | |
66 if (strategy_ == Strategy::AGGRESSIVE_RECOVERY) | |
67 failure_count_ = 1; | |
68 | |
69 // To take into account the time waited when the system is powered off, we | |
70 // subtract the time elapsed with a normal sync period to the initial time | |
71 // to wait. | |
72 base::TimeDelta sync_delta = | |
73 GetJitteredPeriod() - elapsed_time_since_last_sync; | |
74 | |
75 // The elapsed time may be negative if the system clock is changed. In this | |
76 // case, we immediately schedule a sync. | |
77 base::TimeDelta zero_delta = base::TimeDelta::FromSeconds(0); | |
78 if (elapsed_time_since_last_sync < zero_delta || sync_delta < zero_delta) | |
79 sync_delta = zero_delta; | |
80 | |
81 ScheduleNextSync(sync_delta); | |
82 } | |
83 | |
84 void SyncSchedulerImpl::ForceSync() { | |
85 OnTimerFired(); | |
86 } | |
87 | |
88 base::TimeDelta SyncSchedulerImpl::GetTimeToNextSync() const { | |
89 if (!timer_) | |
90 return base::TimeDelta::FromSeconds(0); | |
91 return timer_->GetCurrentDelay(); | |
92 } | |
93 | |
94 SyncScheduler::Strategy SyncSchedulerImpl::GetStrategy() const { | |
95 return strategy_; | |
96 } | |
97 | |
98 SyncScheduler::SyncState SyncSchedulerImpl::GetSyncState() const { | |
99 return sync_state_; | |
100 } | |
101 | |
102 void SyncSchedulerImpl::OnTimerFired() { | |
103 timer_.reset(); | |
104 if (strategy_ == Strategy::PERIODIC_REFRESH) { | |
105 PA_LOG(INFO) << "Timer fired for periodic refresh, making request..."; | |
106 sync_state_ = SyncState::SYNC_IN_PROGRESS; | |
107 } else if (strategy_ == Strategy::AGGRESSIVE_RECOVERY) { | |
108 PA_LOG(INFO) << "Timer fired for aggressive recovery, making request..."; | |
109 sync_state_ = SyncState::SYNC_IN_PROGRESS; | |
110 } else { | |
111 NOTREACHED(); | |
112 return; | |
113 } | |
114 | |
115 delegate_->OnSyncRequested( | |
116 base::MakeUnique<SyncRequest>(weak_ptr_factory_.GetWeakPtr())); | |
117 } | |
118 | |
119 std::unique_ptr<base::Timer> SyncSchedulerImpl::CreateTimer() { | |
120 bool retain_user_task = false; | |
121 bool is_repeating = false; | |
122 return base::MakeUnique<base::Timer>(retain_user_task, is_repeating); | |
123 } | |
124 | |
125 void SyncSchedulerImpl::ScheduleNextSync(const base::TimeDelta& sync_delta) { | |
126 if (sync_state_ != SyncState::WAITING_FOR_REFRESH) { | |
127 PA_LOG(ERROR) << "Unexpected state when scheduling next sync: sync_state=" | |
128 << static_cast<int>(sync_state_); | |
129 return; | |
130 } | |
131 | |
132 bool is_aggressive_recovery = (strategy_ == Strategy::AGGRESSIVE_RECOVERY); | |
133 PA_LOG(INFO) << "Scheduling next sync for " << scheduler_name_ << ":\n" | |
134 << " Strategy: " << (is_aggressive_recovery | |
135 ? "Aggressive Recovery" | |
136 : "Periodic Refresh") << "\n" | |
137 << " Time Delta: " << TimeDeltaToString(sync_delta) | |
138 << (is_aggressive_recovery | |
139 ? base::StringPrintf( | |
140 "\n Previous Failures: %d", | |
141 base::saturated_cast<int>(failure_count_)) | |
142 : ""); | |
143 | |
144 timer_ = CreateTimer(); | |
145 timer_->Start(FROM_HERE, sync_delta, | |
146 base::Bind(&SyncSchedulerImpl::OnTimerFired, | |
147 weak_ptr_factory_.GetWeakPtr())); | |
148 } | |
149 | |
150 void SyncSchedulerImpl::OnSyncCompleted(bool success) { | |
151 if (sync_state_ != SyncState::SYNC_IN_PROGRESS) { | |
152 PA_LOG(ERROR) << "Unexpected state when sync completed: sync_state=" | |
153 << static_cast<int>(sync_state_) | |
154 << ", strategy_=" << static_cast<int>(strategy_); | |
155 return; | |
156 } | |
157 sync_state_ = SyncState::WAITING_FOR_REFRESH; | |
158 | |
159 if (success) { | |
160 strategy_ = Strategy::PERIODIC_REFRESH; | |
161 failure_count_ = 0; | |
162 } else { | |
163 strategy_ = Strategy::AGGRESSIVE_RECOVERY; | |
164 ++failure_count_; | |
165 } | |
166 | |
167 ScheduleNextSync(GetJitteredPeriod()); | |
168 } | |
169 | |
170 base::TimeDelta SyncSchedulerImpl::GetJitteredPeriod() { | |
171 double jitter = 2 * max_jitter_ratio_ * (base::RandDouble() - 0.5); | |
172 base::TimeDelta period = GetPeriod(); | |
173 base::TimeDelta jittered_time_delta = period + (period * jitter); | |
174 if (jittered_time_delta.InMilliseconds() < 0) | |
175 jittered_time_delta = base::TimeDelta::FromMilliseconds(0); | |
176 return jittered_time_delta; | |
177 } | |
178 | |
179 base::TimeDelta SyncSchedulerImpl::GetPeriod() { | |
180 if (strategy_ == Strategy::PERIODIC_REFRESH) { | |
181 return refresh_period_; | |
182 } else if (strategy_ == Strategy::AGGRESSIVE_RECOVERY && failure_count_ > 0) { | |
183 // The backoff for each consecutive failure is exponentially doubled until | |
184 // it is equal to the normal refresh period. | |
185 // Note: |backoff_factor| may evaulate to INF if |failure_count_| is large, | |
186 // but multiplication operations for TimeDelta objects are saturated. | |
187 double backoff_factor = pow(2, failure_count_ - 1); | |
188 base::TimeDelta backoff_period = base_recovery_period_ * backoff_factor; | |
189 return backoff_period < refresh_period_ ? backoff_period : refresh_period_; | |
190 } else { | |
191 PA_LOG(ERROR) << "Error getting period for strategy: " | |
192 << static_cast<int>(strategy_); | |
193 return base::TimeDelta(); | |
194 } | |
195 } | |
196 | |
197 } // namespace proximity_auth | |
OLD | NEW |