blob: 724ed610f931e3d2bfbb5c2bb7bd73a9730cff4d [file] [log] [blame]
/* state-machine.h
*
* Copyright (c) 2023 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file contains general support definitions for state machines in the Thread Border Router
* implementation.
*/
#ifndef __STATE_MACHINE_H__
#define __STATE_MACHINE_H__ 1
#define RELEASE_RETAIN_FUNCS(type) \
void \
type##_retain(type##_t *NONNULL omw) \
{ \
RETAIN_HERE(omw, type); \
} \
\
void \
type##_release(type##_t *NONNULL omw) \
{ \
RELEASE_HERE(omw, type); \
}
#define RELEASE_RETAIN_DECLS(type) \
void type##_retain(type##_t *NONNULL omw); \
void type##_release(type##_t *NONNULL omw)
// The assumptions below are that every object that holds a state that these macros can operate on has
// the following elements:
//
// name: (char *), NUL terminated, name of object instance
// state_name: (const char *), NUL terminated, name of the state the object is in
// state: the current state of the state machine, as an enum
//
// For macros that take events, the event is assumed to have the following elements:
//
// name: (char *), NUL terminated, name of event type (iow, not specific to an event instance)
// For states that never receive events.
#define BR_REQUIRE_STATE_OBJECT_EVENT_NULL(state_object, event) \
do { \
if ((event) != NULL) { \
ERROR(PUB_S_SRP "/" PRI_S_SRP ": received unexpected " PUB_S_SRP " event in state " PUB_S_SRP, \
state_object->state_header.state_machine_type_name, \
state_object->state_header.name, event->name, state_object->state_header.state_name); \
return state_machine_state_invalid; \
} \
} while (false)
// Announce that we have entered a state that takes no events
#define BR_STATE_ANNOUNCE_NO_EVENTS(state_object) \
do { \
INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, state_object->state_header.name, \
state_object->state_header.state_machine_type_name, \
state_object->state_header.state_name); \
} while (false)
// Announce that we have entered a state that takes no events, and include a domain name
#define BR_STATE_ANNOUNCE_NO_EVENTS_NAME(state_object, fqdn) \
do { \
char hostname[kDNSServiceMaxDomainName]; \
dns_name_print(fqdn, hostname, sizeof(hostname)); \
INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP " with host " PRI_S_SRP, \
state_object->state_header.state_machine_type_name, \
state_object->state_header.name, state_object->state_header.state_name, hostname); \
} while (false)
// Announce that we have entered a state that takes no events
#define BR_STATE_ANNOUNCE(state_object, event) \
do { \
if (event != NULL) { \
INFO(PUB_S_SRP "/" PRI_S_SRP ": event " PUB_S_SRP " received in state " PUB_S_SRP, \
state_object->state_header.state_machine_type_name, \
state_object->state_header.name, event->name, state_object->state_header.state_name); \
} else { \
INFO(PUB_S_SRP "/" PRI_S_SRP ": entering state " PUB_S_SRP, \
state_object->state_header.state_machine_type_name, \
state_object->state_header.name, state_object->state_header.state_name); \
} \
} while (false)
#define BR_UNEXPECTED_EVENT_MAIN(state_object, event, bad, event_is_message) \
do { \
if (event_is_message(event)) { \
INFO(PUB_S_SRP "/" PRI_S_SRP ": invalid event " PUB_S_SRP " in state " PUB_S_SRP, \
state_object->state_header.state_machine_type_name, \
(state_object)->state_header.name, (event)->name, state_object->state_header.state_name); \
return (int)bad; \
} \
INFO(PUB_S_SRP "/" PRI_S_SRP ": unexpected event " PUB_S_SRP " in state " PUB_S_SRP, \
state_object->state_header.state_machine_type_name, \
(state_object)->state_header.name, (event)->name, \
state_object->state_header.state_name); \
return (int)state_machine_state_invalid; \
} while (false)
// UNEXPECTED_EVENT flags the response as bad on a protocol level, triggering a retry delay
// UNEXPECTED_EVENT_NO_ERROR doesn't.
#define BR_UNEXPECTED_EVENT(state_object, event) \
BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_machine_state_invalid, \
state_machine_event_is_message)
#define BR_UNEXPECTED_EVENT_NO_ERROR(state_object, event) \
BR_UNEXPECTED_EVENT_MAIN(state_object, event, state_object_drop_state(state_object->instance, state_object), \
state_machine_event_is_message)
// Generalized border router event object
#define state_machine_event_is_message(x) false
typedef enum {
state_machine_event_type_invalid,
state_machine_event_type_timeout,
state_machine_event_type_prefix,
state_machine_event_type_dhcp,
state_machine_event_type_service_list_changed,
state_machine_event_type_listener_ready,
state_machine_event_type_listener_canceled,
state_machine_event_type_ml_eid_changed,
state_machine_event_type_rloc_changed,
state_machine_event_type_thread_network_state_changed,
state_machine_event_type_thread_node_type_changed,
state_machine_event_type_probe_completed,
state_machine_event_type_got_mesh_local_prefix,
state_machine_event_type_daemon_disconnect,
} state_machine_event_type_t;
typedef struct state_machine_event state_machine_event_t;
typedef struct state_machine_header state_machine_header_t;
typedef void (*state_machine_event_finalize_callback_t)(state_machine_event_t *NONNULL event);
typedef struct omr_prefix omr_prefix_t;
struct state_machine_event {
int ref_count;
const char *NULLABLE name;
state_machine_event_type_t type;
omr_prefix_t *NULLABLE thread_prefixes;
state_machine_event_finalize_callback_t NULLABLE finalize;
};
#ifndef STATE_MACHINE_IMPLEMENTATION
typedef enum state_machine_state {
state_machine_state_invalid = 0,
} state_machine_state_t;
#endif // STATE_MACHINE_IMPLEMENTATION
typedef state_machine_state_t (*state_machine_action_t)(state_machine_header_t *NONNULL state_header, state_machine_event_t *NULLABLE event);
typedef struct state_machine_state_decl {
state_machine_state_t state;
const char *NONNULL name;
state_machine_action_t NONNULL action;
} state_machine_decl_t;
typedef enum {
state_machine_type_invalid,
state_machine_type_omr_publisher,
state_machine_type_service_publisher,
state_machine_type_dnssd_client,
} state_machine_type_t;
struct state_machine_header {
char *NULLABLE name;
void *NULLABLE state_object;
const char *NULLABLE state_name;
state_machine_decl_t *NULLABLE states;
const char *NULLABLE state_machine_type_name;
size_t num_states;
state_machine_state_t state;
state_machine_type_t state_machine_type;
bool once;
};
void state_machine_next_state(state_machine_header_t *NONNULL state_header, state_machine_state_t state);
void state_machine_event_finalize(state_machine_event_t *NONNULL event);
RELEASE_RETAIN_DECLS(state_machine_event);
state_machine_event_t *NULLABLE
state_machine_event_create(state_machine_event_type_t type,
state_machine_event_finalize_callback_t NULLABLE finalize_callback);
void state_machine_event_deliver(state_machine_header_t *NONNULL state_header, state_machine_event_t *NONNULL event);
bool state_machine_header_setup(state_machine_header_t *NONNULL state_header, void *NONNULL state_object, const char *NULLABLE name,
state_machine_type_t type, state_machine_decl_t *NONNULL states, size_t num_states);
#endif // __STATE_MACHINE_H__
// Local Variables:
// mode: C
// tab-width: 4
// c-file-style: "bsd"
// c-basic-offset: 4
// fill-column: 120
// indent-tabs-mode: nil
// End: