fix(chat): commit radio mute before applying cache and reporting success
This commit is contained in:
1 parent
32a8c1c48b
commit
1bb824fb8a
3 files changed
+49
-23
No files matched your search
@@ -274,14 +274,15 @@ int chat_core_get_single_member(struct UTUN_INSTANCE* inst, const char* ch_id, u
|
||||
|
||||
int chat_core_toggle_member_radio_mute(struct UTUN_INSTANCE* inst, const char* ch_id, uint64_t node_id) {
|
||||
uint8_t member[CHAT_MEMBER_DISPLAY_SIZE];
|
||||
if (!ch_id || !ch_id[0] || strlen(ch_id) >= 64 || chat_core_get_single_member(inst, ch_id, node_id, member) < 0) {
|
||||
if (!chat_channel_id(ch_id) || chat_core_get_single_member(inst, ch_id, node_id, member) < 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio: mute toggle member not found node=%016llx", (unsigned long long)node_id); return -1;
|
||||
}
|
||||
int muted = !(member[10] & CHAT_MEMBER_FLAG_RADIO_MUTED);
|
||||
if (radio_set_member_muted(inst, strtoull(ch_id, NULL, 10), node_id, muted) < 0) return -1;
|
||||
if (chat_core_get_single_member(inst, ch_id, node_id, member) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: cannot publish local mute ch=%s node=%016llx",
|
||||
ch_id, (unsigned long long)node_id); return -1;
|
||||
/* Снимок уже готов: после committed mute нет нового чтения, способного превратить успех в ошибку. */
|
||||
member[10] = (uint8_t)((member[10] & ~CHAT_MEMBER_FLAG_RADIO_MUTED) | (muted ? CHAT_MEMBER_FLAG_RADIO_MUTED : 0));
|
||||
if (!inst->topo_groups || !topo_groups_find(inst->topo_groups, strtoull(ch_id, NULL, 10))) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "radio: mute committed, group removed by audio callback ch=%s", ch_id); return 0;
|
||||
}
|
||||
size_t cl = strlen(ch_id);
|
||||
uint8_t evt[1 + 64 + 1 + CHAT_MEMBER_DISPLAY_SIZE];
|
||||
|
||||
+40
-19
@@ -478,9 +478,10 @@ static int radio_load_muted_members(struct radio_ctx* ctx) {
|
||||
}
|
||||
members = next; members[count++] = (uint64_t)sqlite3_column_int64(st, 0);
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: read muted members: %s", sqlite3_errmsg(db)); u_free(members); return -1;
|
||||
int final = sqlite3_finalize(st);
|
||||
if (rc != SQLITE_DONE || final != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: read muted members rc=%d finalize=%d: %s", rc, final, sqlite3_errmsg(db));
|
||||
u_free(members); return -1;
|
||||
}
|
||||
u_free(ctx->muted_members); ctx->muted_members = members; ctx->muted_count = count; ctx->muted_loaded = 1;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_RADIO, "radio: loaded local mutes grp=%016llx count=%d",
|
||||
@@ -524,34 +525,54 @@ int radio_set_member_muted(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64
|
||||
DEBUG_WARN(DEBUG_CATEGORY_RADIO, "radio: invalid local mute grp=%016llx node=%016llx muted=%d",
|
||||
(unsigned long long)group_id, (unsigned long long)node_id, muted); return -1;
|
||||
}
|
||||
if (!ctx->muted_loaded && radio_load_muted_members(ctx) < 0) return -1;
|
||||
int slot = -1;
|
||||
sqlite3* db = inst->topo_sqlite_db;
|
||||
if (!db || !sqlite3_get_autocommit(db)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute requires database without external transaction grp=%016llx",
|
||||
(unsigned long long)group_id); return -1;
|
||||
}
|
||||
int rc = sqlite3_exec(db, "BEGIN IMMEDIATE /* radio mute */", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute begin grp=%016llx rc=%d: %s",
|
||||
(unsigned long long)group_id, rc, sqlite3_errmsg(db)); return -1;
|
||||
}
|
||||
sqlite3_stmt* st = NULL; const char* stage = "load cache"; int slot = -1;
|
||||
if (!ctx->muted_loaded && radio_load_muted_members(ctx) < 0) { rc = SQLITE_ERROR; goto error; }
|
||||
for (int i = 0; i < ctx->muted_count; i++) if (ctx->muted_members[i] == node_id) { slot = i; break; }
|
||||
/* Выделить память до записи БД: после commit применение не может отказать. */
|
||||
if (muted && slot < 0) {
|
||||
uint64_t* next = u_realloc(ctx->muted_members, (size_t)(ctx->muted_count + 1) * sizeof(*next));
|
||||
if (!next) { DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: grow muted member cache failed"); return -1; }
|
||||
if (!next) { stage = "reserve cache"; rc = SQLITE_NOMEM; goto error; }
|
||||
ctx->muted_members = next;
|
||||
}
|
||||
sqlite3* db = inst->topo_sqlite_db;
|
||||
char sql[160];
|
||||
snprintf(sql, sizeof(sql), "UPDATE \"peers_%llu\" SET radio_muted=? WHERE node_id=?", (unsigned long long)group_id);
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (!db || sqlite3_prepare_v2(db, sql, -1, &st, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: prepare local mute: %s", db ? sqlite3_errmsg(db) : "no database"); return -1;
|
||||
}
|
||||
sqlite3_bind_int(st, 1, muted); sqlite3_bind_int64(st, 2, (sqlite3_int64)node_id);
|
||||
int rc = sqlite3_step(st); sqlite3_finalize(st);
|
||||
if (rc != SQLITE_DONE || sqlite3_changes(db) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: save local mute grp=%016llx node=%016llx rc=%d changed=%d: %s",
|
||||
(unsigned long long)group_id, (unsigned long long)node_id, rc, sqlite3_changes(db), sqlite3_errmsg(db)); return -1;
|
||||
}
|
||||
stage = "prepare"; rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL); if (rc != SQLITE_OK) goto error;
|
||||
stage = "bind mute"; rc = sqlite3_bind_int(st, 1, muted); if (rc != SQLITE_OK) goto error;
|
||||
stage = "bind member"; rc = sqlite3_bind_int64(st, 2, (sqlite3_int64)node_id); if (rc != SQLITE_OK) goto error;
|
||||
stage = "step"; rc = sqlite3_step(st); if (rc != SQLITE_DONE) goto error;
|
||||
stage = "changes"; if (sqlite3_changes(db) != 1) { rc = SQLITE_CONSTRAINT; goto error; }
|
||||
stage = "finalize"; rc = sqlite3_finalize(st); st = NULL; if (rc != SQLITE_OK) goto error;
|
||||
stage = "commit"; rc = sqlite3_exec(db, "COMMIT /* radio mute */", NULL, NULL, NULL); if (rc != SQLITE_OK) goto error;
|
||||
if (muted && slot < 0) ctx->muted_members[ctx->muted_count++] = node_id;
|
||||
else if (!muted && slot >= 0) ctx->muted_members[slot] = ctx->muted_members[--ctx->muted_count];
|
||||
radio_refresh_member_mute(inst, group_id, node_id);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "radio: local member mute grp=%016llx node=%016llx muted=%d",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_RADIO, "radio: local member mute grp=%016llx node=%016llx muted=%d committed=1",
|
||||
(unsigned long long)group_id, (unsigned long long)node_id, muted);
|
||||
radio_refresh_member_mute(inst, group_id, node_id);
|
||||
return 0;
|
||||
error:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute grp=%016llx node=%016llx muted=%d stage=%s rc=%d: %s",
|
||||
(unsigned long long)group_id, (unsigned long long)node_id, muted, stage, rc, sqlite3_errmsg(db));
|
||||
rc = sqlite3_finalize(st);
|
||||
if (rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute cleanup finalize rc=%d", rc);
|
||||
if (!sqlite3_get_autocommit(db)) {
|
||||
rc = sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute rollback rc=%d: %s", rc, sqlite3_errmsg(db));
|
||||
if (!sqlite3_get_autocommit(db)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "radio: local mute transaction remains open; requesting owner stop/DB close");
|
||||
utun_instance_stop(inst);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ── подписка (вкл/выкл прослушивание) ── */
|
||||
|
||||
@@ -39,6 +39,10 @@ int radio_member_muted(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t n
|
||||
int radio_member_silenced(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id);
|
||||
/* Изменилось временное подавление; настройки в БД и подписка остаются прежними. */
|
||||
void radio_refresh_member_mute(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id);
|
||||
/* Сохранить muted=0/1 для другого участника существующей CHAT-группы;0 — committed,-1 — отказ.
|
||||
* Собственная BEGIN IMMEDIATE,чужая транзакция запрещена. Память резервируется до записи;
|
||||
* cache и mute callback меняются после commit. При отказе настройка/звук сохраняются;
|
||||
* неудачный rollback с открытой транзакцией запрашивает stop/закрытие БД владельцем. */
|
||||
int radio_set_member_muted(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id, int muted);
|
||||
/* После удаления записи участника из БД удалить её из локального кэша. */
|
||||
void radio_forget_member_mute(struct UTUN_INSTANCE* inst, uint64_t group_id, uint64_t node_id);
|
||||
|
||||
Reference in new issue
Block a user