mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-10-19 20:05:06 +00:00
- The `/system` FAT volume is removed. Its files are embedded in the firmware as generated C arrays. - Default-size fonts are generated at build time and used from flash. - The LVGL task and the Launcher run on PSRAM-preferred stacks. A new kernel `file_ops` API takes the file work off those threads. - `MainDispatcher` is reimplemented as an on-demand task. - More long-lived state is allocated with the PSRAM-preferring helpers. - Other fixes and improvements
211 lines
6.7 KiB
C++
211 lines
6.7 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include <service/manager.h>
|
|
|
|
#include <TactilityCpp/Memory.h>
|
|
#include <TactilityCpp/std/string.h>
|
|
#include <TactilityCpp/std/unordered_map.h>
|
|
|
|
#include <tactility/check.h>
|
|
#include <tactility/concurrent/mutex.h>
|
|
#include <tactility/log.h>
|
|
#include <tactility/system_event.h>
|
|
|
|
#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 {
|
|
tt::std::unordered_map<tt::std::string, const ServiceManifest*> manifests;
|
|
Mutex mutex {};
|
|
|
|
ManifestLedger() { mutex_construct(&mutex); }
|
|
~ManifestLedger() { mutex_destruct(&mutex); }
|
|
};
|
|
|
|
struct InstanceLedger {
|
|
tt::std::unordered_map<tt::std::string, ServiceInstance*> instances;
|
|
Mutex mutex {};
|
|
|
|
InstanceLedger() { mutex_construct(&mutex); }
|
|
~InstanceLedger() { mutex_destruct(&mutex); }
|
|
};
|
|
|
|
static ManifestLedger& get_manifest_ledger() {
|
|
static ManifestLedger* ledger = tt::newObject<ManifestLedger>();
|
|
check(ledger != nullptr);
|
|
return *ledger;
|
|
}
|
|
|
|
static InstanceLedger& get_instance_ledger() {
|
|
static InstanceLedger* ledger = tt::newObject<InstanceLedger>();
|
|
check(ledger != nullptr);
|
|
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 = tt::newObject<ServiceInstance>(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);
|
|
tt::deleteObject(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);
|
|
ServiceStartedEvent start_event = { .id = id };
|
|
system_event_emit(KERNEL_EVENT_SERVICE_STARTED, &start_event, sizeof(start_event));
|
|
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);
|
|
tt::deleteObject(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);
|
|
tt::deleteObject(instance);
|
|
|
|
ServiceStoppedEvent stop_event = { .id = id };
|
|
system_event_emit(KERNEL_EVENT_SERVICE_STOPPED, &stop_event, sizeof(stop_event));
|
|
|
|
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"
|