chat: invite-ссылки с выбором достижимого узла + tcp_io фикс half-close FIN

- Общий модуль invite_build: сборка utun://-ссылок, авто-выбор лучшего
  достижимого узла, список кандидатов (вынесено из android jni_bridge)
- chatgui: диалог invite с выбором узла (комбобокс), события INVITE_CANDIDATES /
  INVITE_LINK_READY через gui_bridge
- headless/chatcli: invite <ch_id> [node_id], команда invite_nodes
- tcp_io: error_cb отличает грациозный EPOLLHUP (SO_ERROR==0) от реальной ошибки —
  при приостановленном чтении FIN не роняет недослейные данные (фикс flaky half-close)
- run_test.sh: half-close echo на выделенном порту 19190 + отдельный DNAT (был недостижим)
This commit is contained in:
evgeny committed 2026-09-07 13:59:15 +03:00
1 parent a17a85a383
commit 1345f44e10
23 files changed
+872 -539

No files matched your search

+12 -9
View File
@@ -32,10 +32,19 @@ bool SoundManager::init() {
return true;
}
void SoundManager::clearCurrentPcm() {
if (m_currentPcmSound) { ma_sound_uninit(m_currentPcmSound); delete m_currentPcmSound; m_currentPcmSound = nullptr; }
if (m_currentPcmBuffer) {
ma_audio_buffer* buf = (ma_audio_buffer*)m_currentPcmBuffer;
ma_audio_buffer_uninit(buf); delete buf; m_currentPcmBuffer = nullptr;
}
m_currentPcmSampleRate = 0;
}
void SoundManager::shutdown() {
if (!m_initialized) return;
stopCurrentPcm();
clearCurrentPcm();
for (auto it = m_sounds.begin(); it != m_sounds.end(); ++it) {
if (it->sound) { ma_sound_uninit(it->sound); delete it->sound; }
@@ -45,11 +54,6 @@ void SoundManager::shutdown() {
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "SoundManager: before ma_engine_uninit engine=%p", (void*)m_engine);
if (m_engine) { ma_engine_uninit(m_engine); delete m_engine; m_engine = nullptr; }
if (m_currentPcmSound) { delete m_currentPcmSound; m_currentPcmSound = nullptr; }
if (m_currentPcmBuffer) {
ma_audio_buffer* buf = (ma_audio_buffer*)m_currentPcmBuffer;
ma_audio_buffer_uninit(buf); delete buf; m_currentPcmBuffer = nullptr;
}
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "SoundManager: after ma_engine_uninit");
m_initialized = false;
}
@@ -152,7 +156,7 @@ void SoundManager::playRawPcm(const int16_t* pcm, int sampleCount, int sampleRat
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "SoundManager::playRawPcm: not initialized");
return;
}
if (m_currentPcmSound) ma_sound_stop(m_currentPcmSound);
clearCurrentPcm();
m_currentPcmSampleRate = sampleRate;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "SoundManager::playRawPcm: %d samples %dHz %dch", sampleCount, sampleRate, channels);
@@ -189,8 +193,7 @@ void SoundManager::resumeCurrentPcm() {
}
void SoundManager::stopCurrentPcm() {
if (m_currentPcmSound) ma_sound_stop(m_currentPcmSound);
m_currentPcmSampleRate = 0;
clearCurrentPcm();
}
unsigned long long SoundManager::currentPcmCursor() const {