-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Description
Problem Description
I frequently need to implement common patterns for managing the lifecycle (e.g., starting, stopping, suspending) of distinct software components or modules. Currently, this is handled through ad-hoc, application-specific mechanisms, often involving custom message queues or direct function calls.
This leads to duplicated effort across different applications and a lack of a standardized, reusable approach within the Zephyr Project. Similar to how Zephyr provides a Power Management subsystem for hardware devices, a comparable framework for managing the state of software components would provide significant value.
Proposed Change (Summary)
The proposal is to introduce a new, optional subsystem called the Services API. It would provide a lightweight, iterable (via iterable sections) and configurable framework for defining self-contained software modules, referred to as "services".
Initially, it should be sufficient to provide:
- A
SERVICE_DEFINE(...)macro, analogous toSYS_INIT(...), for declaring a new service. - A standardized API to set and get the state of any services.
- A method for a service to expose a custom, service-specific API.
Alongside this, an optional Supervisor component can be included. It could be set to automatically start any services that are configured for autostart during the system's boot sequence.
In the future, the subsystem could be expanded with more features, including (in no specific order):
- Dependency Management.
- Health Monitoring.
- Resource Monitoring (e.g. Stack / CPU usage).
- State Notifications.
- Shell Integration.
PS. I'm not great at naming software components, so if you have a good suggestion - please share!
Proposed Change (Detailed)
Here is a very rough mock-up of what the first iteration of this could look like:
/* SPDX-License-Identifier: Apache-2.0 */
#ifndef SERVICES_H_
#define SERVICES_H_
#include <zephyr/kernel.h>
#include <zephyr/linker/sections.h>
struct service;
/* Standardized service states and requests */
typedef enum {
SERVICE_STATE_STOPPED,
SERVICE_STATE_RUNNING,
SERVICE_STATE_SUSPENDED,
SERVICE_STATE_ERROR,
} service_state_t;
typedef enum {
SERVICE_REQUEST_START,
SERVICE_REQUEST_STOP,
SERVICE_REQUEST_SUSPEND,
} service_request_t;
/* The common API that every service must implement */
struct service_api {
int (*set_state)(const struct service *svc, service_request_t request);
service_state_t (*get_state)(const struct service *svc);
const void *(*get_api)(const struct service *svc);
};
/* The common configuration for all services */
struct service_config {
bool autostart;
};
/* The main registration struct for a service */
struct service {
const char *name;
const struct service_api *api;
const struct service_config *config;
};
/**
* @brief Defines and registers a new service.
*
* @param _handle A unique C identifier for this service instance.
* @param _name_str The service's unique name string (from Kconfig).
* @param _api_ptr Pointer to the service_api struct.
* @param _config_ptr Pointer to the service_config struct.
*/
#define SERVICE_DEFINE(_handle, _name_str, _api_ptr, _config_ptr) \
static const STRUCT_SECTION_ITERABLE(service, _service_##_handle) = { \
.name = _name_str, \
.api = _api_ptr, \
.config = _config_ptr, \
}
/**
* @brief Retrieves a service instance by its string name.
*/
const struct service *services_get_by_name(const char *name);
/**
* @brief Callback for iterating over services. Return true to continue.
*/
typedef bool (*service_visitor_t)(const struct service *svc, void *user_data);
/**
* @brief Iterates over all registered services.
*/
void services_foreach(service_visitor_t visitor, void *user_data);
#endif /* SERVICES_H_ */Example Kconfig.template.service:
# SPDX-License-Identifier: Apache-2.0
config $(module)_NAME
string "Service Name"
default "$(module | tolower)"
help
Specifies the unique string name for this service.
config $(module)_AUTOSTART
bool "Autostart $(module-str) service at boot"
default y
help
If enabled, the supervisor will automatically start this
service during system initialization.
Example service Kconfig:
# SPDX-License-Identifier: Apache-2.0
config SERVICE_MQTT
bool "Enable MQTT Service"
depends on SERVICES
help
Enable this example MQTT service.
if SERVICE_MQTT
module = SERVICE_MQTT
module-str = "MQTT Service"
source "subsys/services/Kconfig.template.service"
source "subsys/logging/Kconfig.template.log_config"
endif # SERVICE_MQTT
We could also provide a default service supervisor. E.g.:
#if defined(CONFIG_SERVICES_SUPERVISOR_AUTOSTART)
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(supervisor, CONFIG_SERVICES_SUPERVISOR_LOG_LEVEL);
/**
* @brief Visitor function to autostart a single service.
*/
static bool autostart_visitor(const struct service *svc, void *user_data)
{
if (svc->config && svc->config->autostart) {
LOG_INF("Autostarting service: %s", svc->name);
int err = svc->api->set_state(svc, SERVICE_REQUEST_START);
if (err) {
LOG_ERR("Failed to request start for service '%s' (err %d)",
svc->name, err);
}
}
/* Return true to continue iterating over other services */
return true;
}
/**
* @brief The SYS_INIT function for the default supervisor.
*/
static int services_supervisor_init(void)
{
LOG_INF("Default services supervisor initializing...");
services_foreach(autostart_visitor, NULL);
return 0;
}
SYS_INIT(services_supervisor_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
#endif /* CONFIG_SERVICES_SUPERVISOR_AUTOSTART */Dependencies
This proposal shouldn't interfere with any existing components.
Concerns and Unresolved Questions
Without having a global enum with a service list, I currently rely on a configured-name (see _name_str below) to identify a service. Ideally, this could be some global unique number-based identifier, but I am not sure how to implement this.
#define SERVICE_DEFINE(_handle, _name_str, _api_ptr, _config_ptr) \
static const STRUCT_SECTION_ITERABLE(service, _service_##_handle) = { \
.name = _name_str, \
.api = _api_ptr, \
.config = _config_ptr, \
}Alternatives Considered
I tried browsing through the Discord/Issues list, but I was not able to find anything similar discussed previously.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status