state-machine.h revision 1.1 1 1.1 christos /* state-machine.h
2 1.1 christos *
3 1.1 christos * Copyright (c) 2023-2024 Apple Inc. All rights reserved.
4 1.1 christos *
5 1.1 christos * Licensed under the Apache License, Version 2.0 (the "License");
6 1.1 christos * you may not use this file except in compliance with the License.
7 1.1 christos * You may obtain a copy of the License at
8 1.1 christos *
9 1.1 christos * https://www.apache.org/licenses/LICENSE-2.0
10 1.1 christos *
11 1.1 christos * Unless required by applicable law or agreed to in writing, software
12 1.1 christos * distributed under the License is distributed on an "AS IS" BASIS,
13 1.1 christos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 1.1 christos * See the License for the specific language governing permissions and
15 1.1 christos * limitations under the License.
16 1.1 christos *
17 1.1 christos * This file contains general support definitions for state machines in the Thread Border Router
18 1.1 christos * implementation.
19 1.1 christos */
20 1.1 christos
21 1.1 christos #ifndef __STATE_MACHINE_H__
22 1.1 christos #define __STATE_MACHINE_H__ 1
23 1.1 christos
24 1.1 christos #define RELEASE_RETAIN_FUNCS(type) \
25 1.1 christos void \
26 1.1 christos type##_retain_(type##_t * omw, const char *file, int line) \
27 1.1 christos { \
28 1.1 christos RETAIN(omw, type); \
29 1.1 christos } \
30 1.1 christos \
31 1.1 christos void \
32 1.1 christos type##_release_(type##_t *NONNULL omw, const char *file, int line) \
33 1.1 christos { \
34 1.1 christos RELEASE(omw, type); \
35 1.1 christos }
36 1.1 christos
37 1.1 christos #define RELEASE_RETAIN_DECLS(type) \
38 1.1 christos void type##_retain_(type##_t *NONNULL omw, const char *NONNULL file, int line); \
39 1.1 christos void type##_release_(type##_t *NONNULL omw, const char *NONNULL file, int line);
40 1.1 christos
41 1.1 christos // The assumptions below are that every object that holds a state that these macros can operate on has
42 1.1 christos // the following elements:
43 1.1 christos //
44 1.1 christos // name: (char *), NUL terminated, name of object instance
45 1.1 christos // state_name: (const char *), NUL terminated, name of the state the object is in
46 1.1 christos // state: the current state of the state machine, as an enum
47 1.1 christos //
48 1.1 christos // For macros that take events, the event is assumed to have the following elements:
49 1.1 christos //
50 1.1 christos // name: (char *), NUL terminated, name of event type (iow, not specific to an event instance)
51 1.1 christos
52 1.1 christos // For states that never receive events.
53 1.1 christos #define BR_REQUIRE_STATE_OBJECT_EVENT_NULL(state_object, event) \
54 1.1 christos do { \
55 1.1 christos if ((event) != NULL) { \
56 1.1 christos ERROR(PUB_S_SRP "/" PRI_S_SRP ": received unexpected " PUB_S_SRP " event in state " PUB_S_SRP, \
57 1.1 christos state_object->state_header.state_machine_type_name, \
58 1.1 christos state_object->state_header.name, event->name, state_object->state_header.state_name); \
59 1.1 christos return state_machine_state_invalid; \
60 1.1 christos } \
61 1.1 christos } while (false)
62 1.1 christos
63 1.1 christos // Announce that we have entered a state that takes no events
64 1.1 christos #define BR_STATE_ANNOUNCE_NO_EVENTS(state_object) \
65 1.1 christos do { \
66 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, state_object->state_header.name, \
67 1.1 christos state_object->state_header.state_machine_type_name, \
68 1.1 christos state_object->state_header.state_name); \
69 1.1 christos } while (false)
70 1.1 christos
71 1.1 christos // Announce that we have entered a state that takes no events, and include a domain name
72 1.1 christos #define BR_STATE_ANNOUNCE_NO_EVENTS_NAME(state_object, fqdn) \
73 1.1 christos do { \
74 1.1 christos char hostname[kDNSServiceMaxDomainName]; \
75 1.1 christos dns_name_print(fqdn, hostname, sizeof(hostname)); \
76 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP " with host " PRI_S_SRP, \
77 1.1 christos state_object->state_header.state_machine_type_name, \
78 1.1 christos state_object->state_header.name, state_object->state_header.state_name, hostname); \
79 1.1 christos } while (false)
80 1.1 christos
81 1.1 christos // Announce that we have entered a state that takes no events
82 1.1 christos #define BR_STATE_ANNOUNCE(state_object, event) \
83 1.1 christos do { \
84 1.1 christos if (event != NULL) { \
85 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP, \
86 1.1 christos state_object->state_header.state_machine_type_name, \
87 1.1 christos state_object->state_header.name, event->name, state_object->state_header.state_name); \
88 1.1 christos } else { \
89 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, \
90 1.1 christos state_object->state_header.state_machine_type_name, \
91 1.1 christos state_object->state_header.name, state_object->state_header.state_name); \
92 1.1 christos } \
93 1.1 christos } while (false)
94 1.1 christos
95 1.1 christos #define BR_UNEXPECTED_EVENT_MAIN(state_object, event, bad, event_is_message) \
96 1.1 christos do { \
97 1.1 christos if (event_is_message(event)) { \
98 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": invalid event " PUB_S_SRP " in state " PUB_S_SRP, \
99 1.1 christos state_object->state_header.state_machine_type_name, \
100 1.1 christos (state_object)->state_header.name, (event)->name, state_object->state_header.state_name); \
101 1.1 christos return (int)bad; \
102 1.1 christos } \
103 1.1 christos INFO(PUB_S_SRP "/" PRI_S_SRP ": unexpected event " PUB_S_SRP " in state " PUB_S_SRP, \
104 1.1 christos state_object->state_header.state_machine_type_name, \
105 1.1 christos (state_object)->state_header.name, (event)->name, \
106 1.1 christos state_object->state_header.state_name); \
107 1.1 christos return (int)state_machine_state_invalid; \
108 1.1 christos } while (false)
109 1.1 christos
110 1.1 christos // UNEXPECTED_EVENT flags the response as bad on a protocol level, triggering a retry delay
111 1.1 christos // UNEXPECTED_EVENT_NO_ERROR doesn't.
112 1.1 christos #define BR_UNEXPECTED_EVENT(state_object, event) \
113 1.1 christos BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_machine_state_invalid, \
114 1.1 christos state_machine_event_is_message)
115 1.1 christos #define BR_UNEXPECTED_EVENT_NO_ERROR(state_object, event) \
116 1.1 christos BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_object_drop_state(state_object->instance, state_object), \
117 1.1 christos state_machine_event_is_message)
118 1.1 christos
119 1.1 christos // Generalized border router event object
120 1.1 christos
121 1.1 christos #define state_machine_event_is_message(x) false
122 1.1 christos
123 1.1 christos typedef enum {
124 1.1 christos state_machine_event_type_invalid,
125 1.1 christos state_machine_event_type_timeout,
126 1.1 christos state_machine_event_type_prefix,
127 1.1 christos state_machine_event_type_dhcp,
128 1.1 christos state_machine_event_type_service_list_changed,
129 1.1 christos state_machine_event_type_listener_ready,
130 1.1 christos state_machine_event_type_listener_canceled,
131 1.1 christos state_machine_event_type_ml_eid_changed,
132 1.1 christos state_machine_event_type_rloc_changed,
133 1.1 christos state_machine_event_type_thread_network_state_changed,
134 1.1 christos state_machine_event_type_thread_node_type_changed,
135 1.1 christos state_machine_event_type_probe_completed,
136 1.1 christos state_machine_event_type_got_mesh_local_prefix,
137 1.1 christos state_machine_event_type_daemon_disconnect,
138 1.1 christos state_machine_event_type_stop,
139 1.1 christos state_machine_event_type_dns_registration_invalidated,
140 1.1 christos state_machine_event_type_thread_interface_changed,
141 1.1 christos state_machine_event_type_wed_ml_eid_changed,
142 1.1 christos state_machine_event_type_neighbor_ml_eid_changed,
143 1.1 christos state_machine_event_type_srp_needed,
144 1.1 christos state_machine_event_type_dns_registration_bad_service,
145 1.1 christos } state_machine_event_type_t;
146 1.1 christos
147 1.1 christos typedef struct state_machine_event state_machine_event_t;
148 1.1 christos typedef struct state_machine_header state_machine_header_t;
149 1.1 christos typedef void (*state_machine_event_finalize_callback_t)(state_machine_event_t *NONNULL event);
150 1.1 christos typedef struct omr_prefix omr_prefix_t;
151 1.1 christos struct state_machine_event {
152 1.1 christos int ref_count;
153 1.1 christos const char *NULLABLE name;
154 1.1 christos state_machine_event_type_t type;
155 1.1 christos omr_prefix_t *NULLABLE thread_prefixes;
156 1.1 christos state_machine_event_finalize_callback_t NULLABLE finalize;
157 1.1 christos };
158 1.1 christos
159 1.1 christos #ifndef STATE_MACHINE_IMPLEMENTATION
160 1.1 christos typedef enum state_machine_state {
161 1.1 christos state_machine_state_invalid = 0,
162 1.1 christos } state_machine_state_t;
163 1.1 christos #endif // STATE_MACHINE_IMPLEMENTATION
164 1.1 christos
165 1.1 christos typedef state_machine_state_t (*state_machine_action_t)(state_machine_header_t *NONNULL state_header, state_machine_event_t *NULLABLE event);
166 1.1 christos typedef struct state_machine_state_decl {
167 1.1 christos state_machine_state_t state;
168 1.1 christos const char *NONNULL name;
169 1.1 christos state_machine_action_t NONNULL action;
170 1.1 christos } state_machine_decl_t;
171 1.1 christos
172 1.1 christos typedef enum {
173 1.1 christos state_machine_type_invalid,
174 1.1 christos state_machine_type_omr_publisher,
175 1.1 christos state_machine_type_service_publisher,
176 1.1 christos state_machine_type_dnssd_client,
177 1.1 christos } state_machine_type_t;
178 1.1 christos
179 1.1 christos struct state_machine_header {
180 1.1 christos char *NULLABLE name;
181 1.1 christos void *NULLABLE state_object;
182 1.1 christos const char *NULLABLE state_name;
183 1.1 christos state_machine_decl_t *NULLABLE states;
184 1.1 christos const char *NULLABLE state_machine_type_name;
185 1.1 christos size_t num_states;
186 1.1 christos state_machine_state_t state;
187 1.1 christos state_machine_type_t state_machine_type;
188 1.1 christos bool once;
189 1.1 christos };
190 1.1 christos
191 1.1 christos void state_machine_next_state(state_machine_header_t *NONNULL state_header, state_machine_state_t state);
192 1.1 christos void state_machine_event_finalize(state_machine_event_t *NONNULL event);
193 1.1 christos RELEASE_RETAIN_DECLS(state_machine_event);
194 1.1 christos state_machine_event_t *NULLABLE
195 1.1 christos state_machine_event_create(state_machine_event_type_t type,
196 1.1 christos state_machine_event_finalize_callback_t NULLABLE finalize_callback);
197 1.1 christos void state_machine_event_deliver(state_machine_header_t *NONNULL state_header, state_machine_event_t *NONNULL event);
198 1.1 christos bool state_machine_header_setup(state_machine_header_t *NONNULL state_header, void *NONNULL state_object, const char *NULLABLE name,
199 1.1 christos state_machine_type_t type, state_machine_decl_t *NONNULL states, size_t num_states);
200 1.1 christos void state_machine_cancel(state_machine_header_t *NONNULL state_header);
201 1.1 christos #endif // __STATE_MACHINE_H__
202 1.1 christos
203 1.1 christos // Local Variables:
204 1.1 christos // mode: C
205 1.1 christos // tab-width: 4
206 1.1 christos // c-file-style: "bsd"
207 1.1 christos // c-basic-offset: 4
208 1.1 christos // fill-column: 120
209 1.1 christos // indent-tabs-mode: nil
210 1.1 christos // End:
211