Единый пакетный интерфейс транспорта ================================== Публичный контракт: lib/packet_link.h. Интерфейс реализации: lib/packet_link_backend.h. Привязка ETCP к UDP/STCP: src/transport_layer/etcp_link_io.h/c. Все операции и callbacks выполняются в одном uasync-потоке. Владение и события ----------------- create копирует ops, callbacks и начальное состояние; arg остаётся заимствованным. При успехе оболочка владеет impl; при отказе impl освобождает вызывающий. Первое уведомление состояния отложено; RX первоначально выключен. После установки своего владельца вызвать packet_link_set_rx_enabled(link, 1). send принимает целый ll_entry, не состоящий в очереди. OK передаёт владение entry и dgram модулю. Любой отказ сохраняет владение вызывающему. Частичного принятия нет. Успех означает принятие ответственности, а не доставку удалённому приложению. Ошибка после принятия сообщается состоянием TERMINATED. Подтверждения ETCP остаются. tx_ready означает свободное место в данном модуле для пакета до max_packet. Адаптер со своей очередью считает только её заполнение. Если своей очереди нет, он использует публичную готовность следующего модуля. Чужие внутренние буферы не инспектируются и не суммируются. Проверка готовности не заменяет результат send. Из send верхние callbacks не вызываются. Состояние меняется немедленно, state_changed доставляется через call_soon и объединяет накопленные изменения. Обработчик читает актуальный get_state; он может отправлять и закрывать линк. backend_receive передаёт владение получателю только при OK. При отключённом RX, CONNECTING или активном send возвращает WOULD_BLOCK, не забирая пакет. Оболочка очередей данных не создаёт. Каждый backend сам удерживает свой лимит RX. close сразу отменяет уведомления и вызывает stop реализации. destroy выполняется после выхода активных callbacks, через call_soon. Это защищает также стек backend, из которого вызван backend_receive. После close handle недействителен; новых событий от реализации быть не должно. До уничтожения uasync освободить все линки и выполнить его штатный drain. TERMINATED необратим; OPEN может перейти в CONNECTING у адаптера пути, который сохраняет владельца при native-реконнекте. ETCP ---- ETCP_LINK владеет etcp_link_io. Общий packet_link из get заимствован: отдельно не закрывать, его освобождает etcp_link_close до native-ресурсов. UDP передаёт существующие зашифрованные датаграммы, STCP — существующие сообщения в его потоковое шифрование/framing. Wire-форматы, INIT, session handshake, SOCKS, REALITY и правила реконнекта сохранены. Неизвестный UDP endpoint проходит существующий bootstrap до создания ETCP_LINK; последующие пакеты привязанного пути проходят общий receive callback. UDP сначала пробует sendto. EAGAIN сохраняет одну датаграмму в очереди данного адаптера и включает writable общего сокета. При SOCKS, ещё не готовом к передаче, адаптер ждёт ready_cb relay, не крутит EPOLLOUT. writable обслуживает ожидающие датаграммы путей сокета. Ошибка досылки закрывает только соответствующий путь. STCP-адаптер разрешает один ожидающий пакет в своей TX-очереди. Модуль STCP извлекает следующий только после освобождения собственной досылки. Освобождение очереди адаптера уведомляет ETCP через packet_link. Входная очередь STCP сохраняет свои прежние пороги и приостанавливает чтение TCP; отключение RX останавливает её потребление. У ETCP-адаптера RX ограничен 64 пакетами, обычный приём синхронный. Выбор DATA проверяет готовность packet_link вместе с ETCP-окном и pacing. ACK и измерительные серии также проверяют транспортную готовность. ACK, служебные признаки и номер пакета измерительной серии фиксируются только после принятого send. При WOULD_BLOCK остаются исходные очереди и признаки; новый проход отправителя вызывает событие готовности, не периодический polling. Проверки -------- test_packet_link: владение TX/RX, oversized, blocked/resume, объединённые события, RX pause, отправка из state callback, закрытие из state/receive, отмена событий, освобождение всех учитываемых выделений. test_etcp_packet_link: настоящий UDP-адаптер с управляемым send hook, EAGAIN, два пути общего сокета, выбор доступного пути, запрет обхода занятости burst, ACK commit/rollback, ошибка досылки и независимое закрытие. Существующие test_transport/test_etcp_transport, test_etcp_windows, test_etcp_session/test_etcp_multipath и сетевые интеграционные тесты сохраняют прежние ожидания. В синтетических fixtures освобождается добавленный I/O handle. Диагностика: sys=debug для состояния packet_link, etcp=debug для привязки/ETCP.