1 //
2 // Copyright (C) 2009 The Android Open Source Project
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 #include "update_engine/common/action_processor.h"
18 
19 #include <string>
20 #include <utility>
21 
22 #include <base/logging.h>
23 
24 #include "update_engine/common/action.h"
25 #include "update_engine/common/error_code_utils.h"
26 
27 using std::string;
28 using std::unique_ptr;
29 
30 namespace chromeos_update_engine {
31 
~ActionProcessor()32 ActionProcessor::~ActionProcessor() {
33   if (IsRunning())
34     StopProcessing();
35 }
36 
EnqueueAction(unique_ptr<AbstractAction> action)37 void ActionProcessor::EnqueueAction(unique_ptr<AbstractAction> action) {
38   action->SetProcessor(this);
39   actions_.push_back(std::move(action));
40 }
41 
IsRunning() const42 bool ActionProcessor::IsRunning() const {
43   return current_action_ != nullptr || suspended_;
44 }
45 
StartProcessing()46 void ActionProcessor::StartProcessing() {
47   CHECK(!IsRunning());
48   if (!actions_.empty()) {
49     current_action_ = std::move(actions_.front());
50     actions_.pop_front();
51     LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
52     current_action_->PerformAction();
53   }
54 }
55 
StopProcessing()56 void ActionProcessor::StopProcessing() {
57   CHECK(IsRunning());
58   if (current_action_) {
59     current_action_->TerminateProcessing();
60   }
61   LOG(INFO) << "ActionProcessor: aborted "
62             << (current_action_ ? current_action_->Type() : "")
63             << (suspended_ ? " while suspended" : "");
64   current_action_.reset();
65   suspended_ = false;
66   // Delete all the actions before calling the delegate.
67   actions_.clear();
68   if (delegate_)
69     delegate_->ProcessingStopped(this);
70 }
71 
SuspendProcessing()72 void ActionProcessor::SuspendProcessing() {
73   // No current_action_ when not suspended means that the action processor was
74   // never started or already finished.
75   if (suspended_ || !current_action_) {
76     LOG(WARNING) << "Called SuspendProcessing while not processing.";
77     return;
78   }
79   suspended_ = true;
80 
81   // If there's a current action we should notify it that it should suspend, but
82   // the action can ignore that and terminate at any point.
83   LOG(INFO) << "ActionProcessor: suspending " << current_action_->Type();
84   current_action_->SuspendAction();
85 }
86 
ResumeProcessing()87 void ActionProcessor::ResumeProcessing() {
88   if (!suspended_) {
89     LOG(WARNING) << "Called ResumeProcessing while not suspended.";
90     return;
91   }
92   suspended_ = false;
93   if (current_action_) {
94     // The current_action_ did not call ActionComplete while suspended, so we
95     // should notify it of the resume operation.
96     LOG(INFO) << "ActionProcessor: resuming " << current_action_->Type();
97     current_action_->ResumeAction();
98   } else {
99     // The last action called ActionComplete while suspended, so there is
100     // already a log message with the type of the finished action. We simply
101     // state that we are resuming processing and the next function will log the
102     // start of the next action or processing completion.
103     LOG(INFO) << "ActionProcessor: resuming processing";
104     StartNextActionOrFinish(suspended_error_code_);
105   }
106 }
107 
ActionComplete(AbstractAction * actionptr,ErrorCode code)108 void ActionProcessor::ActionComplete(AbstractAction* actionptr,
109                                      ErrorCode code) {
110   CHECK_EQ(actionptr, current_action_.get());
111   if (delegate_)
112     delegate_->ActionCompleted(this, actionptr, code);
113   string old_type = current_action_->Type();
114   current_action_->ActionCompleted(code);
115   current_action_.reset();
116   LOG(INFO) << "ActionProcessor: finished "
117             << (actions_.empty() ? "last action " : "") << old_type
118             << (suspended_ ? " while suspended" : "") << " with code "
119             << utils::ErrorCodeToString(code);
120   if (!actions_.empty() && code != ErrorCode::kSuccess) {
121     LOG(INFO) << "ActionProcessor: Aborting processing due to failure.";
122     actions_.clear();
123   }
124   if (suspended_) {
125     // If an action finished while suspended we don't start the next action (or
126     // terminate the processing) until the processor is resumed. This condition
127     // will be flagged by a nullptr current_action_ while suspended_ is true.
128     suspended_error_code_ = code;
129     return;
130   }
131   StartNextActionOrFinish(code);
132 }
133 
StartNextActionOrFinish(ErrorCode code)134 void ActionProcessor::StartNextActionOrFinish(ErrorCode code) {
135   if (actions_.empty()) {
136     if (delegate_) {
137       delegate_->ProcessingDone(this, code);
138     }
139     return;
140   }
141   current_action_ = std::move(actions_.front());
142   actions_.pop_front();
143   LOG(INFO) << "ActionProcessor: starting " << current_action_->Type();
144   current_action_->PerformAction();
145 }
146 
147 }  // namespace chromeos_update_engine
148