diff --git a/src/media_delivery/media_download.c b/src/media_delivery/media_download.c index 3893ac4e..f72fd740 100644 --- a/src/media_delivery/media_download.c +++ b/src/media_delivery/media_download.c @@ -230,8 +230,8 @@ static int md_dl_pump(struct media_download* dl) { if (dl->err) return -1; for (int bi = 0; bi < dl->num_blocks; bi++) { struct media_download_block* b = &dl->blocks[bi]; - if ((b->state == MD_BLK_IDLE || b->state == MD_BLK_WAIT) - && md_dl_request_block(dl, bi) < 0 && md_dl_failover_block(dl, bi, get_time_tb()) < 0) return -1; + if (b->state != MD_BLK_IDLE && b->state != MD_BLK_WAIT) continue; + if (md_dl_request_block(dl, bi) < 0 && (dl->err || md_dl_failover_block(dl, bi, get_time_tb()) < 0)) return -1; } return 0; } @@ -243,10 +243,15 @@ int md_dl_failover_block(struct media_download* dl, int bi, uint64_t now_tb) { if (md_dl_block_inflight(b) && dl->inflight > 0) dl->inflight--; b->state = MD_BLK_IDLE; b->bytes_received = 0; b->last_progress_tb = now_tb; b->retry_at_tb = 0; do { + if (dl->err) return -1; b->attempts++; DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "%s: failover block=%d attempts=%d/%d holder=%d/%d", MDL_ID, bi, b->attempts, md_dl_max_attempts(dl->inst), b->holder_idx, b->num_holders); - if (b->attempts >= md_dl_max_attempts(dl->inst)) { b->state = MD_BLK_FAILED; return -1; } + if (b->attempts >= md_dl_max_attempts(dl->inst)) { + b->state = MD_BLK_FAILED; + DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "%s: block attempts exhausted block=%d attempts=%d", MDL_ID, bi, b->attempts); + return -1; + } if (++b->holder_idx >= b->num_holders) { b->num_holders = 0; b->holder_idx = -1; b->state = MD_BLK_WAIT; b->wait_reason = MD_WAIT_SEARCH; return 0; diff --git a/tests/test_media_query.c b/tests/test_media_query.c index b87ed536..952b0097 100644 --- a/tests/test_media_query.c +++ b/tests/test_media_query.c @@ -7,6 +7,7 @@ #include "../src/media_delivery/media_index.h" #include "../src/media_delivery/media_download.h" #include "../src/routing_layer/etcp_router.h" +#include "../src/transport_layer/etcp.h" #include "../lib/debug_config.h" #include "../lib/mem.h" #include "../lib/platform_compat.h" @@ -53,7 +54,7 @@ int __wrap_etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t } /* u_malloc округляет выделение: ASAN защищает именно логическую границу входного payload. */ -static void release_injected(void* data) { +static void release_injected(uint8_t* data) { #ifdef __SANITIZE_ADDRESS__ if (data == injected) __asan_unpoison_memory_region(poison_tail, 64); #endif @@ -263,22 +264,31 @@ static void test_peer_reuse_and_completion(void) { puts("65 blocks from 13 holders, peer slot reuse, file contents and registration: PASS"); } -static int denied_reads; +static int denied_reads, started_selects; +static int trace_query(unsigned event, void* arg, void* statement, void* sql) { + (void)arg; (void)sql; + if (event == SQLITE_TRACE_STMT && !strncmp(sqlite3_sql(statement), "SELECT node_id FROM (", 21)) started_selects++; + return 0; +} static int deny_second_block(void* arg, int action, const char* table, const char* column, const char* db, const char* trigger) { (void)arg; (void)db; (void)trigger; if (action == SQLITE_READ && table && column && !strcmp(table, "block_availability") && !strcmp(column, "block_uuid")) - if (++denied_reads == 2) return SQLITE_DENY; + if (started_selects) { denied_reads++; return SQLITE_DENY; } return SQLITE_OK; } static void test_failures_and_restart(void) { reset(); struct UTUN_INSTANCE* s = instance(SERVER); struct UTUN_INSTANCE* c = instance(CLIENT); struct media_index_result r = result(2); seed(s, &r, 2, 2, 0); start(c, &r); - denied_reads = 0; sqlite3_set_authorizer(s->topo_sqlite_db, deny_second_block, NULL); + denied_reads = started_selects = 0; + sqlite3_trace_v2(s->topo_sqlite_db, SQLITE_TRACE_STMT, trace_query, NULL); + sqlite3_set_authorizer(s->topo_sqlite_db, deny_second_block, NULL); struct packet error = answer(s); struct media_pkt_query_resp* header = (struct media_pkt_query_resp*)error.data; - REQUIRE(denied_reads == 2 && header->status == MEDIA_QUERY_DB_ERROR && !header->num_blocks && error.len == MEDIA_QUERY_RESP_HDR_SIZE); + REQUIRE(denied_reads == 1 && started_selects == 1 && header->status == MEDIA_QUERY_DB_ERROR + && !header->num_blocks && error.len == MEDIA_QUERY_RESP_HDR_SIZE); sqlite3_set_authorizer(s->topo_sqlite_db, NULL, NULL); + sqlite3_trace_v2(s->topo_sqlite_db, 0, NULL, NULL); struct media_download* dl = download(c); REQUIRE(!media_download_cancel(c, dl->media_id, NULL, 0) && done_count == 1); start(c, &r); dl = download(c); struct media_download snapshot = *dl;