Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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