1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/credential_provider/extension/task_manager.h"
#include <windows.h>
#include <memory>
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "chrome/credential_provider/extension/extension_utils.h"
#include "chrome/credential_provider/extension/user_context_enumerator.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider {
namespace extension {
namespace {
// Backoff policy for errors returned by performing task operation.
const net::BackoffEntry::Policy kRetryLaterPolicy = {
// Number of initial errors to ignore before starting to back off.
0,
// Initial delay in ms: 1 minute.
1000 * 60,
// Factor by which the waiting time is multiplied.
2,
// Fuzzing percentage; this spreads delays randomly between 80% and 100%
// of the calculated time.
0.20,
// Maximum delay in ms: 3 hours. However this field of back off policy is
// overridden by individual task's execution period when actually backing
// off.
1000 * 60 * 60 * 3,
// When to discard an entry: never.
-1,
// |always_use_initial_delay|; false means that the initial delay is
// applied after the first error, and starts backing off from there.
true,
};
// Returns the elapsed time delta since the last time the periodic sync were
// successfully performed for the given task registry.
base::TimeDelta GetTimeDeltaSinceLastPeriodicSync(
const std::wstring& task_reg_name) {
wchar_t last_sync_millis[512];
ULONG last_sync_size = std::size(last_sync_millis);
HRESULT hr = GetGlobalFlag(task_reg_name, last_sync_millis, &last_sync_size);
if (FAILED(hr)) {
// The periodic sync has never happened before.
return base::TimeDelta::Max();
}
int64_t last_sync_millis_int64;
base::StringToInt64(last_sync_millis, &last_sync_millis_int64);
const auto last_sync = base::Time::FromDeltaSinceWindowsEpoch(
base::Milliseconds(last_sync_millis_int64));
return base::Time::Now() - last_sync;
}
} // namespace
// static
TaskManager** TaskManager::GetInstanceStorage() {
static TaskManager* instance = new TaskManager();
return &instance;
}
// static
TaskManager* TaskManager::Get() {
return *GetInstanceStorage();
}
TaskManager::TaskManager() = default;
TaskManager::~TaskManager() = default;
void TaskManager::ExecuteTask(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const std::string& task_name) {
LOGFN(VERBOSE);
// Get an instance of the task and execute it.
std::unique_ptr<Task> task((task_list_[task_name]).Run());
if (task == nullptr) {
LOGFN(ERROR) << task_name << " task is null";
return;
}
HRESULT hr =
UserContextEnumerator::Get()->PerformTask(task_name, *task.get());
// Calculate next time the task should be executed.
base::TimeDelta next_run;
if (FAILED(hr)) {
LOGFN(ERROR) << task_name << " failed hr=" << putHR(hr);
// Check whether a backoff entry exists for the task. If so, consider the
// backoff period when calculating next run.
if (task_execution_backoffs_.find(task_name) ==
task_execution_backoffs_.end()) {
// First set the template backoff policy for the task.
task_execution_policies_[task_name] =
std::make_unique<net::BackoffEntry::Policy>();
*task_execution_policies_[task_name] = kRetryLaterPolicy;
// Max backoff time shouldn't be more than task execution period.
task_execution_policies_[task_name]->maximum_backoff_ms =
task->GetConfig().execution_period.InMilliseconds();
const net::BackoffEntry::Policy* policy =
task_execution_policies_.find(task_name)->second.get();
// Create backoff entry as this is the first failure after a success.
task_execution_backoffs_[task_name] =
std::make_unique<net::BackoffEntry>(policy);
}
task_execution_backoffs_[task_name]->InformOfRequest(false);
// Get backoff time for the next request.
next_run = task_execution_backoffs_[task_name]->GetTimeUntilRelease();
} else {
// Clear the back off entry as the task succeeded. An alternative was to
// keep it and report success.
task_execution_backoffs_.erase(task_name);
next_run = task->GetConfig().execution_period;
LOGFN(INFO) << task_name << " was executed successfully!";
const base::Time sync_time = base::Time::Now();
const std::wstring sync_time_millis = base::NumberToWString(
sync_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
SetGlobalFlag(GetLastSyncRegNameForTask(base::UTF8ToWide(task_name)),
sync_time_millis);
}
// Schedule next task execution.
task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TaskManager::ExecuteTask, base::Unretained(this),
task_runner, task_name),
next_run);
}
void TaskManager::RunTasks(
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
// Enumerate registered tasks and kick-off the periodic execution.
for (auto it = task_list_.begin(); it != task_list_.end(); ++it) {
// Get an instance of registered Task.
std::unique_ptr<Task> task((it->second).Run());
if (task == nullptr) {
LOGFN(ERROR) << it->first << " task is null";
continue;
}
// Calculate the next run so that periodic polling happens within
// proper time intervals. When the tasks are scheduled, we don't want to
// immediately start executing to allow some warm-up.
base::TimeDelta next_run = base::Seconds(10);
const base::TimeDelta time_since_last_run =
GetTimeDeltaSinceLastPeriodicSync(
GetLastSyncRegNameForTask(base::UTF8ToWide(it->first)));
if (time_since_last_run < task->GetConfig().execution_period)
next_run = task->GetConfig().execution_period - time_since_last_run;
task_runner->PostDelayedTask(
FROM_HERE,
base::BindOnce(&TaskManager::ExecuteTask, base::Unretained(this),
task_runner, it->first),
next_run);
}
}
void TaskManager::RegisterTask(const std::string& task_name,
TaskCreator task_creator) {
LOGFN(VERBOSE);
task_list_.emplace(task_name, std::move(task_creator));
}
} // namespace extension
} // namespace credential_provider
|