blob: 8faae35540bba675315dc852c9ba7dfb85962325 [file] [log] [blame]
Vitaly Buka4615e0d2015-10-14 15:35:12 -07001// Copyright 2015 The Weave Authors. All rights reserved.
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Stefan Sauer2d16dfa2015-09-25 17:08:35 +02005#include "src/commands/command_queue.h"
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -07006
Vitaly Bukaae0f3a12015-05-11 16:27:30 -07007#include <base/bind.h>
Vitaly Buka2a9b30f2015-04-01 10:51:59 -07008#include <base/time/time.h>
9
Vitaly Bukab6f015a2015-07-09 14:59:23 -070010namespace weave {
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070011
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070012namespace {
13const int kRemoveCommandDelayMin = 5;
Alex Vakulenko88f55d82015-12-03 15:30:27 -080014
15std::string GetCommandHandlerKey(const std::string& component_path,
16 const std::string& command_name) {
17 return component_path + ":" + command_name;
18}
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070019}
20
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080021CommandQueue::CommandQueue(provider::TaskRunner* task_runner,
22 base::Clock* clock)
23 : task_runner_{task_runner}, clock_{clock} {}
24
Vitaly Buka695a5fb2015-10-06 16:26:08 -070025void CommandQueue::AddCommandAddedCallback(const CommandCallback& callback) {
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070026 on_command_added_.push_back(callback);
Vitaly Buka72410b22015-05-13 13:48:59 -070027 // Send all pre-existed commands.
28 for (const auto& command : map_)
29 callback.Run(command.second.get());
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070030}
31
Vitaly Buka695a5fb2015-10-06 16:26:08 -070032void CommandQueue::AddCommandRemovedCallback(const CommandCallback& callback) {
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070033 on_command_removed_.push_back(callback);
34}
35
Vitaly Bukac6029262015-10-07 09:29:13 -070036void CommandQueue::AddCommandHandler(
Alex Vakulenko88f55d82015-12-03 15:30:27 -080037 const std::string& component_path,
Vitaly Bukac6029262015-10-07 09:29:13 -070038 const std::string& command_name,
39 const Device::CommandHandlerCallback& callback) {
Vitaly Buka695a5fb2015-10-06 16:26:08 -070040 if (!command_name.empty()) {
41 CHECK(default_command_callback_.is_null())
42 << "Commands specific handler are not allowed after default one";
43
44 for (const auto& command : map_) {
Vitaly Buka0209da42015-10-08 00:07:18 -070045 if (command.second->GetState() == Command::State::kQueued &&
Alex Vakulenko88f55d82015-12-03 15:30:27 -080046 command.second->GetName() == command_name &&
47 command.second->GetComponent() == component_path) {
Vitaly Bukac6029262015-10-07 09:29:13 -070048 callback.Run(command.second);
Vitaly Buka695a5fb2015-10-06 16:26:08 -070049 }
50 }
51
Alex Vakulenko88f55d82015-12-03 15:30:27 -080052 std::string key = GetCommandHandlerKey(component_path, command_name);
53 CHECK(command_callbacks_.insert(std::make_pair(key, callback)).second)
Vitaly Buka695a5fb2015-10-06 16:26:08 -070054 << command_name << " already has handler";
55
56 } else {
Alex Vakulenko88f55d82015-12-03 15:30:27 -080057 CHECK(component_path.empty())
58 << "Default handler must not be component-specific";
Vitaly Buka695a5fb2015-10-06 16:26:08 -070059 for (const auto& command : map_) {
Alex Vakulenko88f55d82015-12-03 15:30:27 -080060 std::string key = GetCommandHandlerKey(command.second->GetComponent(),
61 command.second->GetName());
Vitaly Buka0209da42015-10-08 00:07:18 -070062 if (command.second->GetState() == Command::State::kQueued &&
Alex Vakulenko88f55d82015-12-03 15:30:27 -080063 command_callbacks_.find(key) == command_callbacks_.end()) {
Vitaly Bukac6029262015-10-07 09:29:13 -070064 callback.Run(command.second);
Vitaly Buka695a5fb2015-10-06 16:26:08 -070065 }
66 }
67
68 CHECK(default_command_callback_.is_null()) << "Already has default handler";
69 default_command_callback_ = callback;
70 }
71}
72
Anton Muhin5191e812014-10-30 17:49:48 +040073void CommandQueue::Add(std::unique_ptr<CommandInstance> instance) {
74 std::string id = instance->GetID();
75 LOG_IF(FATAL, id.empty()) << "Command has no ID";
Vitaly Bukac6029262015-10-07 09:29:13 -070076 instance->AttachToQueue(this);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070077 auto pair = map_.insert(std::make_pair(id, std::move(instance)));
78 LOG_IF(FATAL, !pair.second) << "Command with ID '" << id
79 << "' is already in the queue";
Vitaly Bukaae0f3a12015-05-11 16:27:30 -070080 for (const auto& cb : on_command_added_)
81 cb.Run(pair.first->second.get());
Vitaly Buka695a5fb2015-10-06 16:26:08 -070082
Alex Vakulenko88f55d82015-12-03 15:30:27 -080083 std::string key = GetCommandHandlerKey(pair.first->second->GetComponent(),
84 pair.first->second->GetName());
85 auto it_handler = command_callbacks_.find(key);
Vitaly Buka695a5fb2015-10-06 16:26:08 -070086
87 if (it_handler != command_callbacks_.end())
Vitaly Bukac6029262015-10-07 09:29:13 -070088 it_handler->second.Run(pair.first->second);
Vitaly Buka695a5fb2015-10-06 16:26:08 -070089 else if (!default_command_callback_.is_null())
Vitaly Bukac6029262015-10-07 09:29:13 -070090 default_command_callback_.Run(pair.first->second);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070091}
92
Alex Vakulenko329ad802016-02-01 12:11:30 -080093void CommandQueue::RemoveLater(const std::string& id) {
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -070094 auto p = map_.find(id);
Vitaly Buka2a9b30f2015-04-01 10:51:59 -070095 if (p == map_.end())
96 return;
Alex Vakulenko98d1fee2016-02-01 12:25:21 -080097 auto remove_delay = base::TimeDelta::FromMinutes(kRemoveCommandDelayMin);
98 remove_queue_.push(std::make_pair(clock_->Now() + remove_delay, id));
99 if (remove_queue_.size() == 1) {
100 // The queue was empty, this is the first command to be removed, schedule
101 // a clean-up task.
102 ScheduleCleanup(remove_delay);
103 }
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700104}
105
106bool CommandQueue::Remove(const std::string& id) {
107 auto p = map_.find(id);
108 if (p == map_.end())
109 return false;
Vitaly Bukac6029262015-10-07 09:29:13 -0700110 std::shared_ptr<CommandInstance> instance = p->second;
111 instance->DetachFromQueue();
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700112 map_.erase(p);
Vitaly Bukaae0f3a12015-05-11 16:27:30 -0700113 for (const auto& cb : on_command_removed_)
114 cb.Run(instance.get());
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700115 return true;
116}
117
Alex Vakulenko98d1fee2016-02-01 12:25:21 -0800118void CommandQueue::Cleanup(const base::Time& cutoff_time) {
119 while (!remove_queue_.empty() && remove_queue_.top().first <= cutoff_time) {
120 Remove(remove_queue_.top().second);
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700121 remove_queue_.pop();
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700122 }
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700123}
124
Alex Vakulenko98d1fee2016-02-01 12:25:21 -0800125void CommandQueue::ScheduleCleanup(base::TimeDelta delay) {
126 task_runner_->PostDelayedTask(
Vitaly Buka5e94dc82016-03-01 13:03:01 -0800127 FROM_HERE, base::Bind(&CommandQueue::PerformScheduledCleanup,
128 weak_ptr_factory_.GetWeakPtr()),
Alex Vakulenko98d1fee2016-02-01 12:25:21 -0800129 delay);
Vitaly Buka2a9b30f2015-04-01 10:51:59 -0700130}
131
Alex Vakulenko98d1fee2016-02-01 12:25:21 -0800132void CommandQueue::PerformScheduledCleanup() {
133 base::Time now = clock_->Now();
134 Cleanup(now);
135 if (!remove_queue_.empty())
136 ScheduleCleanup(remove_queue_.top().first - now);
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700137}
138
Alex Vakulenkofedc4872014-08-20 12:38:43 -0700139CommandInstance* CommandQueue::Find(const std::string& id) const {
Alex Vakulenkoaa3a5592014-08-07 07:24:06 -0700140 auto p = map_.find(id);
141 return (p != map_.end()) ? p->second.get() : nullptr;
142}
143
Vitaly Bukab6f015a2015-07-09 14:59:23 -0700144} // namespace weave