Service logic moved to kernel (#554)

+ fix BT service file paths
This commit is contained in:
Ken Van Hoeylandt
2026-07-07 22:52:19 +02:00
committed by GitHub
parent cca7224252
commit f4f91f81d6
24 changed files with 1101 additions and 196 deletions
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/service/service_instance.h>
#include <tactility/concurrent/mutex.h>
#include <tactility/log.h>
#include <new>
#define TAG "service_instance"
struct ServiceInstanceInternal {
Mutex mutex {};
ServiceState state = SERVICE_STATE_STOPPED;
};
extern "C" {
error_t service_instance_construct(ServiceInstance* instance, const ServiceManifest* manifest) {
instance->internal = new(std::nothrow) ServiceInstanceInternal;
if (instance->internal == nullptr) {
return ERROR_OUT_OF_MEMORY;
}
mutex_construct(&instance->internal->mutex);
instance->manifest = manifest;
instance->data = manifest->create_service(manifest);
LOG_D(TAG, "construct %s", manifest->id);
return ERROR_NONE;
}
error_t service_instance_destruct(ServiceInstance* instance) {
auto* internal = instance->internal;
mutex_lock(&internal->mutex);
if (internal->state != SERVICE_STATE_STOPPED) {
mutex_unlock(&internal->mutex);
return ERROR_INVALID_STATE;
}
mutex_unlock(&internal->mutex);
LOG_D(TAG, "destruct %s", instance->manifest->id);
instance->manifest->destroy_service(instance->manifest, instance->data);
instance->data = nullptr;
instance->internal = nullptr;
mutex_destruct(&internal->mutex);
delete internal;
return ERROR_NONE;
}
const ServiceManifest* service_instance_get_manifest(ServiceInstance* instance) {
return instance->manifest;
}
void* service_instance_get_data(ServiceInstance* instance) {
return instance->data;
}
ServiceState service_instance_get_state(ServiceInstance* instance) {
mutex_lock(&instance->internal->mutex);
ServiceState state = instance->internal->state;
mutex_unlock(&instance->internal->mutex);
return state;
}
/** Internal-only: mutate the state of a service instance. Used by service_registration.cpp. */
void service_instance_set_state(ServiceInstance* instance, ServiceState state) {
mutex_lock(&instance->internal->mutex);
instance->internal->state = state;
mutex_unlock(&instance->internal->mutex);
}
} // extern "C"
@@ -0,0 +1,200 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/service/service_manager.h>
#include <tactility/concurrent/mutex.h>
#include <tactility/log.h>
#include <new>
#include <string>
#include <unordered_map>
#define TAG "service_registration"
// Defined in service_instance.cpp. Internal-only: lets the registry drive state
// transitions without exposing mutation on the public ServiceInstance API.
extern "C" void service_instance_set_state(ServiceInstance* instance, ServiceState state);
struct ManifestLedger {
std::unordered_map<std::string, const ServiceManifest*> manifests;
Mutex mutex {};
ManifestLedger() { mutex_construct(&mutex); }
~ManifestLedger() { mutex_destruct(&mutex); }
};
struct InstanceLedger {
std::unordered_map<std::string, ServiceInstance*> instances;
Mutex mutex {};
InstanceLedger() { mutex_construct(&mutex); }
~InstanceLedger() { mutex_destruct(&mutex); }
};
static ManifestLedger& get_manifest_ledger() {
static ManifestLedger ledger;
return ledger;
}
static InstanceLedger& get_instance_ledger() {
static InstanceLedger ledger;
return ledger;
}
#define manifest_ledger get_manifest_ledger()
#define instance_ledger get_instance_ledger()
extern "C" {
error_t service_manager_add(const ServiceManifest* manifest, bool auto_start) {
mutex_lock(&manifest_ledger.mutex);
if (manifest_ledger.manifests.contains(manifest->id)) {
mutex_unlock(&manifest_ledger.mutex);
LOG_E(TAG, "Manifest with id '%s' is already registered", manifest->id);
return ERROR_INVALID_ARGUMENT;
}
manifest_ledger.manifests[manifest->id] = manifest;
mutex_unlock(&manifest_ledger.mutex);
LOG_I(TAG, "add %s", manifest->id);
if (auto_start) {
return service_manager_start(manifest->id);
}
return ERROR_NONE;
}
error_t service_manager_remove(const char* id) {
if (service_manager_get_state(id) != SERVICE_STATE_STOPPED) {
return ERROR_INVALID_STATE;
}
mutex_lock(&manifest_ledger.mutex);
const auto iterator = manifest_ledger.manifests.find(id);
if (iterator == manifest_ledger.manifests.end()) {
mutex_unlock(&manifest_ledger.mutex);
return ERROR_NOT_FOUND;
}
manifest_ledger.manifests.erase(iterator);
mutex_unlock(&manifest_ledger.mutex);
LOG_I(TAG, "remove %s", id);
return ERROR_NONE;
}
error_t service_manager_start(const char* id) {
mutex_lock(&manifest_ledger.mutex);
const auto manifest_iterator = manifest_ledger.manifests.find(id);
if (manifest_iterator == manifest_ledger.manifests.end()) {
mutex_unlock(&manifest_ledger.mutex);
return ERROR_NOT_FOUND;
}
const ServiceManifest* manifest = manifest_iterator->second;
mutex_unlock(&manifest_ledger.mutex);
mutex_lock(&instance_ledger.mutex);
if (instance_ledger.instances.contains(id)) {
mutex_unlock(&instance_ledger.mutex);
return ERROR_INVALID_STATE;
}
auto* instance = new(std::nothrow) ServiceInstance { .manifest = nullptr, .data = nullptr, .internal = nullptr };
if (instance == nullptr) {
mutex_unlock(&instance_ledger.mutex);
return ERROR_OUT_OF_MEMORY;
}
error_t error = service_instance_construct(instance, manifest);
if (error != ERROR_NONE) {
mutex_unlock(&instance_ledger.mutex);
delete instance;
return error;
}
// Register before on_start() so a service can find itself while starting.
instance_ledger.instances[id] = instance;
mutex_unlock(&instance_ledger.mutex);
service_instance_set_state(instance, SERVICE_STATE_STARTING);
LOG_I(TAG, "start %s", id);
error = (manifest->on_start != nullptr) ? manifest->on_start(instance, instance->data) : ERROR_NONE;
if (error == ERROR_NONE) {
service_instance_set_state(instance, SERVICE_STATE_STARTED);
return ERROR_NONE;
}
LOG_E(TAG, "Failed to start %s: %s", id, error_to_string(error));
service_instance_set_state(instance, SERVICE_STATE_STOPPED);
mutex_lock(&instance_ledger.mutex);
instance_ledger.instances.erase(id);
mutex_unlock(&instance_ledger.mutex);
service_instance_destruct(instance);
delete instance;
return ERROR_RESOURCE;
}
error_t service_manager_stop(const char* id) {
mutex_lock(&instance_ledger.mutex);
const auto iterator = instance_ledger.instances.find(id);
if (iterator == instance_ledger.instances.end()) {
mutex_unlock(&instance_ledger.mutex);
return ERROR_NOT_FOUND;
}
ServiceInstance* instance = iterator->second;
mutex_unlock(&instance_ledger.mutex);
LOG_I(TAG, "stop %s", id);
service_instance_set_state(instance, SERVICE_STATE_STOPPING);
if (instance->manifest->on_stop != nullptr) {
instance->manifest->on_stop(instance, instance->data);
}
service_instance_set_state(instance, SERVICE_STATE_STOPPED);
mutex_lock(&instance_ledger.mutex);
instance_ledger.instances.erase(id);
mutex_unlock(&instance_ledger.mutex);
service_instance_destruct(instance);
delete instance;
return ERROR_NONE;
}
ServiceState service_manager_get_state(const char* id) {
mutex_lock(&instance_ledger.mutex);
const auto iterator = instance_ledger.instances.find(id);
if (iterator == instance_ledger.instances.end()) {
mutex_unlock(&instance_ledger.mutex);
return SERVICE_STATE_STOPPED;
}
ServiceInstance* instance = iterator->second;
mutex_unlock(&instance_ledger.mutex);
return service_instance_get_state(instance);
}
const ServiceManifest* service_manager_find_manifest(const char* id) {
mutex_lock(&manifest_ledger.mutex);
const auto iterator = manifest_ledger.manifests.find(id);
const ServiceManifest* manifest = (iterator != manifest_ledger.manifests.end()) ? iterator->second : nullptr;
mutex_unlock(&manifest_ledger.mutex);
return manifest;
}
ServiceInstance* service_manager_find_instance(const char* id) {
mutex_lock(&instance_ledger.mutex);
const auto iterator = instance_ledger.instances.find(id);
auto* instance = (iterator != instance_ledger.instances.end()) ? iterator->second : nullptr;
mutex_unlock(&instance_ledger.mutex);
return instance;
}
} // extern "C"
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
#include <tactility/service/service_paths.h>
#include <tactility/paths.h>
#include <cstdio>
extern "C" {
error_t service_paths_get_user_data_directory(const char* service_id, char* out_path, size_t out_path_size) {
char root[192];
error_t error = paths_get_user_data_path(root, sizeof(root));
if (error != ERROR_NONE) {
return error;
}
int written = std::snprintf(out_path, out_path_size, "%s/service/%s", root, service_id);
if (written < 0 || (size_t)written >= out_path_size) {
return ERROR_BUFFER_OVERFLOW;
}
return ERROR_NONE;
}
error_t service_paths_get_user_data_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size) {
char directory[224];
error_t error = service_paths_get_user_data_directory(service_id, directory, sizeof(directory));
if (error != ERROR_NONE) {
return error;
}
int written = std::snprintf(out_path, out_path_size, "%s/%s", directory, child_path);
if (written < 0 || (size_t)written >= out_path_size) {
return ERROR_BUFFER_OVERFLOW;
}
return ERROR_NONE;
}
error_t service_paths_get_assets_directory(const char* service_id, char* out_path, size_t out_path_size) {
char directory[224];
error_t error = service_paths_get_user_data_directory(service_id, directory, sizeof(directory));
if (error != ERROR_NONE) {
return error;
}
int written = std::snprintf(out_path, out_path_size, "%s/assets", directory);
if (written < 0 || (size_t)written >= out_path_size) {
return ERROR_BUFFER_OVERFLOW;
}
return ERROR_NONE;
}
error_t service_paths_get_assets_path(const char* service_id, const char* child_path, char* out_path, size_t out_path_size) {
char directory[224];
error_t error = service_paths_get_assets_directory(service_id, directory, sizeof(directory));
if (error != ERROR_NONE) {
return error;
}
int written = std::snprintf(out_path, out_path_size, "%s/%s", directory, child_path);
if (written < 0 || (size_t)written >= out_path_size) {
return ERROR_BUFFER_OVERFLOW;
}
return ERROR_NONE;
}
} // extern "C"