1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "cloud_sync_state_machine.h"
16 
17 #include <cmath>
18 #include <climits>
19 #include <algorithm>
20 
21 #include "db_errno.h"
22 #include "log_print.h"
23 #include "runtime_context.h"
24 
25 namespace DistributedDB {
26 using Event = CloudSyncEvent;
27 using State = CloudSyncState;
28 namespace {
29     // used for state switch table
30     constexpr int CURRENT_STATE_INDEX = 0;
31     constexpr int EVENT_INDEX = 1;
32     constexpr int OUTPUT_STATE_INDEX = 2;
33 
34     const std::vector<std::vector<uint8_t>> STATE_SWITCH_TABLE = {
35         // In IDEL state
36         {State::IDLE, Event::START_SYNC_EVENT, State::DO_DOWNLOAD},
37         {State::IDLE, Event::ERROR_EVENT, State::DO_FINISHED},
38 
39         // In DO_DOWNLOAD state
40         {State::DO_DOWNLOAD, Event::DOWNLOAD_FINISHED_EVENT, State::DO_UPLOAD},
41         {State::DO_DOWNLOAD, Event::ERROR_EVENT, State::DO_FINISHED},
42 
43         // In DO_UPLOAD state
44         {State::DO_UPLOAD, Event::UPLOAD_FINISHED_EVENT, State::DO_FINISHED},
45         {State::DO_UPLOAD, Event::ERROR_EVENT, State::DO_FINISHED},
46         {State::DO_UPLOAD, Event::REPEAT_CHECK_EVENT, State::DO_REPEAT_CHECK},
47 
48         // Repeat download check state
49         {State::DO_REPEAT_CHECK, Event::REPEAT_DOWNLOAD_EVENT, State::DO_DOWNLOAD},
50         {State::DO_REPEAT_CHECK, Event::ERROR_EVENT, State::DO_FINISHED},
51 
52         // In DO_FINISHED state
53         {State::DO_FINISHED, Event::ALL_TASK_FINISHED_EVENT, State::IDLE},
54     };
55 }
56 
57 std::mutex CloudSyncStateMachine::stateSwitchTableLock_;
58 std::vector<CloudStateSwitchTable> CloudSyncStateMachine::stateSwitchTables_;
59 bool CloudSyncStateMachine::isStateSwitchTableInited_ = false;
60 
Initialize()61 int CloudSyncStateMachine::Initialize()
62 {
63     InitCloudStateSwitchTables();
64     return E_OK;
65 }
66 
SyncStep()67 void CloudSyncStateMachine::SyncStep()
68 {
69     Event event = Event::ERROR_EVENT;
70     do {
71         auto iter = stateMapping_.find(currentState_);
72         if (iter != stateMapping_.end()) {
73             event = static_cast<Event>(iter->second());
74         } else {
75             LOGE("[CloudSyncStateMachine][SyncStep] can not find state=%d", currentState_);
76             break;
77         }
78     } while (SwitchMachineState(event) == E_OK && currentState_ != State::IDLE);
79 }
80 
InitCloudStateSwitchTables()81 void CloudSyncStateMachine::InitCloudStateSwitchTables()
82 {
83     if (isStateSwitchTableInited_) {
84         return;
85     }
86 
87     std::lock_guard<std::mutex> lock(stateSwitchTableLock_);
88     if (isStateSwitchTableInited_) {
89         return;
90     }
91 
92     InitCloudStateSwitchTable(STATE_SWITCH_TABLE);
93     isStateSwitchTableInited_ = true;
94 }
95 
InitCloudStateSwitchTable(const std::vector<std::vector<uint8_t>> & switchTable)96 void CloudSyncStateMachine::InitCloudStateSwitchTable(
97     const std::vector<std::vector<uint8_t>> &switchTable)
98 {
99     CloudStateSwitchTable table;
100     for (const auto &stateSwitch : switchTable) {
101         if (stateSwitch.size() <= OUTPUT_STATE_INDEX) {
102             LOGE("[StateMachine][InitSwitchTable] stateSwitch size err,size=%zu", stateSwitch.size());
103             return;
104         }
105         if (table.switchTable.count(stateSwitch[CURRENT_STATE_INDEX]) == 0) {
106             EventToState eventToState; // new EventToState
107             eventToState[stateSwitch[EVENT_INDEX]] = stateSwitch[OUTPUT_STATE_INDEX];
108             table.switchTable[stateSwitch[CURRENT_STATE_INDEX]] = eventToState;
109         } else { // key stateSwitch[CURRENT_STATE_INDEX] already has EventToState
110             EventToState &eventToState = table.switchTable[stateSwitch[CURRENT_STATE_INDEX]];
111             eventToState[stateSwitch[EVENT_INDEX]] = stateSwitch[OUTPUT_STATE_INDEX];
112         }
113     }
114     stateSwitchTables_.push_back(table);
115 }
116 
RegisterFunc(State state,const std::function<uint8_t (void)> & function)117 void CloudSyncStateMachine::RegisterFunc(State state, const std::function<uint8_t(void)> &function)
118 {
119     stateMapping_[state] = function;
120 };
121 
SwitchMachineState(uint8_t event)122 int CloudSyncStateMachine::SwitchMachineState(uint8_t event)
123 {
124     auto tableIter = std::find_if(stateSwitchTables_.begin(), stateSwitchTables_.end(),
125         [](const CloudStateSwitchTable &table) {
126             return table.version == 0;
127         });
128     if (tableIter == stateSwitchTables_.end()) {
129         LOGE("[CloudSyncStateMachine][SwitchState] Can't find a compatible state switch table.");
130         return -E_NOT_FOUND;
131     }
132 
133     const std::map<uint8_t, EventToState> &table = (*tableIter).switchTable;
134     auto eventToStateIter = table.find(currentState_);
135     if (eventToStateIter == table.end()) {
136         LOGE("[CloudSyncStateMachine][SwitchState] Can't find EventToState with currentSate %u",
137             currentState_);
138         return E_OK;
139     }
140 
141     const EventToState &eventToState = eventToStateIter->second;
142     auto stateIter = eventToState.find(event);
143     if (stateIter == eventToState.end()) {
144         LOGD("[CloudSyncStateMachine][SwitchState] Can't find event %u int currentSate %u ignore",
145             event, currentState_);
146         return -E_NOT_FOUND;
147     }
148 
149     currentState_ = static_cast<CloudSyncState>(stateIter->second);
150     return E_OK;
151 }
152 
SwitchStateAndStep(uint8_t event)153 void CloudSyncStateMachine::SwitchStateAndStep(uint8_t event)
154 {
155     if (SwitchMachineState(event) == E_OK) {
156         SyncStep();
157     }
158 }
159 
160 } // namespace DistributedDB
161