|
|
|
|
@ -13,9 +13,11 @@
|
|
|
|
|
#include "../lib/debug_config.h" |
|
|
|
|
#include "../lib/mem.h" |
|
|
|
|
#include "test_utils.h" |
|
|
|
|
#include "test_clock.h" |
|
|
|
|
#include <openssl/sha.h> |
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <string.h> |
|
|
|
|
#include <pthread.h> |
|
|
|
|
|
|
|
|
|
#define GROUP 5858585858585858ULL |
|
|
|
|
#define CHANNEL "5858585858585858" |
|
|
|
|
@ -38,6 +40,10 @@ struct network_test {
|
|
|
|
|
unsigned phase, ticks, progress; |
|
|
|
|
int answered, finished, transfer_status, failed; |
|
|
|
|
int slow_queued, slow_done, receive_pauses; |
|
|
|
|
pthread_mutex_t disk_mutex; |
|
|
|
|
pthread_cond_t disk_condition; |
|
|
|
|
uint64_t disk_started_tb; |
|
|
|
|
int disk_released, disk_error; |
|
|
|
|
unsigned peak_inflight; |
|
|
|
|
int (*saved_receive_ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t); |
|
|
|
|
unsigned restart_tick; |
|
|
|
|
@ -53,16 +59,20 @@ static void fail(struct network_test* t, const char* why) {
|
|
|
|
|
static void hex(const uint8_t* input, char* out) { for (unsigned i = 0; i < 32; i++) snprintf(out + 2 * i, 3, "%02x", input[i]); } |
|
|
|
|
|
|
|
|
|
static void slow_disk(void* arg) { |
|
|
|
|
(void)arg; |
|
|
|
|
#ifdef _WIN32 |
|
|
|
|
Sleep(6000); |
|
|
|
|
#else |
|
|
|
|
struct timespec delay = {6, 0}; nanosleep(&delay, NULL); |
|
|
|
|
#endif |
|
|
|
|
struct network_test* t = arg; |
|
|
|
|
pthread_mutex_lock(&t->disk_mutex); |
|
|
|
|
t->disk_started_tb = get_time_tb(); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: disk worker blocked started_tb=%llu", |
|
|
|
|
(unsigned long long)t->disk_started_tb); |
|
|
|
|
while (!t->disk_released) { |
|
|
|
|
int rc = pthread_cond_wait(&t->disk_condition, &t->disk_mutex); |
|
|
|
|
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "member_files test: disk wait failed rc=%d", rc); t->disk_error = rc; break; } |
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&t->disk_mutex); |
|
|
|
|
} |
|
|
|
|
static void slow_done(void* arg, int error) { |
|
|
|
|
struct network_test* t = arg; |
|
|
|
|
if (error) fail(t, "slow disk task rejected"); |
|
|
|
|
if (error || t->disk_error) fail(t, "slow disk task rejected or wait failed"); |
|
|
|
|
t->slow_done = 1; |
|
|
|
|
} |
|
|
|
|
static void capture_log(int level, const char* category, const char* message) { |
|
|
|
|
@ -197,6 +207,15 @@ static int downloaded(struct network_test* t) {
|
|
|
|
|
static void tick(void* arg) { |
|
|
|
|
struct network_test* t = arg; t->timer = NULL; |
|
|
|
|
if (++t->ticks > 6000) { fail(t, "60s deadline"); return; } |
|
|
|
|
// Отсчитываем полные 6с от фактической блокировки worker, не от постановки задания.
|
|
|
|
|
pthread_mutex_lock(&t->disk_mutex); |
|
|
|
|
if (t->disk_started_tb && !t->disk_released && get_time_tb() - t->disk_started_tb >= 60000) { |
|
|
|
|
t->disk_released = 1; |
|
|
|
|
pthread_cond_signal(&t->disk_condition); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files test: disk released elapsed_tb=%llu receive_pauses=%d peak_inflight=%u", |
|
|
|
|
(unsigned long long)(get_time_tb() - t->disk_started_tb), t->receive_pauses, t->peak_inflight); |
|
|
|
|
} |
|
|
|
|
pthread_mutex_unlock(&t->disk_mutex); |
|
|
|
|
struct ETCP_ROUTER_CONN* route = etcp_router_conn_get(t->nodes[AUTHOR], GROUP, t->ids[SERVER], ETCP_RT_ID_MEMBER_FILES); |
|
|
|
|
if (route->inflight_q->count > t->peak_inflight) t->peak_inflight = route->inflight_q->count; |
|
|
|
|
if (t->phase == 0) { |
|
|
|
|
@ -304,10 +323,11 @@ again:
|
|
|
|
|
|
|
|
|
|
int main(void) { |
|
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_category_level(DEBUG_CATEGORY_MEDIA, DEBUG_LEVEL_DEBUG); |
|
|
|
|
test_clock_init(1000); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_TRACE); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_ETCPROUTE, DEBUG_LEVEL_DEBUG); |
|
|
|
|
utun_instance_set_tun_init_enabled(0); |
|
|
|
|
struct network_test t = {0}; active = &t; |
|
|
|
|
struct network_test t = { .disk_mutex = PTHREAD_MUTEX_INITIALIZER, .disk_condition = PTHREAD_COND_INITIALIZER }; active = &t; |
|
|
|
|
debug_set_log_hook(capture_log); |
|
|
|
|
snprintf(t.dir, sizeof(t.dir), "/tmp/utun_member_files_net_XXXXXX"); |
|
|
|
|
if (test_mkdtemp(t.dir)) return 1; |
|
|
|
|
@ -316,28 +336,29 @@ int main(void) {
|
|
|
|
|
int port = 39000 + (int)(get_time_tb() % 8000); |
|
|
|
|
for (unsigned i = 0; i < NODE_COUNT; i++) if (start(&t, i, port)) { t.failed = 1; goto stop; } |
|
|
|
|
if (setup(&t)) { t.failed = 1; goto stop; } |
|
|
|
|
t.timer = uasync_set_timeout(t.ua, 100, &t, tick, "member_files_test"); uasync_mainloop(t.ua); |
|
|
|
|
t.timer = uasync_set_timeout(t.ua, 100, &t, tick, "member_files_test"); test_clock_mainloop(t.ua); |
|
|
|
|
if (!t.failed) { |
|
|
|
|
// Stop/destroy вне callbacks: server cleanup ещё использует storage после текущего I/O.
|
|
|
|
|
api_start(&t, 0, AUTHOR, 1, "stop.bin", t.source); |
|
|
|
|
api_start(&t, 1, AUTHOR, 0, "file.bin", NULL); |
|
|
|
|
uint64_t deadline = get_time_tb() + 50000; |
|
|
|
|
while (!t.nodes[SERVER]->member_files->server_transfer_count && get_time_tb() < deadline) { |
|
|
|
|
uasync_poll(t.ua, 0); |
|
|
|
|
#ifdef _WIN32 |
|
|
|
|
Sleep(1); |
|
|
|
|
#else |
|
|
|
|
struct timespec delay = {0, 1000000}; nanosleep(&delay, NULL); |
|
|
|
|
#endif |
|
|
|
|
test_clock_poll(t.ua, 10); |
|
|
|
|
} |
|
|
|
|
if (!t.nodes[SERVER]->member_files->server_transfer_count) fail(&t, "server stream did not start before teardown"); |
|
|
|
|
} |
|
|
|
|
stop: |
|
|
|
|
if (t.timer) uasync_cancel_timeout(t.ua, t.timer); |
|
|
|
|
// При ошибке сценария освобождаем worker до destroy, иначе drain будет ждать блокировку.
|
|
|
|
|
pthread_mutex_lock(&t.disk_mutex); |
|
|
|
|
t.disk_released = 1; pthread_cond_signal(&t.disk_condition); |
|
|
|
|
pthread_mutex_unlock(&t.disk_mutex); |
|
|
|
|
for (unsigned i = 0; i < NODE_COUNT; i++) if (t.nodes[i]) utun_instance_destroy(t.nodes[i]); |
|
|
|
|
if (!t.failed && (!t.api[0].finished || !t.api[1].finished || t.api[0].status != MF_CANCELLED || t.api[1].status != MF_CANCELLED)) |
|
|
|
|
t.failed = 1; |
|
|
|
|
uasync_destroy(t.ua, 0); |
|
|
|
|
pthread_cond_destroy(&t.disk_condition); pthread_mutex_destroy(&t.disk_mutex); |
|
|
|
|
debug_set_log_hook(NULL); |
|
|
|
|
test_clock_finish(); |
|
|
|
|
printf("member_files network: %s phase=%u\n", t.failed ? "FAIL" : "PASS", t.phase); return t.failed; |
|
|
|
|
} |
|
|
|
|
|