Document instance ownership, lifecycle, reload and callback contracts

This commit is contained in:
evgeny committed 2026-10-26 09:08:42 +01:00
1 parent f5488adab0
commit 0d14613244
1 file changed
+59 -36
+59 -36
View File
@@ -1,8 +1,9 @@
/* utun_instance — владелец ядра и сервисов одного узла.
* Ядро содержит транспорт, маршрутизатор, идентичность и общую БД.
* create → core_start; UTUN и чат запускаются отдельно через service_start/stop.
* Все операции жизненного цикла — в потоке переданного uasync, вне callbacks сервисов.
* destroy останавливает сервисы и освобождает ядро, но не сам uasync. */
* Все операции — в потоке переданного UASYNC; stop/reload/destroy вне callbacks останавливаемых модулей.
* Цикл uasync_poll запускает вызывающий; один UASYNC можно разделять между несколькими instance.
* destroy останавливает сервисы и освобождает ядро, но не UASYNC. Подробности: utun_instance_doc.md. */
#ifndef UTUN_INSTANCE_H
#define UTUN_INSTANCE_H
@@ -34,8 +35,6 @@ extern "C" {
#include "ntp_time.h"
#include "ntp_node_time.h"
#include "etcp_api.h"
#include "config_parser.h"
#include "topo_group.h"
#include "chat/chat_setting.h"
#include "chat/chat_event.h"
@@ -77,16 +76,16 @@ struct STUN_NAT;
struct merkle_sync;
struct NETWORK_ENTRY {
uint64_t id; // 56-bit (offset 0 = index key)
uint8_t pubkey[SC_PUBKEY_SIZE]; // 32 bytes binary
uint8_t signing_key[SC_PRIVKEY_SIZE]; // 32 bytes binary
char name[64];
uint64_t id; // 56-битный ID сети; первые 7 байт — ключ индекса
uint8_t pubkey[SC_PUBKEY_SIZE]; // бинарный публичный ключ сети
uint8_t signing_key[SC_PRIVKEY_SIZE]; // бинарный ключ подписи сети
char name[64]; // имя [network:name], ASCIIZ
};
// Queue entry for instance->connections (indexed by peer_node_id)
struct conn_queue_entry {
uint64_t peer_node_id;
struct ETCP_CONN* conn;
uint64_t peer_node_id; // ключ индекса, 0 до определения пира
struct ETCP_CONN* conn; // живой транспорт; удаляется при close
};
/* Постоянный транспорт ядра к пиру [client]; UTUN приобретает отдельный групповой запрос.
@@ -112,9 +111,9 @@ struct tcp_conn_entry {
// подписчикам (topo_group, keepalive и т.д.) при каждом изменении активности.
typedef void (*utun_activity_cbk_fn)(struct UTUN_INSTANCE* instance, int active, void* arg);
struct utun_activity_cbk_entry {
utun_activity_cbk_fn fn;
void* arg;
struct utun_activity_cbk_entry* next;
utun_activity_cbk_fn fn; // синхронный обработчик эффективной активности
void* arg; // заимствованный контекст обработчика
struct utun_activity_cbk_entry* next; // следующая подписка
uint8_t removed; // tombstone до выхода из внешней рассылки
};
@@ -123,16 +122,16 @@ struct utun_activity_cbk_entry {
// sleeping=1 — пир ушёл в спячку (в фон), 0 — проснулся (дедлайн истёк / обычный keepalive).
typedef void (*peer_sleep_cbk_fn)(struct UTUN_INSTANCE* instance, uint64_t peer_node_id, int sleeping, void* arg);
struct peer_sleep_cbk_entry {
peer_sleep_cbk_fn fn;
void* arg;
struct peer_sleep_cbk_entry* next;
peer_sleep_cbk_fn fn; // синхронный обработчик sleep/wake пира
void* arg; // заимствованный контекст обработчика
struct peer_sleep_cbk_entry* next; // следующая подписка
uint8_t removed; // снятая подписка не вызывается, память удерживает текущая рассылка
};
// Состояние одного узла. Вложенные модули освобождаются через их lifecycle API.
struct UTUN_INSTANCE {
struct etcp_tx_history tx_history;
struct reality_owner reality_owner;
struct etcp_tx_history tx_history; // секундное окно ACK всех транспортов ядра
struct reality_owner reality_owner; // relay и replay-кеш REALITY данного ядра
uint8_t core_started, utun_started, chat_started; // успешный запуск ядра / UTUN / чата
uint8_t destroying; // запрещает повторный запуск ядра/повторное destroy во время очистки
@@ -150,7 +149,7 @@ struct UTUN_INSTANCE {
struct ROUTE_TABLE* rt; // таблица маршрутов подсетей
struct TOPO_GROUPS* topo_groups; // Groups module for topology exchange
sqlite3* topo_sqlite_db; // Shared SQLite DB (nodes/channels/peers)
sqlite3* topo_sqlite_db; // собственная общая SQLite; открывается при create с топологией
struct NAT_DETECTION* nat_det; // NAT detection module
struct STUN_NAT* stun_nat; // независимая локальная STUN-диагностика UDP
struct stun_ports* stun_port_history; // ранее использованные STUN-порты; переживает reload config
@@ -164,17 +163,17 @@ struct UTUN_INSTANCE {
uint8_t my_ed25519_pubkey[SC_PUBKEY_SIZE]; // свой Ed25519 pubkey (для подписи метрик)
uint8_t my_ed25519_privkey[SC_PRIVKEY_SIZE]; // свой Ed25519 privkey (derived from X25519)
uint8_t next_socket_id; // Counter for unique socket IDs (0-255)
uint8_t next_socket_id; // начало поиска свободного ID 0..255; поколение различает повторное использование
// Заимствованный цикл событий: создаёт и уничтожает вызывающий.
struct UASYNC* ua;
// State
int running; // продолжать utun_instance_run; не флаг готовности сервисов
int running; // продолжать цикл приложения; готовность задают отдельные *_started
// Connections (очередь всех подключений для instance)
struct ll_queue* connections; // indexed by peer_node_id (0=pending), data=conn_queue_entry
struct ll_queue* tcp_connections; // indexed by node_id, data=tcp_conn_entry [TCP-only]
struct ll_queue* tcp_connections; // старый TCP-реестр tcp_conn_entry, fallback instance_find_conn; обычный STCP живёт в links
// Callback chain for new ETCP connections
struct etcp_inst_cbk_entry* new_conn_cbks;
@@ -224,7 +223,7 @@ struct UTUN_INSTANCE {
// Media async engine (thread-per-task for crypto/file ops)
struct media_async* media_async;
// Socket initialization status: 0=OK, 1=partial (some sockets failed), -1=error (none created)
// Последний результат init/reload endpoint: 0=успех, -1=ошибка хотя бы одного обязательного endpoint.
int socket_init_status;
// TCP proxy client (optional, NULL if not enabled)
@@ -301,47 +300,71 @@ struct UTUN_INSTANCE {
uint8_t ncd_control_bound; // 1 = etcp_bind(ETCP_ID_NCD_CONTROL) сделан
};
// Создать ядро по файлу конфигурации; NULL при ошибке. Сервисы ещё не запущены.
/* Создать ядро: ua заимствован, config_file — обязательный путь. Файл может создаваться/нормализоваться updater.
* Уже открывает listeners, SQLite/топологию, router/proxy и мониторинг; core/UTUN/chat ещё не запущены.
* Возвращает instance либо NULL с освобождением частично созданных ресурсов. */
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file);
// При успехе забирает config; при ошибке config остаётся у вызывающего.
/* Как create, но без updater и файлового parse: config должен содержать корректные ключи.
* Успех передаёт владение config ядру; при NULL config остаётся caller (node_id мог быть исправлен).
* log_file и debug меняют общий logger процесса даже при последующем отказе создания. */
struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config);
// Как create, но конфигурация передана строкой; строку не сохраняет.
/* Как create из ASCIIZ config_text; строка заимствована лишь на вызов. Использует mkstemp в /tmp,
* updater и файловый parse, затем unlink. Нужна доступная ФС; ошибки дают NULL. */
struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text);
// Остановить сервисы и освободить instance. Переданный uasync остаётся у вызывающего.
/* Остановить сервисы/транспорт и освободить instance/config; NULL допустим. UASYNC остаётся caller.
* Выполняет drain отложенных callbacks и join media workers; вызывать вне callbacks и без параллельных обращений.
* Заимствованные SQLite statements/backup должны быть закрыты caller; BUSY логируется и переводится в close_v2. */
void utun_instance_destroy(struct UTUN_INSTANCE* instance);
/* Запустить общие сетевые службы и постоянные транспорты [client], без членства в группах. Повтор допустим; 0/-1. */
/* Запустить репликацию, NCD/keepalive, [client], control и время; без вступления в группы. Успех: running=core_started=1.
* Повтор успешного start — 0. Ошибка — -1; уже подготовленные ресурсы остаются для retry/destroy.
* Отказ NTP/STUN не фатален. NULL/разрушаемый instance отклоняется. */
int utun_core_start(struct UTUN_INSTANCE* instance);
/* Запустить UTUN-группу, TUN/NAT по конфигу и групповые запросы к настроенным пирам. Нужен core_start; 0/-1. */
/* Запустить UTUN-группу, TUN/NAT по конфигу и отдельные запросы к [client]. Нужны core_start и топология.
* 0 — успех/уже запущен; -1 — откат ресурсов UTUN, ядро и чат сохраняются. */
int utun_service_start(struct UTUN_INSTANCE* instance);
/* Остановить UTUN, сохранив ядро и чат. Повтор допустим. */
void utun_service_stop(struct UTUN_INSTANCE* instance);
/* Запустить ядро, UTUN и включённый в конфиге чат. 0/-1. */
/* Последовательно core_start → UTUN (при наличии топологии) → чат (chatserver_enabled).
* 0/-1; ошибка позднего сервиса сохраняет ранее запущенные сервисы. */
int utun_instance_init(struct UTUN_INSTANCE *instance);
/* Перечитать конфигурацию, при необходимости пересоздать экземпляр; использовать возвращённый указатель. */
/* Перечитать config_file через updater; ua должен совпадать с instance->ua. Всегда использовать результат.
* Ошибка подготовки сохраняет прежний указатель. Смена негорячих global/подсетей либо automatic servers
* пересоздаёт ядро с прежними core/UTUN/chat/running; ошибка после destroy возвращает NULL.
* Ручные endpoints обновляются по имени и всем параметрам, неизменённые сохраняются (включая port=0).
* Частичный reload не транзакционный: отказ endpoint логируется, новый config сохраняется для следующего retry. */
struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struct UASYNC *ua, const char *config_file);
/* Выполнять цикл событий до stop(). */
void utun_instance_run(struct UTUN_INSTANCE *instance);
/* Снять running и разбудить цикл; ресурсы освобождает destroy(), а не stop(). */
void utun_instance_stop(struct UTUN_INSTANCE *instance);
/* Глобальные переключатели создания TUN/топологии для тестов и встраивания. */
/* Общий переключатель TUN (по умолчанию 0): влияет на последующий UTUN start и proxy test mode при create. */
void utun_instance_set_tun_init_enabled(int enabled);
/* Общий переключатель топологии (по умолчанию 1): влияет на последующие create, включая открытие SQLite. */
void utun_instance_set_topo_group_enabled(int enabled);
/* Сохраняет желаемый режим frontend; stream owners удерживают ACTIVE до последнего close.
* Применяет эффективную активность и мобильный standby до уведомления подписчиков; выполнять в uasync-потоке.
* Повторная команда согласует фактический режим без повторного BGP-события. 0 — успех, <0 — ошибка. */
* Повторная команда согласует фактический режим без повторного BGP-события. SERVER — успешный no-op.
* 0 — успех, -1 — NULL/ошибка standby с откатом режима. phone_state не меняется. */
int utun_set_client_activity(struct UTUN_INSTANCE* instance, int active);
/* Подписки уникальны по fn+arg, новые идут в голову; arg заимствован. NULL instance/fn — no-op,
* OOM логируется без добавления. Remove из callback безопасен; память удерживается до конца внешней рассылки.
* Новая подписка не получает уже начатое событие, но участвует во вложенном. Destroy внутри callback запрещён. */
void utun_add_activity_cbk(struct UTUN_INSTANCE* instance, utun_activity_cbk_fn fn, void* arg);
/* Удалить подписку fn+arg; отсутствие — no-op. Вложенная новая активность отменяет остаток старого события. */
void utun_remove_activity_cbk(struct UTUN_INSTANCE* instance, utun_activity_cbk_fn fn, void* arg);
/* Аналогичная подписка на peer_node_id/sleeping; nesting исполняется синхронно до продолжения внешней рассылки. */
void utun_add_peer_sleep_cbk(struct UTUN_INSTANCE* instance, peer_sleep_cbk_fn fn, void* arg);
/* Удалить peer-sleep подписку fn+arg; отсутствие/NULL — no-op. */
void utun_remove_peer_sleep_cbk(struct UTUN_INSTANCE* instance, peer_sleep_cbk_fn fn, void* arg);
/* Синхронно передать подписчикам node_id и sleeping (1 сон, 0 пробуждение); NULL instance — no-op. */
void utun_fire_peer_sleep_cbk(struct UTUN_INSTANCE* instance, uint64_t peer_node_id, int sleeping);
// Diagnostic function for memory leak analysis
/* Вывести sys/info счётчики ресурсов и простые sanity-проверки списков с меткой phase.
* Живые ресурсы сами по себе не означают утечку; функция ничего не освобождает. */
void utun_instance_diagnose_leaks(struct UTUN_INSTANCE* instance, const char* phase);
/* Найти транспорт по node_id в реестрах ETCP/TCP. Указатель заимствован.
* NULL — не найден; ненулевой результат не гарантирует UP или готовность группы. */
static inline struct ETCP_CONN* instance_find_conn(struct UTUN_INSTANCE* inst, uint64_t node_id) {
if (!inst) return NULL;
if (inst->connections) {
struct ll_entry* e = queue_find_data_by_index(inst->connections, (const uint8_t*)&node_id);
if (e) return ((struct conn_queue_entry*)e->data)->conn;