1 /*
2 * Copyright (c) 2022 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
16 #include "utils_state_machine.h"
17 #include "utils_log.h"
18 #include "utils_mem.h"
19
20 #include <stddef.h>
21
22 typedef struct {
23 ListNode link;
24 uint32_t event;
25 const void *para;
26 } PendingEvent;
27
InitStateMachine(StateMachine * machine,uint32_t machineId,uint32_t initState)28 void InitStateMachine(StateMachine *machine, uint32_t machineId, uint32_t initState)
29 {
30 if (machine == NULL) {
31 return;
32 }
33 machine->machineId = machineId;
34 machine->isScheduling = false;
35 machine->currState = initState;
36 InitListHead(&machine->pendingEventList);
37 InitRecursiveMutex(&machine->mutex);
38 }
39
GetScheduleStateNode(const StateNode * nodes,uint32_t nodeCnt,uint32_t state,uint32_t event)40 static const StateNode *GetScheduleStateNode(const StateNode *nodes, uint32_t nodeCnt, uint32_t state, uint32_t event)
41 {
42 for (uint32_t i = 0; i < nodeCnt; i++) {
43 const StateNode *node = nodes + i;
44 if ((node->state == state) && (node->event == event)) {
45 return node;
46 }
47 }
48 return NULL;
49 }
50
PushPendingEvent(StateMachine * machine,uint32_t event,const void * para)51 static inline void PushPendingEvent(StateMachine *machine, uint32_t event, const void *para)
52 {
53 PendingEvent *pending = MALLOC(sizeof(PendingEvent));
54 if (pending == NULL) {
55 return;
56 }
57 pending->event = event;
58 pending->para = para;
59 AddListNodeBefore(&pending->link, &machine->pendingEventList);
60 }
61
PopPendingEvent(StateMachine * machine,uint32_t * event,const void ** para)62 static inline bool PopPendingEvent(StateMachine *machine, uint32_t *event, const void **para)
63 {
64 ListHead *head = &machine->pendingEventList;
65 if (IsEmptyList(head)) {
66 return false;
67 }
68 PendingEvent *pending = LIST_ENTRY(head->next, PendingEvent, link);
69 RemoveListNode(&pending->link);
70 *event = pending->event;
71 *para = pending->para;
72 FREE(pending);
73 return true;
74 }
75
ScheduleMachine(const StateNode * nodes,uint32_t nodeCnt,StateMachine * machine,uint32_t event,const void * para)76 void ScheduleMachine(const StateNode *nodes, uint32_t nodeCnt, StateMachine *machine, uint32_t event, const void *para)
77 {
78 // EventPara could be null, need not to check
79 if ((nodes == NULL) || (nodeCnt == 0) || (machine == NULL)) {
80 SECURITY_LOG_ERROR("invlid params, nodes or context is null");
81 return;
82 }
83 LockMutex(&machine->mutex);
84
85 if (machine->isScheduling) {
86 PushPendingEvent(machine, event, para);
87 UnlockMutex(&machine->mutex);
88 return;
89 }
90
91 uint32_t state = machine->currState;
92 const StateNode *node = GetScheduleStateNode(nodes, nodeCnt, state, event);
93 if (node != NULL) {
94 bool result = true;
95 if (node->process != NULL) {
96 machine->isScheduling = true;
97 result = node->process(machine, event, para);
98 machine->isScheduling = false;
99 }
100 machine->currState = (result == true) ? node->nextStateT : node->nextStateF;
101 }
102 SECURITY_LOG_INFO("Statemachine(%{public}x) schedule state(%{public}u) + event(%{public}u) -> newState(%{public}u)",
103 machine->machineId, state, event, machine->currState);
104 uint32_t nextEvent = 0;
105 const void *nextPara = NULL;
106 bool isPending = PopPendingEvent(machine, &nextEvent, &nextPara);
107 UnlockMutex(&machine->mutex);
108 if (isPending) {
109 return ScheduleMachine(nodes, nodeCnt, machine, nextEvent, nextPara);
110 }
111 }
112