diff --git a/1 b/1 index 6d071627..e3bae668 100644 --- a/1 +++ b/1 @@ -11,3 +11,546 @@ timeout: не удалось выполнить команду «./test_etcp_two timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога +cc1: fatal error: test_ll_queue_fixed.c: Нет такого файла или каталога +compilation terminated. +Using built-in specs. +COLLECT_GCC=gcc +COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper +OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa +OFFLOAD_TARGET_DEFAULT=1 +Target: x86_64-linux-gnu +Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 +Thread model: posix +Supported LTO compression algorithms: zlib zstd +gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04) +COLLECT_GCC_OPTIONS='-I' '/home/vnc1/proj/utun3/src' '-I' '/home/vnc1/proj/utun3/lib' '-I' '/home/vnc1/proj/utun3/tinycrypt/lib/include' '-I' '/home/vnc1/proj/utun3/tinycrypt/lib/source' '-g' '-O2' '-v' '-o' 'test_ll_queue_fixed' '-mtune=generic' '-march=x86-64' '-dumpdir' 'test_ll_queue_fixed-' + /usr/libexec/gcc/x86_64-linux-gnu/13/cc1 -quiet -v -I /home/vnc1/proj/utun3/src -I /home/vnc1/proj/utun3/lib -I /home/vnc1/proj/utun3/tinycrypt/lib/include -I /home/vnc1/proj/utun3/tinycrypt/lib/source -imultiarch x86_64-linux-gnu test_ll_queue_fixed.c -D_FORTIFY_SOURCE=3 -quiet -dumpdir test_ll_queue_fixed- -dumpbase test_ll_queue_fixed.c -dumpbase-ext .c -mtune=generic -march=x86-64 -g -O2 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccwoLaK6.s +GNU C17 (Ubuntu 13.3.0-6ubuntu2~24.04) version 13.3.0 (x86_64-linux-gnu) + compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP + +GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 +ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed" +ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include" +#include "..." search starts here: +#include <...> search starts here: + /home/vnc1/proj/utun3/src + /home/vnc1/proj/utun3/lib + /home/vnc1/proj/utun3/tinycrypt/lib/include + /home/vnc1/proj/utun3/tinycrypt/lib/source + /usr/lib/gcc/x86_64-linux-gnu/13/include + /usr/local/include + /usr/include/x86_64-linux-gnu + /usr/include +End of search list. +cc1: fatal error: test_ll_queue_fixed.c: Нет такого файла или каталога +compilation terminated. +malloc(): corrupted top size +timeout: отслеживаемая команда завершилась созданием дампа +timeout: не удалось выполнить команду «./test_ll_queue_updated»: Нет такого файла или каталога +cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога +compilation terminated. +cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога +compilation terminated. +cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога +compilation terminated. +malloc(): corrupted top size +timeout: отслеживаемая команда завершилась созданием дампа +malloc(): corrupted top size +timeout: отслеживаемая команда завершилась созданием дампа +timeout: отслеживаемая команда завершилась созданием дампа +../build-aux/test-driver: строка 112: 1176142 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +free(): double free detected in tcache 2 +timeout: отслеживаемая команда завершилась созданием дампа +ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») +utun_instance.c: In function ‘utun_instance_create’: +utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] + 109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); + | ^~~ +In file included from /usr/include/stdio.h:980, + from utun_instance.h:6, + from utun_instance.c:2: +In function ‘snprintf’, + inlined from ‘utun_instance_create’ at utun_instance.c:109:9: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 + 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 55 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 56 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +In file included from config_parser.c:8: +config_parser.c: In function ‘parse_debug_categories’: +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error + | ^~~~~~~~~~~~~~~~~~ +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 66 | categories |= DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +etcp.c: In function ‘etcp_connection_create’: +etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + | ^~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +In file included from etcp.h:7, + from etcp.c:3: +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + | ^~~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: +pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] + 347 | uint8_t* d = (entry); + | ^ +make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: +test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] + 428 | write(sockets[0], &data, 1); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +In file included from test_debug_categories.c:8: +test_debug_categories.c: In function ‘main’: +../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 64 | debug_categories = DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») +utun_instance.c: In function ‘utun_instance_create’: +utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] + 109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); + | ^~~ +In file included from /usr/include/stdio.h:980, + from utun_instance.h:6, + from utun_instance.c:2: +In function ‘snprintf’, + inlined from ‘utun_instance_create’ at utun_instance.c:109:9: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 + 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 55 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 56 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +In file included from config_parser.c:8: +config_parser.c: In function ‘parse_debug_categories’: +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error + | ^~~~~~~~~~~~~~~~~~ +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 66 | categories |= DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +etcp.c: In function ‘etcp_connection_create’: +etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + | ^~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +In file included from etcp.h:7, + from etcp.c:3: +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + | ^~~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: +pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] + 347 | uint8_t* d = (entry); + | ^ +make[1]: *** [Makefile:375: all-recursive] Ошибка 1 +make: *** [Makefile:316: all] Обрыв канала +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») +utun_instance.c: In function ‘utun_instance_create’: +utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] + 109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); + | ^~~ +In file included from /usr/include/stdio.h:980, + from utun_instance.h:6, + from utun_instance.c:2: +In function ‘snprintf’, + inlined from ‘utun_instance_create’ at utun_instance.c:109:9: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 + 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 55 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 56 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +In file included from config_parser.c:8: +config_parser.c: In function ‘parse_debug_categories’: +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error + | ^~~~~~~~~~~~~~~~~~ +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 66 | categories |= DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +etcp.c: In function ‘etcp_connection_create’: +etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + | ^~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +In file included from etcp.h:7, + from etcp.c:3: +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + | ^~~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: +pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] + 347 | uint8_t* d = (entry); + | ^ +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: +test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] + 428 | write(sockets[0], &data, 1); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +In file included from test_debug_categories.c:8: +test_debug_categories.c: In function ‘main’: +../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 64 | debug_categories = DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +../build-aux/test-driver: строка 112: 1197495 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») +utun_instance.c: In function ‘utun_instance_create’: +utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] + 109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); + | ^~~ +In file included from /usr/include/stdio.h:980, + from utun_instance.h:6, + from utun_instance.c:2: +In function ‘snprintf’, + inlined from ‘utun_instance_create’ at utun_instance.c:109:9: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 + 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 55 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 56 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +In file included from config_parser.c:8: +config_parser.c: In function ‘parse_debug_categories’: +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error + | ^~~~~~~~~~~~~~~~~~ +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 66 | categories |= DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +etcp.c: In function ‘etcp_connection_create’: +etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + | ^~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +In file included from etcp.h:7, + from etcp.c:3: +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + | ^~~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: +pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] + 347 | uint8_t* d = (entry); + | ^ +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c: In function ‘test_memory_pool_integration’: +test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) + 670 | queue_data_free(entry_data3); + | ^~~~~~~~~~~ +test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in +test_ll_queue.c: In function ‘test_edge_cases’: +test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) + 719 | queue_data_free(entry_retrieved); + | ^~~~~~~~~~~~~~~ +test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: +test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) + 1005 | queue_data_put(q, entry_data, data->id); + | ^~~~~~~~~~ +make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c: In function ‘test_memory_pool_integration’: +test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) + 670 | queue_data_free(entry_data3); + | ^~~~~~~~~~~ +test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in +test_ll_queue.c: In function ‘test_edge_cases’: +test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) + 719 | queue_data_free(entry_retrieved); + | ^~~~~~~~~~~~~~~ +test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: +test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) + 1005 | queue_data_put(q, entry_data, data->id); + | ^~~~~~~~~~ +make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c: In function ‘test_memory_pool_integration’: +test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) + 670 | queue_data_free(entry_data3); + | ^~~~~~~~~~~ +test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in +test_ll_queue.c: In function ‘test_edge_cases’: +test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) + 719 | queue_data_free(entry_retrieved); + | ^~~~~~~~~~~~~~~ +test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: +test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) + 1005 | queue_data_put(q, entry_data, data->id); + | ^~~~~~~~~~ +make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») +utun_instance.c: In function ‘utun_instance_create’: +utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] + 109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); + | ^~~ +In file included from /usr/include/stdio.h:980, + from utun_instance.h:6, + from utun_instance.c:2: +In function ‘snprintf’, + inlined from ‘utun_instance_create’ at utun_instance.c:109:9: +/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 + 54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 55 | __glibc_objsize (__s), __fmt, + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 56 | __va_arg_pack ()); + | ~~~~~~~~~~~~~~~~~ +In file included from config_parser.c:8: +config_parser.c: In function ‘parse_debug_categories’: +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error + | ^~~~~~~~~~~~~~~~~~ +../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 66 | categories |= DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +etcp.c: In function ‘etcp_connection_create’: +etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); + | ^~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +In file included from etcp.h:7, + from etcp.c:3: +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] + 100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + | ^~~~~~~~~~~~~~~ + | | + | void (*)(struct ll_queue *, void *) +../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ + 83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); + | ~~~~~~~~~~~~~~~~~~^~~~~~ +pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: +pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] + 347 | uint8_t* d = (entry); + | ^ +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c: In function ‘test_memory_pool_integration’: +test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) + 670 | queue_data_free(entry_data3); + | ^~~~~~~~~~~ +test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in +test_ll_queue.c: In function ‘test_edge_cases’: +test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) + 719 | queue_data_free(entry_retrieved); + | ^~~~~~~~~~~~~~~ +test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: +test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) + 1005 | queue_data_put(q, entry_data, data->id); + | ^~~~~~~~~~ +make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 +make[1]: *** [Makefile:1494: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: +test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] + 428 | write(sockets[0], &data, 1); + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ +In file included from test_debug_categories.c:8: +test_debug_categories.c: In function ‘main’: +../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] + 42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) + | ^ +test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ + 64 | debug_categories = DEBUG_CATEGORY_ALL; + | ^~~~~~~~~~~~~~~~~~ +../build-aux/test-driver: строка 112: 1203433 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +../build-aux/test-driver: строка 112: 1203954 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 +make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 +make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 +make[1]: *** [Makefile:1495: check-am] Ошибка 2 +make: *** [Makefile:375: check-recursive] Ошибка 1 +make: *** Нет правила для сборки цели «test_ll_queue». Останов. +make: *** Нет правила для сборки цели «test_ll_queue». Останов. +make: *** Нет правила для сборки цели «test_ll_queue». Останов. +/usr/bin/ld: невозможно найти ../lib/libuasync.a: Нет такого файла или каталога +collect2: error: ld returned 1 exit status +/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o: в функции «_start»: +(.text+0x1b): undefined reference to `main' +collect2: error: ld returned 1 exit status +timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога +timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога +timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога +/usr/bin/ld: невозможно найти ../lib/libuasync.a: Нет такого файла или каталога +collect2: error: ld returned 1 exit status diff --git a/AGENTS.md b/AGENTS.md index eca2c6a7..5271307f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -214,6 +214,8 @@ Crypto: Fixed CCM nonce size to 13 bytes, all crypto tests passing - Когда пишешь код всегда загружай в контекст и мысленно разберись как работают все функции/струтуры, назначение переменных которые используешь. - Для отладки не printf а DEBUG_* - перед сборкой всегда make clean +- прежде чем вносить правки хорошо разберись как должно работать, просмотри все нужные функции полностью, нужно целостное понимание. +- всегда думай можно ли написать проще. Проверяй дубликаты и что уже есть, продумай логику до тех пор пока не будет полного понимания Действия при поиске бага: 1. создать комит или бэкап всего что меняешь diff --git a/lib/debug_config.c b/lib/debug_config.c index df915457..f799cdf0 100644 --- a/lib/debug_config.c +++ b/lib/debug_config.c @@ -1,6 +1,5 @@ /** - * Debug configuration implementation - * Runtime debug configuration system for flexible debug output control + * Debug module */ #include "debug_config.h" @@ -13,6 +12,75 @@ #include #include +#include +#include +#include +#include + +void log_dump(const char* prefix, const uint8_t* data, size_t len) { + // Build packet data as hex string for single-line output + char hex_buf[513]; // 256 bytes * 2 chars + 1 for null terminator + size_t hex_len = 0; + size_t show_len = (len > 128) ? 128 : len; // Show max 128 bytes + + for (size_t i = 0; i < show_len && hex_len < 512 - 3; i++) { + hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]); + if (i < show_len - 1 && (i + 1) % 32 == 0) { // Add space every 32 bytes + hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, " "); + } + } + + if (len > 128) { + hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "..."); + } + + // Single-line debug output with packet info + DEBUG_INFO(DEBUG_CATEGORY_ETCP, "%s: len=%zu hex=%s", prefix, len, hex_buf); + + // Additional debug info for first few bytes + if (len >= 2) { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "%s: first_bytes=%02x%02x last_bytes=%02x%02x", + prefix, data[0], data[1], data[len-2], data[len-1]); + } +} + + +size_t addr_to_string(const struct sockaddr_storage *addr, char *buf, size_t buflen) { + if (!buf || buflen == 0) + return 0; + + buf[0] = '\0'; // всегда валидная строка + + char ip[INET6_ADDRSTRLEN]; + uint16_t port; + int n; + + if (addr->ss_family == AF_INET) { + const struct sockaddr_in *a = (const struct sockaddr_in *)addr; + inet_ntop(AF_INET, &a->sin_addr, ip, sizeof(ip)); + port = ntohs(a->sin_port); + + n = snprintf(buf, buflen, "%s:%u", ip, port); + + } else if (addr->ss_family == AF_INET6) { + const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)addr; + inet_ntop(AF_INET6, &a->sin6_addr, ip, sizeof(ip)); + port = ntohs(a->sin6_port); + + n = snprintf(buf, buflen, "[%s]:%u", ip, port); + + } else { + return 0; + } + + // snprintf гарантирует '\0', но на всякий случай: + buf[buflen - 1] = '\0'; + + // Возвращаем фактическую длину (как snprintf) + return (n < 0) ? 0 : (size_t)n; +} + + /* Get category by name */ static debug_category_t get_category_by_name(const char* name) { struct { diff --git a/lib/debug_config.c1 b/lib/debug_config.c1 deleted file mode 100755 index c24c39c3..00000000 --- a/lib/debug_config.c1 +++ /dev/null @@ -1,435 +0,0 @@ -/** - * Debug configuration implementation - * Runtime debug configuration system for flexible debug output control - */ - -#include "debug_config.h" -#include -#include -#include -#include -#include -#include -#include -#include - -/* Rate limiting */ -static size_t output_count = 0; -static time_t last_output_time = 0; - -/* ANSI color codes */ -static const char* color_red = "\033[31m"; -static const char* color_yellow = "\033[33m"; -static const char* color_green = "\033[32m"; -static const char* color_blue = "\033[34m"; -static const char* color_magenta = "\033[35m"; -static const char* color_cyan = "\033[36m"; -static const char* color_reset = "\033[0m"; - -/* Get category by name */ -static debug_category_t get_category_by_name(const char* name) { - struct { - const char* n; - debug_category_t c; - } map[] = { - {"none", DEBUG_CATEGORY_NONE}, - {"uasync", DEBUG_CATEGORY_UASYNC}, - {"ll_queue", DEBUG_CATEGORY_LL_QUEUE}, - {"connection", DEBUG_CATEGORY_CONNECTION}, - {"etcp", DEBUG_CATEGORY_ETCP}, - {"crypto", DEBUG_CATEGORY_CRYPTO}, - {"memory", DEBUG_CATEGORY_MEMORY}, - {"timing", DEBUG_CATEGORY_TIMING}, - {"config", DEBUG_CATEGORY_CONFIG}, - {"tun", DEBUG_CATEGORY_TUN}, - {"routing", DEBUG_CATEGORY_ROUTING}, - {"timers", DEBUG_CATEGORY_TIMERS}, - {"all", DEBUG_CATEGORY_ALL}, - {NULL, 0} - }; - - for (int i = 0; map[i].n; i++) { - if (strcasecmp(map[i].n, name) == 0) { - return map[i].c; - } - } - return DEBUG_CATEGORY_NONE; -} - -/* Get level by name */ -static debug_level_t get_level_by_name(const char* name) { - struct { - const char* n; - debug_level_t l; - } map[] = { - {"none", DEBUG_LEVEL_NONE}, - {"error", DEBUG_LEVEL_ERROR}, - {"warn", DEBUG_LEVEL_WARN}, - {"info", DEBUG_LEVEL_INFO}, - {"debug", DEBUG_LEVEL_DEBUG}, - {"trace", DEBUG_LEVEL_TRACE}, - {NULL, 0} - }; - - for (int i = 0; map[i].n; i++) { - if (strcasecmp(map[i].n, name) == 0) { - return map[i].l; - } - } - return DEBUG_LEVEL_NONE; -} - -/* Global debug configuration */ -debug_config_t g_debug_config; -FILE* debug_output_file = NULL; - -/* Forward declaration for global_config structure */ -struct global_config { - char log_file[256]; - char debug_level[32]; - uint32_t debug_categories; - int enable_timestamp; - int enable_function_names; - int enable_file_lines; - int enable_colors; -}; - -/* Apply debug settings from configuration if not overridden by CLI */ -void debug_apply_config(const struct global_config *config, int cli_override_level, int cli_override_categories) { - if (!config) return; - - // Apply log file if specified and not overridden - if (config->log_file[0] != '\0') { - debug_set_output_file(config->log_file); - DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set log file to: %s", config->log_file); - } - - // Apply debug level from config if not overridden by CLI - if (!cli_override_level && config->debug_level[0] != '\0') { - debug_level_t level = DEBUG_LEVEL_INFO; // default - if (strcasecmp(config->debug_level, "error") == 0) level = DEBUG_LEVEL_ERROR; - else if (strcasecmp(config->debug_level, "warn") == 0) level = DEBUG_LEVEL_WARN; - else if (strcasecmp(config->debug_level, "info") == 0) level = DEBUG_LEVEL_INFO; - else if (strcasecmp(config->debug_level, "debug") == 0) level = DEBUG_LEVEL_DEBUG; - else if (strcasecmp(config->debug_level, "trace") == 0) level = DEBUG_LEVEL_TRACE; - - debug_set_level(level); - DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug level from config: %s", config->debug_level); - } - - // Apply debug categories from config if not overridden by CLI - if (!cli_override_categories && config->debug_categories != 0) { - debug_set_categories(config->debug_categories); - DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Set debug categories from config: 0x%X", config->debug_categories); - } - - // Apply output options from config - debug_enable_timestamp(config->enable_timestamp); - debug_enable_function_name(config->enable_function_names); - debug_enable_file_line(config->enable_file_lines); - debug_enable_color(config->enable_colors); - - DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied debug configuration from file"); -} - -/* Initialize debug system with default settings */ -void debug_config_init(void) { - g_debug_config.default_level = DEBUG_LEVEL_ERROR; - g_debug_config.categories = DEBUG_CATEGORY_ALL; - memset(g_debug_config.category_levels, 0, sizeof(g_debug_config.category_levels)); - g_debug_config.timestamp_enabled = 1; - g_debug_config.function_name_enabled = 1; - g_debug_config.file_line_enabled = 1; - g_debug_config.color_enabled = 1; - g_debug_config.max_output_per_second = 0; - g_debug_config.output_file = NULL; - - if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { - fclose(debug_output_file); - } - debug_output_file = stdout; - - output_count = 0; - last_output_time = 0; -} - -/* Set debug level */ -void debug_set_level(debug_level_t level) { - g_debug_config.default_level = level; -} - -/* Enable/disable specific categories */ -void debug_enable_category(debug_category_t category) { - g_debug_config.categories |= category; -} - -void debug_disable_category(debug_category_t category) { - g_debug_config.categories &= ~category; -} - -void debug_set_categories(uint32_t categories) { - g_debug_config.categories = categories; -} - -/* Configure output options */ -void debug_enable_timestamp(int enable) { - g_debug_config.timestamp_enabled = enable; -} - -void debug_enable_function_name(int enable) { - g_debug_config.function_name_enabled = enable; -} - -void debug_enable_file_line(int enable) { - g_debug_config.file_line_enabled = enable; -} - -void debug_enable_color(int enable) { - g_debug_config.color_enabled = enable; -} - -void debug_set_rate_limit(size_t max_per_second) { - g_debug_config.max_output_per_second = max_per_second; -} - -void debug_set_output_file(const char* file_path) { - if (file_path == NULL) { - if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { - fclose(debug_output_file); - } - debug_output_file = stdout; - g_debug_config.output_file = NULL; - } else { - FILE* new_file = fopen(file_path, "a"); - if (new_file) { - if (debug_output_file && debug_output_file != stdout && debug_output_file != stderr) { - fclose(debug_output_file); - } - debug_output_file = new_file; - g_debug_config.output_file = file_path; - } - } -} - -/* Check if debug output should be shown for given level and category */ -int debug_should_output(debug_level_t level, debug_category_t category) { - /* Check if category is enabled */ - if (!(g_debug_config.categories & category)) { - return 0; - } - - /* Check if level is sufficient */ - debug_level_t eff_level = debug_get_effective_level(category); - if (level > eff_level) { - return 0; - } - - /* Rate limiting check */ - if (g_debug_config.max_output_per_second > 0) { - time_t current_time = time(NULL); - if (current_time != last_output_time) { - output_count = 0; - last_output_time = current_time; - } - - if (output_count >= g_debug_config.max_output_per_second) { - return 0; - } - output_count++; - } - - return 1; -} - -/* Get current debug level for a category */ -debug_level_t debug_get_effective_level(debug_category_t category) { - if (category == DEBUG_CATEGORY_ALL || category == DEBUG_CATEGORY_NONE) { - return g_debug_config.default_level; - } - - /* Assume single category bit */ - uint32_t cat = category; - if (__builtin_popcount(cat) != 1) { - return DEBUG_LEVEL_NONE; /* Not supported for multiple */ - } - - int index = __builtin_ctz(cat); - if (index >= NUM_DEBUG_CATEGORIES) { - return DEBUG_LEVEL_NONE; - } - - debug_level_t lev = g_debug_config.category_levels[index]; - return (lev == DEBUG_LEVEL_NONE) ? g_debug_config.default_level : lev; -} - -/* Get color for debug level */ -static const char* get_level_color(debug_level_t level) { - if (!g_debug_config.color_enabled) { - return ""; - } - - switch (level) { - case DEBUG_LEVEL_ERROR: return color_red; - case DEBUG_LEVEL_WARN: return color_yellow; - case DEBUG_LEVEL_INFO: return color_green; - case DEBUG_LEVEL_DEBUG: return color_blue; - case DEBUG_LEVEL_TRACE: return color_magenta; - default: return ""; - } -} - -/* Get level name */ -static const char* get_level_name(debug_level_t level) { - switch (level) { - case DEBUG_LEVEL_ERROR: return "ERROR"; - case DEBUG_LEVEL_WARN: return "WARN"; - case DEBUG_LEVEL_INFO: return "INFO"; - case DEBUG_LEVEL_DEBUG: return "DEBUG"; - case DEBUG_LEVEL_TRACE: return "TRACE"; - default: return "UNKNOWN"; - } -} - -/* Format and output debug message */ -void debug_output(debug_level_t level, debug_category_t category, - const char* function, const char* file, int line, - const char* format, ...) { - -#define BUFFER_SIZE 4096 - char buffer[BUFFER_SIZE]; - int offset = 0; - size_t remaining = BUFFER_SIZE; - - va_list args; - va_start(args, format); - - FILE* output = debug_output_file ? debug_output_file : stdout; - - /* Add timestamp if enabled */ - if (g_debug_config.timestamp_enabled) { - time_t now = time(NULL); - struct tm* tm_info = localtime(&now); - char time_str[32]; - strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info); - offset += snprintf(buffer + offset, remaining, "[%s] ", time_str); - remaining = BUFFER_SIZE - offset; - } - - /* Add level and color */ - const char* color = get_level_color(level); - const char* level_name = get_level_name(level); - if (g_debug_config.color_enabled) { - offset += snprintf(buffer + offset, remaining, "%s[%s]%s ", color, level_name, color_reset); - } else { - offset += snprintf(buffer + offset, remaining, "[%s] ", level_name); - } - remaining = BUFFER_SIZE - offset; - - /* Add category */ - offset += snprintf(buffer + offset, remaining, "[%d] ", category); - remaining = BUFFER_SIZE - offset; - - /* Add function name if enabled */ - if (g_debug_config.function_name_enabled && function) { - offset += snprintf(buffer + offset, remaining, "%s() ", function); - remaining = BUFFER_SIZE - offset; - } - - /* Add file:line if enabled */ - if (g_debug_config.file_line_enabled && file) { - offset += snprintf(buffer + offset, remaining, "(%s:%d) ", file, line); - remaining = BUFFER_SIZE - offset; - } - - /* Add the actual message */ - offset += vsnprintf(buffer + offset, remaining, format, args); - remaining = BUFFER_SIZE - offset; - - /* Add newline */ - if (remaining > 1) { - buffer[offset] = '\n'; - buffer[offset + 1] = '\0'; - } else { - buffer[BUFFER_SIZE - 2] = '\n'; - buffer[BUFFER_SIZE - 1] = '\0'; - } - - va_end(args); - - /* Output the entire line at once */ - fprintf(output, "%s", buffer); - fflush(output); -#undef BUFFER_SIZE -} - -/* Parse debug configuration from string */ -int debug_parse_config(const char* config_string) { - if (!config_string || !*config_string) { - return 0; - } - - char* str = strdup(config_string); - if (!str) { - return -1; - } - - char* token = strtok(str, ","); - while (token) { - /* Trim leading spaces */ - while (isspace(*token)) { - token++; - } - - char* colon = strchr(token, ':'); - if (!colon) { - free(str); - return -1; - } - - *colon = '\0'; - char* cat_name = token; - char* level_name = colon + 1; - - /* Trim leading spaces in level_name */ - while (isspace(*level_name)) { - level_name++; - } - - /* Find category */ - debug_category_t cat = get_category_by_name(cat_name); - if (cat == DEBUG_CATEGORY_NONE) { - free(str); - return -1; - } - - /* Find level */ - debug_level_t lev = get_level_by_name(level_name); - if (lev == DEBUG_LEVEL_NONE) { - free(str); - return -1; - } - - if (cat == DEBUG_CATEGORY_ALL) { - g_debug_config.default_level = lev; - } else { - g_debug_config.categories |= cat; - int index = __builtin_ctz(cat); - if (index < NUM_DEBUG_CATEGORIES) { - g_debug_config.category_levels[index] = lev; - } - } - - token = strtok(NULL, ","); - } - - free(str); - return 0; -} - -/* Apply debug settings from configuration string values */ -void debug_apply_config_values(const char *log_file, const char *debug_level_str, uint32_t debug_categories, - int enable_timestamp, int enable_function_names, int enable_file_lines, int enable_colors, - int cli_override_level, int cli_override_categories) { - // This function would be implemented to apply individual config values - // For now, it's a placeholder that could be expanded based on specific needs -} \ No newline at end of file diff --git a/lib/debug_config.h b/lib/debug_config.h index feb84113..209ef110 100644 --- a/lib/debug_config.h +++ b/lib/debug_config.h @@ -6,6 +6,8 @@ #ifndef DEBUG_CONFIG_H #define DEBUG_CONFIG_H +#include // struct sockaddr_storage +#include // size_t #include #include #include @@ -74,6 +76,12 @@ void debug_enable_function_name(int enable); void debug_enable_file_line(int enable); void debug_set_output_file(const char* file_path); +// вывод struct sockaddr_storage как текст +size_t addr_to_string(const struct sockaddr_storage *addr, char *buf, size_t buflen); + +// hex dump в лог +void log_dump(const char* prefix, const uint8_t* data, size_t len); + #include extern FILE* debug_output_file; diff --git a/lib/debug_config.h1 b/lib/debug_config.h1 deleted file mode 100755 index f262233d..00000000 --- a/lib/debug_config.h1 +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Runtime debug configuration system - * Provides flexible control over debug output without recompilation - */ - -#ifndef DEBUG_CONFIG_H -#define DEBUG_CONFIG_H - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Number of categories (bits 0-10) */ -#define NUM_DEBUG_CATEGORIES 11 - -/* Debug levels */ -typedef enum { - DEBUG_LEVEL_NONE = 0, // No debug output - DEBUG_LEVEL_ERROR = 1, // Errors only - DEBUG_LEVEL_WARN = 2, // Warnings and errors - DEBUG_LEVEL_INFO = 3, // Info, warnings, errors - DEBUG_LEVEL_DEBUG = 4, // Full debug output - DEBUG_LEVEL_TRACE = 5 // Trace everything -} debug_level_t; - -/* Debug categories - can be combined with bitwise OR */ -typedef enum { - DEBUG_CATEGORY_NONE = 0, - DEBUG_CATEGORY_UASYNC = 1 << 0, // u_async module - DEBUG_CATEGORY_LL_QUEUE = 1 << 1, // ll_queue module - DEBUG_CATEGORY_CONNECTION = 1 << 2, // connection module - DEBUG_CATEGORY_ETCP = 1 << 3, // etcp module - DEBUG_CATEGORY_CRYPTO = 1 << 4, // crypto operations - DEBUG_CATEGORY_MEMORY = 1 << 5, // memory management - DEBUG_CATEGORY_TIMING = 1 << 6, // timing/performance - DEBUG_CATEGORY_CONFIG = 1 << 7, // configuration parsing - DEBUG_CATEGORY_TUN = 1 << 8, // TUN interface - DEBUG_CATEGORY_ROUTING = 1 << 9, // routing table - DEBUG_CATEGORY_TIMERS = 1 << 10, // timer management - DEBUG_CATEGORY_ALL = 0xFFFFFFFF -} debug_category_t; - -/* Debug configuration structure */ -typedef struct { - debug_level_t default_level; // Default debug level - uint32_t categories; // Enabled categories (bitmask) - debug_level_t category_levels[NUM_DEBUG_CATEGORIES]; // Per-category levels (NONE means use default) - int timestamp_enabled; // Include timestamps in output - int function_name_enabled; // Include function names - int file_line_enabled; // Include file:line info - int color_enabled; // Use ANSI colors (if terminal supports) - size_t max_output_per_second; // Rate limiting (0 = unlimited) - const char* output_file; // NULL = stdout, otherwise file path -} debug_config_t; - -/* Global debug configuration */ -extern debug_config_t g_debug_config; - -/* Initialize debug system with default settings */ -void debug_config_init(void); - -/* Set debug level */ -void debug_set_level(debug_level_t level); - -/* Enable/disable specific categories */ -void debug_enable_category(debug_category_t category); -void debug_disable_category(debug_category_t category); -void debug_set_categories(uint32_t categories); - -/* Configure output options */ -void debug_enable_timestamp(int enable); -void debug_enable_function_name(int enable); -void debug_enable_file_line(int enable); -void debug_enable_color(int enable); -void debug_set_rate_limit(size_t max_per_second); -void debug_set_output_file(const char* file_path); - -/* Parse debug configuration from string (e.g., "uasync:debug,ll_queue:info") */ -int debug_parse_config(const char* config_string); - -#include -extern FILE* debug_output_file; - -/* Check if debug output should be shown for given level and category */ -int debug_should_output(debug_level_t level, debug_category_t category); - -/* Get current debug level for a category */ -debug_level_t debug_get_effective_level(debug_category_t category); - -/* Format and output debug message (internal use by macros) */ -void debug_output(debug_level_t level, debug_category_t category, - const char* function, const char* file, int line, - const char* format, ...); - -/* Convenience macros for debug output */ -#define DEBUG_ERROR(category, fmt, ...) \ - do { if (debug_should_output(DEBUG_LEVEL_ERROR, category)) { \ - debug_output(DEBUG_LEVEL_ERROR, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } } while(0) - -#define DEBUG_WARN(category, fmt, ...) \ - do { if (debug_should_output(DEBUG_LEVEL_WARN, category)) { \ - debug_output(DEBUG_LEVEL_WARN, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } } while(0) - -#define DEBUG_INFO(category, fmt, ...) \ - do { if (debug_should_output(DEBUG_LEVEL_INFO, category)) { \ - debug_output(DEBUG_LEVEL_INFO, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } } while(0) - -#define DEBUG_DEBUG(category, fmt, ...) \ - do { if (debug_should_output(DEBUG_LEVEL_DEBUG, category)) { \ - debug_output(DEBUG_LEVEL_DEBUG, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } } while(0) - -#define DEBUG_TRACE(category, fmt, ...) \ - do { if (debug_should_output(DEBUG_LEVEL_TRACE, category)) { \ - debug_output(DEBUG_LEVEL_TRACE, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \ - } } while(0) - -/* Backward compatibility - default to ERROR level if not specified */ -#define DEBUG_OUTPUT(fmt, ...) DEBUG_ERROR(DEBUG_CATEGORY_ALL, fmt, ##__VA_ARGS__) - -/* Apply debug settings from configuration string values */ -void debug_apply_config_values(const char *log_file, const char *debug_level_str, uint32_t debug_categories, - int enable_timestamp, int enable_function_names, int enable_file_lines, int enable_colors, - int cli_override_level, int cli_override_categories); - -#ifdef __cplusplus -} -#endif - -#endif // DEBUG_CONFIG_H diff --git a/lib/ll_queue.c b/lib/ll_queue.c index abb87fef..dce46287 100755 --- a/lib/ll_queue.c +++ b/lib/ll_queue.c @@ -86,8 +86,7 @@ static void queue_resume_timeout_cb(void* arg) { // Вызвать коллбэк если есть элементы и коллбэки разрешены if (q->head && !q->callback_suspended && q->callback) { - void* data = (void*)(q->head + xxx); - q->callback(q, data, q->callback_arg); + q->callback(q, q->callback_arg); } } @@ -229,15 +228,17 @@ int queue_data_put(struct ll_queue* q, void* data, uint32_t id) { q->count++; q->total_bytes += entry->size; // ВАЖНО: НЕ увеличиваем ref_count - элемент просто находится в очереди + + size_t send_q_bytes = queue_total_bytes(q); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "qqqq check total bytes: new_q_len=%d element_size:%d", send_q_bytes, entry->size); add_to_hash(q, entry); - DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put: added entry %p (id=%u), count=%d", - entry, id, q->count); +// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put: added entry %p (id=%u), count=%d", entry, id, q->count); // Если очередь была пуста и коллбэки разрешены - вызвать коллбэк if (q->count == 1 && !q->callback_suspended && q->callback) { - q->callback(q, data, q->callback_arg); + q->callback(q, q->callback_arg); } // Проверить ожидающие коллбэки @@ -274,12 +275,11 @@ int queue_data_put_first(struct ll_queue* q, void* data, uint32_t id) { add_to_hash(q, entry); - DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put_first: added entry %p (id=%u), count=%d", - entry, id, q->count); +// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_put_first: added entry %p (id=%u), count=%d", entry, id, q->count); // Если очередь была пуста и коллбэки разрешены - вызвать коллбэк if (q->count == 1 && !q->callback_suspended && q->callback) { - q->callback(q, data, q->callback_arg); + q->callback(q, q->callback_arg); } // Проверить ожидающие коллбэки @@ -307,8 +307,7 @@ void* queue_data_get(struct ll_queue* q) { // ВАЖНО: НЕ уменьшаем ref_count - просто удаляем из очереди - DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_get: got entry %p (id=%u), count=%d", - entry, entry->id, q->count); +// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_data_get: got entry %p (id=%u), count=%d", entry, entry->id, q->count); // Приостановить коллбэки для предотвращения рекурсии q->callback_suspended = 1; @@ -399,8 +398,7 @@ int queue_remove_data(struct ll_queue* q, void* data) { remove_from_hash(q, entry); - DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_remove_data: removed entry %p (id=%u), count=%d", - entry, entry->id, q->count); + DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_remove_data: removed entry %p (id=%u), count=%d", entry, entry->id, q->count); return 0; } \ No newline at end of file diff --git a/lib/ll_queue.h b/lib/ll_queue.h index 638d2472..e747ee1d 100644 --- a/lib/ll_queue.h +++ b/lib/ll_queue.h @@ -16,7 +16,7 @@ struct UASYNC; // Автозабор элемента из очереди. вызывается когда в очереди что-то есть и коллбэк не занят обработкой. // Параметры: указатель на очередь, указатель на структуру элемента (struct ll_entry*), пользовательский аргумент // Когда коллбэк закончит обрабатывать элемент он должен вызвать queue_resume_callback - сообщить о готовности получить следующий элемент (приём очередным вызовом коллбэка в следующем цикле mainloop). -typedef void (*queue_callback_fn)(struct ll_queue* q, void* data, void* arg); +typedef void (*queue_callback_fn)(struct ll_queue* q, void* arg); // Структура элемента - переменный размер, данные расположены сразу после структуры struct ll_entry { diff --git a/lib/timeout_heap.c1 b/lib/timeout_heap.c1 deleted file mode 100755 index 25213ce5..00000000 --- a/lib/timeout_heap.c1 +++ /dev/null @@ -1,131 +0,0 @@ -// timeout_heap.c - -#include "timeout_heap.h" -#include "debug_config.h" -#include -#include // For potential error printing, optional - -// Helper macros for 1-based indices -#define PARENT(i) ((i) / 2) -#define LEFT_CHILD(i) (2 * (i)) -#define RIGHT_CHILD(i) (2 * (i) + 1) - -TimeoutHeap *timeout_heap_create(size_t initial_capacity) { - TimeoutHeap *h = malloc(sizeof(TimeoutHeap)); - if (!h) return NULL; - h->heap = malloc(sizeof(TimeoutEntry) * initial_capacity); - if (!h->heap) { - free(h); - return NULL; - } - h->size = 0; - h->capacity = initial_capacity; - return h; -} - -void timeout_heap_destroy(TimeoutHeap *h) { - if (h) { - free(h->heap); - free(h); - } -} - -static void bubble_up(TimeoutHeap *h, size_t i) { - // i is 1-based - while (i > 1 && h->heap[PARENT(i) - 1].expiration > h->heap[i - 1].expiration) { - // Swap with parent - TimeoutEntry temp = h->heap[PARENT(i) - 1]; - h->heap[PARENT(i) - 1] = h->heap[i - 1]; - h->heap[i - 1] = temp; - i = PARENT(i); - } -} - -int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data) { - if (h->size == h->capacity) { - size_t new_cap = h->capacity ? h->capacity * 2 : 1; - TimeoutEntry *new_heap = realloc(h->heap, sizeof(TimeoutEntry) * new_cap); - if (!new_heap) return -1; // Allocation failed - h->heap = new_heap; - h->capacity = new_cap; - } - - // Insert at end (0-based) - size_t idx = h->size++; - h->heap[idx].expiration = expiration; - h->heap[idx].data = data; - - // Bubble up (1-based) - bubble_up(h, idx + 1); - return 0; -} - -static void heapify_down(TimeoutHeap *h, size_t i) { - // i is 1-based - while (1) { - size_t smallest = i; - size_t left = LEFT_CHILD(i); - size_t right = RIGHT_CHILD(i); - - if (left <= h->size && h->heap[left - 1].expiration < h->heap[smallest - 1].expiration) { - smallest = left; - } - if (right <= h->size && h->heap[right - 1].expiration < h->heap[smallest - 1].expiration) { - smallest = right; - } - if (smallest == i) break; - - // Swap - TimeoutEntry temp = h->heap[smallest - 1]; - h->heap[smallest - 1] = h->heap[i - 1]; - h->heap[i - 1] = temp; - i = smallest; - } -} - -static void remove_root(TimeoutHeap *h) { - if (h->size == 0) return; - - // Move last to root - h->heap[0] = h->heap[--h->size]; - - // Heapify down (1-based) - if (h->size > 0) { - heapify_down(h, 1); - } -} - -int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out) { - if (h->size == 0) return -1; - *out = h->heap[0]; - return 0; -} - -int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out) { - if (h->size == 0) return -1; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "timeout_heap_pop: entering, size=%zu", h->size); - - *out = h->heap[0]; - remove_root(h); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "timeout_heap_pop: returning element, data=%p", out->data); - return 0; -} - -int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data) { - // Linear search for the entry - for (size_t i = 0; i < h->size; ++i) { - if (h->heap[i].expiration == expiration && h->heap[i].data == data) { - // Remove by swapping with last and heapify down - if (i != h->size - 1) { - h->heap[i] = h->heap[h->size - 1]; - // Heapify down from the position (1-based) - heapify_down(h, i + 1); - } - h->size--; - return 0; - } - } - return -1; // Not found -} diff --git a/lib/timeout_heap.c2 b/lib/timeout_heap.c2 deleted file mode 100755 index d2f3da5c..00000000 --- a/lib/timeout_heap.c2 +++ /dev/null @@ -1,170 +0,0 @@ -// Updated timeout_heap.c - -#include "timeout_heap.h" -#include "debug_config.h" -#include -#include // For potential error printing, optional -#include // For SIZE_MAX - -// Helper macros for 1-based indices -#define PARENT(i) ((i) / 2) -#define LEFT_CHILD(i) (2 * (i)) -#define RIGHT_CHILD(i) (2 * (i) + 1) - -// Forward declaration of timeout_node (from u_async.h, but included here for type safety) -struct timeout_node; - -// Private helpers -static void update_index(void *data, size_t idx) { - if (data) { - ((struct timeout_node *)data)->heap_index = idx; - } -} - -static void bubble_up(TimeoutHeap *h, size_t i) { - // i is 1-based - while (i > 1 && h->heap[PARENT(i) - 1].expiration > h->heap[i - 1].expiration) { - // Swap with parent - TimeoutEntry temp = h->heap[PARENT(i) - 1]; - h->heap[PARENT(i) - 1] = h->heap[i - 1]; - h->heap[i - 1] = temp; - - // Update indices - update_index(h->heap[PARENT(i) - 1].data, PARENT(i) - 1); - update_index(h->heap[i - 1].data, i - 1); - - i = PARENT(i); - } -} - -static void heapify_down(TimeoutHeap *h, size_t i) { - // i is 1-based - while (1) { - size_t smallest = i; - size_t left = LEFT_CHILD(i); - size_t right = RIGHT_CHILD(i); - - if (left <= h->size && h->heap[left - 1].expiration < h->heap[smallest - 1].expiration) { - smallest = left; - } - if (right <= h->size && h->heap[right - 1].expiration < h->heap[smallest - 1].expiration) { - smallest = right; - } - if (smallest == i) break; - - // Swap - TimeoutEntry temp = h->heap[smallest - 1]; - h->heap[smallest - 1] = h->heap[i - 1]; - h->heap[i - 1] = temp; - - // Update indices - update_index(h->heap[smallest - 1].data, smallest - 1); - update_index(h->heap[i - 1].data, i - 1); - - i = smallest; - } -} - -TimeoutHeap *timeout_heap_create(size_t initial_capacity) { - TimeoutHeap *h = malloc(sizeof(TimeoutHeap)); - if (!h) return NULL; - h->heap = malloc(sizeof(TimeoutEntry) * initial_capacity); - if (!h->heap) { - free(h); - return NULL; - } - h->size = 0; - h->capacity = initial_capacity; - return h; -} - -void timeout_heap_destroy(TimeoutHeap *h) { - if (h) { - free(h->heap); - free(h); - } -} - -int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data) { - if (h->size == h->capacity) { - size_t new_cap = h->capacity ? h->capacity * 2 : 1; - TimeoutEntry *new_heap = realloc(h->heap, sizeof(TimeoutEntry) * new_cap); - if (!new_heap) return -1; // Allocation failed - h->heap = new_heap; - h->capacity = new_cap; - } - - // Insert at end (0-based) - size_t idx = h->size++; - h->heap[idx].expiration = expiration; - h->heap[idx].data = data; - - // Set initial index - update_index(data, idx); - - // Bubble up (1-based) - bubble_up(h, idx + 1); - return 0; -} - -static void remove_root(TimeoutHeap *h) { - if (h->size == 0) return; - - // Move last to root - size_t last = --h->size; - h->heap[0] = h->heap[last]; - - // Update index for new root - update_index(h->heap[0].data, 0); - - // Heapify down (1-based) - if (h->size > 0) { - heapify_down(h, 1); - } -} - -int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out) { - if (h->size == 0) return -1; - *out = h->heap[0]; - return 0; -} - -int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out) { - if (h->size == 0) return -1; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "timeout_heap_pop: entering, size=%zu", h->size); - - *out = h->heap[0]; - remove_root(h); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "timeout_heap_pop: returning element, data=%p", out->data); - return 0; -} - -int timeout_heap_remove(TimeoutHeap *h, void *data) { - struct timeout_node *node = (struct timeout_node *)data; - size_t idx = node->heap_index; - if (idx == SIZE_MAX || idx >= h->size || h->heap[idx].data != data) { - return -1; // Not found or invalid - } - - // Swap with last - size_t last = --h->size; - if (idx != last) { - TimeoutEntry temp = h->heap[idx]; - h->heap[idx] = h->heap[last]; - h->heap[last] = temp; - - // Update indices - update_index(h->heap[idx].data, idx); - update_index(h->heap[last].data, last); - - // Heapify down and up to restore property - heapify_down(h, idx + 1); - bubble_up(h, idx + 1); - } - - // Invalidate index - node->heap_index = SIZE_MAX; - return 0; -} \ No newline at end of file diff --git a/lib/timeout_heap.h1 b/lib/timeout_heap.h1 deleted file mode 100755 index 26ff7fa6..00000000 --- a/lib/timeout_heap.h1 +++ /dev/null @@ -1,75 +0,0 @@ -// timeout_heap.h - -#ifndef TIMEOUT_HEAP_H -#define TIMEOUT_HEAP_H - -#include // For uint64_t -#include // For size_t - -typedef uint64_t TimeoutTime; // e.g., milliseconds since epoch or from now - -typedef struct { - TimeoutTime expiration; // Sort key (smaller = earlier) - void *data; // User data (e.g., callback or ID) -} TimeoutEntry; // Removed deleted flag - -typedef struct TimeoutHeap TimeoutHeap; - -struct TimeoutHeap { - TimeoutEntry *heap; // Dynamic array - size_t size; // Current number of elements - size_t capacity; // Allocated size - // Removed freed_count and free_callback -}; - -/** - * Create a new timeout heap with initial capacity. - * @param initial_capacity Starting capacity (will grow as needed). - * @return Pointer to the heap, or NULL on failure. - */ -TimeoutHeap *timeout_heap_create(size_t initial_capacity); - -/** - * Destroy the timeout heap and free resources (but not the data pointers). - * @param h The heap to destroy. - */ -void timeout_heap_destroy(TimeoutHeap *h); - -/** - * Insert a new timeout into the heap. - * @param h The heap. - * @param expiration The expiration time. - * @param data User data associated with the timeout. - * @return 0 on success, -1 on allocation failure. - */ -int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data); - -/** - * Peek at the earliest timeout without removing it. - * @param h The heap. - * @param out Where to store the entry. - * @return 0 on success, -1 if empty. - */ -int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out); - -/** - * Pop the earliest timeout from the heap. - * @param h The heap. - * @param out Where to store the entry. - * @return 0 on success, -1 if empty. - */ -int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out); - -/** - * Remove a timeout by matching expiration and data (immediate removal). - * Scans the heap linearly, O(n) time. - * Assumes combinations are unique; removes the first match. - * Does not free the data — caller should do it if success. - * @param h The heap. - * @param expiration The expiration time to match. - * @param data The data to match. - * @return 0 if found and removed, -1 if not found. - */ -int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data); - -#endif // TIMEOUT_HEAP_H diff --git a/lib/u_async.c b/lib/u_async.c index c8790463..67c13d65 100644 --- a/lib/u_async.c +++ b/lib/u_async.c @@ -394,6 +394,8 @@ void uasync_mainloop(struct UASYNC* ua) { // Instance version void uasync_poll(struct UASYNC* ua, int timeout_tb) { if (!ua) return; + + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "async: mainloop: event waiting"); /* Process expired timeouts */ process_timeouts(ua); diff --git a/lib/u_async.c.backup2 b/lib/u_async.c.backup2 deleted file mode 100644 index d0fdb876..00000000 --- a/lib/u_async.c.backup2 +++ /dev/null @@ -1,834 +0,0 @@ -// uasync.c - -#include "u_async.h" -#include "debug_config.h" -#include -#include -#include -#include -#include -#include -#include -#include - - - -// Timeout node with safe cancellation -struct timeout_node { - void* arg; - timeout_callback_t callback; - uint64_t expiration_ms; // absolute expiration time in milliseconds - struct UASYNC* ua; // Pointer back to uasync instance for counter updates - int cancelled; // Cancellation flag -}; - -// Socket node with array-based storage -struct socket_node { - int fd; - socket_callback_t read_cbk; - socket_callback_t write_cbk; - socket_callback_t except_cbk; - void* user_data; - int active; // 1 if socket is active, 0 if freed (for reuse) -}; - -// Array-based socket management for O(1) operations -struct socket_array { - struct socket_node* sockets; // Dynamic array of socket nodes - int* fd_to_index; // FD to array index mapping - int* index_to_fd; // Array index to FD mapping - int capacity; // Total allocated capacity - int count; // Number of active sockets - int max_fd; // Maximum FD for bounds checking -}; - -static struct socket_array* socket_array_create(int initial_capacity); -static void socket_array_destroy(struct socket_array* sa); -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data); -static int socket_array_remove(struct socket_array* sa, int fd); -static struct socket_node* socket_array_get(struct socket_array* sa, int fd); -static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds, int max_fds); - -// No global instance - each module must use its own struct UASYNC instance - -// Array-based socket management implementation -static struct socket_array* socket_array_create(int initial_capacity) { - if (initial_capacity < 4) initial_capacity = 4; // Minimum capacity - - struct socket_array* sa = malloc(sizeof(struct socket_array)); - if (!sa) return NULL; - - sa->sockets = calloc(initial_capacity, sizeof(struct socket_node)); - sa->fd_to_index = calloc(initial_capacity, sizeof(int)); - sa->index_to_fd = calloc(initial_capacity, sizeof(int)); - - if (!sa->sockets || !sa->fd_to_index || !sa->index_to_fd) { - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); - return NULL; - } - - // Initialize mapping arrays to -1 (invalid) - for (int i = 0; i < initial_capacity; i++) { - sa->fd_to_index[i] = -1; - sa->index_to_fd[i] = -1; - sa->sockets[i].fd = -1; - sa->sockets[i].active = 0; - } - - sa->capacity = initial_capacity; - sa->count = 0; - sa->max_fd = -1; - - return sa; -} - -static void socket_array_destroy(struct socket_array* sa) { - if (!sa) return; - - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); -} - -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!sa || fd < 0 || fd >= FD_SETSIZE) return -1; - if (fd >= sa->capacity) { - // Need to resize - double the capacity - int new_capacity = sa->capacity * 2; - if (fd >= new_capacity) new_capacity = fd + 16; // Ensure enough space - - struct socket_node* new_sockets = realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); - int* new_fd_to_index = realloc(sa->fd_to_index, new_capacity * sizeof(int)); - int* new_index_to_fd = realloc(sa->index_to_fd, new_capacity * sizeof(int)); - - if (!new_sockets || !new_fd_to_index || !new_index_to_fd) { - // Allocation failed - free(new_sockets); - free(new_fd_to_index); - free(new_index_to_fd); - return -1; - } - - // Initialize new elements - for (int i = sa->capacity; i < new_capacity; i++) { - new_fd_to_index[i] = -1; - new_index_to_fd[i] = -1; - new_sockets[i].fd = -1; - new_sockets[i].active = 0; - } - - sa->sockets = new_sockets; - sa->fd_to_index = new_fd_to_index; - sa->index_to_fd = new_index_to_fd; - sa->capacity = new_capacity; - } - - // Check if FD already exists - if (sa->fd_to_index[fd] != -1) return -1; // FD already exists - - // Find first free slot - int index = -1; - for (int i = 0; i < sa->capacity; i++) { - if (!sa->sockets[i].active) { - index = i; - break; - } - } - - if (index == -1) return -1; // No free slots (shouldn't happen) - - // Add the socket - sa->sockets[index].fd = fd; - sa->sockets[index].read_cbk = read_cbk; - sa->sockets[index].write_cbk = write_cbk; - sa->sockets[index].except_cbk = except_cbk; - sa->sockets[index].user_data = user_data; - sa->sockets[index].active = 1; - - sa->fd_to_index[fd] = index; - sa->index_to_fd[index] = fd; - sa->count++; - - if (fd > sa->max_fd) sa->max_fd = fd; - - return index; -} - -static int socket_array_remove(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return -1; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return -1; // FD not found - - // Mark as inactive - sa->sockets[index].active = 0; - sa->sockets[index].fd = -1; - sa->fd_to_index[fd] = -1; - sa->index_to_fd[index] = -1; - sa->count--; - - return 0; -} - -static struct socket_node* socket_array_get(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return NULL; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return NULL; - - return &sa->sockets[index]; -} - -static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds, int max_fds) { - if (!sa || !fds || max_fds <= 0) return 0; - - int count = 0; - for (int i = 0; i < sa->capacity && count < max_fds; i++) { - if (sa->sockets[i].active) { - fds[count].fd = sa->sockets[i].fd; - fds[count].events = 0; - if (sa->sockets[i].read_cbk) fds[count].events |= POLLIN; - if (sa->sockets[i].write_cbk) fds[count].events |= POLLOUT; - if (sa->sockets[i].except_cbk) fds[count].events |= POLLERR; - fds[count].revents = 0; - count++; - } - } - - return count; -} - -// Callback to free timeout node and update counters -static void timeout_node_free_callback(void* user_data, void* data) { - struct UASYNC* ua = (struct UASYNC*)user_data; - struct timeout_node* node = (struct timeout_node*)data; - (void)node; // Not used directly, but keep for consistency - ua->timer_free_count++; - free(data); -} - -// Helper to get current time -static void get_current_time(struct timeval* tv) { - gettimeofday(tv, NULL); -} - - - -// Drain wakeup pipe - read all available bytes -static void drain_wakeup_pipe(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return; - - char buf[64]; - while (1) { - ssize_t n = read(ua->wakeup_pipe[0], buf, sizeof(buf)); - if (n <= 0) break; - } -} - -// Helper to add timeval: tv += dt (timebase units) -static void timeval_add_tb(struct timeval* tv, int dt) { - tv->tv_usec += (dt % 10000) * 100; - tv->tv_sec += dt / 10000 + tv->tv_usec / 1000000; - tv->tv_usec %= 1000000; -} - -// Convert timeval to milliseconds (uint64_t) -static uint64_t timeval_to_ms(const struct timeval* tv) { - return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL; -} - - - -// Simplified timeout handling without reference counting - -// Process expired timeouts with safe cancellation -static void process_timeouts(struct UASYNC* ua) { - if (!ua || !ua->timeout_heap) return; - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - while (1) { - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) break; - if (entry.expiration > now_ms) break; - - // Pop the expired timeout - timeout_heap_pop(ua->timeout_heap, &entry); - struct timeout_node* node = (struct timeout_node*)entry.data; - - if (node && node->callback && !node->cancelled) { - // Execute callback only if not cancelled - node->callback(node->arg); - } - - // Always free the node after processing - if (node && node->ua) { - node->ua->timer_free_count++; - } - free(node); - } -} - -// Compute time to next timeout -static void get_next_timeout(struct UASYNC* ua, struct timeval* tv) { - if (!ua || !ua->timeout_heap) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - if (entry.expiration <= now_ms) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - uint64_t delta_ms = entry.expiration - now_ms; - if (delta_ms > 86400000) { // Cap at 1 day to avoid overflow - delta_ms = 86400000; - } - tv->tv_sec = delta_ms / 1000; - tv->tv_usec = (delta_ms % 1000) * 1000; -} - - - -// Instance version -void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_callback_t callback) { - if (!ua || timeout_tb < 0 || !callback) return NULL; - if (!ua->timeout_heap) return NULL; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: ua=%p, timeout=%d tb, arg=%p, callback=%p", - ua, timeout_tb, arg, callback); - - struct timeout_node* node = malloc(sizeof(struct timeout_node)); - if (!node) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); - return NULL; - } - ua->timer_alloc_count++; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: allocated node %p (alloc_count=%zu)", - node, ua->timer_alloc_count); - - node->arg = arg; - node->callback = callback; - node->ua = ua; - node->cancelled = 0; - - // Calculate expiration time in milliseconds - struct timeval now; - get_current_time(&now); - timeval_add_tb(&now, timeout_tb); - node->expiration_ms = timeval_to_ms(&now); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: node %p expires at %llu ms", - node, (unsigned long long)node->expiration_ms); - - // Add to heap - if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); - free(node); - ua->timer_free_count++; // Balance the alloc counter - return NULL; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: successfully created timer %p", node); - return node; -} - - - -// Instance version -err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) { - if (!ua || !t_id || !ua->timeout_heap) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: invalid parameters ua=%p, t_id=%p, heap=%p", - ua, t_id, ua ? ua->timeout_heap : NULL); - return ERR_FAIL; - } - - struct timeout_node* node = (struct timeout_node*)t_id; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: ua=%p, t_id=%p, node=%p, expires=%llu ms", - ua, t_id, node, (unsigned long long)node->expiration_ms); - - // Try to cancel from heap first - if (timeout_heap_cancel(ua->timeout_heap, node->expiration_ms, node) == 0) { - // Successfully removed from heap - mark as cancelled - node->cancelled = 1; - node->callback = NULL; - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: successfully cancelled timer %p from heap", node); - return ERR_OK; - - // If not found in heap (maybe already expired and being processed) - // We still need to mark it as cancelled to prevent callback execution - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: timer %p not found in heap, marking as cancelled", node); - node->cancelled = 1; - node->callback = NULL; - return ERR_OK; -} // Successfully cancelled (marked) -} - - - -// Instance version -void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!ua || fd < 0 || fd >= FD_SETSIZE) return NULL; // Bounds check - - int index = socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, user_data); - if (index < 0) return NULL; - - ua->socket_alloc_count++; - - // Return pointer to the socket node (same as before for API compatibility) - return &ua->sockets->sockets[index]; -} - - - -// Instance version -err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { - if (!ua || !s_id) return ERR_FAIL; - - struct socket_node* node = (struct socket_node*)s_id; - if (node->fd < 0) return ERR_FAIL; // Invalid node - - int result = socket_array_remove(ua->sockets, node->fd); - if (result != 0) return ERR_FAIL; - - ua->socket_free_count++; - return ERR_OK; -} - - - -void uasync_mainloop(struct UASYNC* ua) { - while (1) { - uasync_poll(ua, -1); /* infinite timeout */ - } -} - -// Instance version -void uasync_poll(struct UASYNC* ua, int timeout_tb) { - if (!ua) return; - - /* Process expired timeouts */ - process_timeouts(ua); - - /* Compute timeout for poll in milliseconds */ - int timeout_ms = -1; // infinite by default - - // Get next timeout from heap - struct timeval tv; - get_next_timeout(ua, &tv); - - if (tv.tv_sec > 0 || tv.tv_usec > 0 || (ua->timeout_heap && ua->timeout_heap->size > 0)) { - // Convert timeval to milliseconds, cap at INT_MAX - uint64_t ms = (uint64_t)tv.tv_sec * 1000ULL + (uint64_t)tv.tv_usec / 1000ULL; - if (ms > INT_MAX) ms = INT_MAX; - timeout_ms = (int)ms; - } - - /* If timeout_tb >= 0, compute timeout as min(timeout_tb, existing timer) */ - if (timeout_tb >= 0) { - // Convert timebase (0.1 ms) to milliseconds - int user_timeout_ms = timeout_tb / 10; - if (timeout_tb % 10 != 0) user_timeout_ms++; // round up - - if (timeout_ms < 0 || user_timeout_ms < timeout_ms) { - timeout_ms = user_timeout_ms; - } - } - - /* Build pollfd array from socket array - O(1) per socket */ - int socket_count = ua->sockets ? ua->sockets->count : 0; - int wakeup_fd_present = ua->wakeup_initialized ? 1 : 0; - int total_fds = socket_count + wakeup_fd_present; - - if (total_fds == 0) { - /* No sockets and no wakeup fd, just wait for timeout */ - if (timeout_ms >= 0) { - /* usleep would be better but we just call poll with empty set */ - struct pollfd dummy; - poll(&dummy, 0, timeout_ms); - } else { - /* Infinite timeout with no sockets - should not happen in practice */ - return; - } - /* Check timeouts again after sleep */ - process_timeouts(ua); - return; - } - - struct pollfd* fds = malloc(total_fds * sizeof(struct pollfd)); - struct socket_node** nodes = NULL; - if (socket_count > 0) { - nodes = malloc(socket_count * sizeof(struct socket_node*)); - } - if (!fds || (socket_count > 0 && !nodes)) { - free(fds); - free(nodes); - return; /* out of memory */ - } - - /* Fill arrays */ - int idx = 0; - - /* Add wakeup fd first if present */ - if (wakeup_fd_present) { - fds[idx].fd = ua->wakeup_pipe[0]; - fds[idx].events = POLLIN; - fds[idx].revents = 0; - idx++; - } - - /* Add socket fds using efficient array traversal */ - int node_idx = 0; - for (int i = 0; i < ua->sockets->capacity && node_idx < socket_count; i++) { - if (ua->sockets->sockets[i].active) { - struct socket_node* cur = &ua->sockets->sockets[i]; - fds[idx].fd = cur->fd; - fds[idx].events = 0; - fds[idx].revents = 0; - - if (cur->read_cbk) fds[idx].events |= POLLIN; - if (cur->write_cbk) fds[idx].events |= POLLOUT; - if (cur->except_cbk) fds[idx].events |= POLLPRI; - - if (nodes) { - nodes[node_idx] = cur; - } - idx++; - node_idx++; - } - } - - /* Call poll */ - int ret = poll(fds, total_fds, timeout_ms); - if (ret < 0) { - if (errno == EINTR) { - free(fds); - free(nodes); - return; - } - perror("poll"); - free(fds); - free(nodes); - return; - } - - /* Process timeouts that may have expired during poll */ - process_timeouts(ua); - - /* Process socket events */ - if (ret > 0) { - for (int i = 0; i < total_fds; i++) { - if (fds[i].revents == 0) continue; - - /* Handle wakeup fd separately */ - if (wakeup_fd_present && i == 0) { - if (fds[i].revents & POLLIN) { - drain_wakeup_pipe(ua); - } - continue; - } - - /* Socket event */ - int socket_idx = i - wakeup_fd_present; - struct socket_node* node = nodes[socket_idx]; - - /* Check for error conditions first */ - if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { - /* Treat as exceptional condition */ - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Exceptional data (out-of-band) */ - if (fds[i].revents & POLLPRI) { - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Read readiness */ - if (fds[i].revents & POLLIN) { - if (node->read_cbk) { - node->read_cbk(node->fd, node->user_data); - } - } - - /* Write readiness */ - if (fds[i].revents & POLLOUT) { - if (node->write_cbk) { - node->write_cbk(node->fd, node->user_data); - } - } - } - } - - free(fds); - free(nodes); -} - - - -// ========== Instance management functions ========== - -struct UASYNC* uasync_create(void) { - // Initialize debug system on first use - static int debug_initialized = 0; - if (!debug_initialized) { - debug_config_init(); - debug_initialized = 1; - } - - struct UASYNC* ua = malloc(sizeof(struct UASYNC)); - if (!ua) return NULL; - - memset(ua, 0, sizeof(struct UASYNC)); - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - ua->wakeup_initialized = 0; - - // Create wakeup pipe - if (pipe(ua->wakeup_pipe) < 0) { - DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno)); - // Continue without wakeup mechanism - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - } else { - ua->wakeup_initialized = 1; - // Set non-blocking on read end to avoid blocking if pipe is full - int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0); - if (flags >= 0) { - fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK); - } - } - - ua->sockets = socket_array_create(16); - if (!ua->sockets) { - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - ua->timeout_heap = timeout_heap_create(16); - if (!ua->timeout_heap) { - socket_array_destroy(ua->sockets); - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - // Set callback to free timeout nodes and update counters - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - - return ua; -} - -// Print all resources for debugging -void uasync_print_resources(struct UASYNC* ua, const char* prefix) { - if (!ua) { - printf("%s: NULL uasync instance\n", prefix); - return; - } - - printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); - printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - - // Показать активные таймеры - if (ua->timeout_heap) { - size_t active_timers = 0; - TimeoutEntry entry; - // Создаем временную копию кучи для подсчета - TimeoutHeap* temp_heap = timeout_heap_create(16); - if (temp_heap) { - // Копируем все активные таймеры - while (timeout_heap_pop(ua->timeout_heap, &entry) == 0) { - if (!entry.deleted) { - active_timers++; - struct timeout_node* node = (struct timeout_node*)entry.data; - printf(" Timer: node=%p, expires=%llu ms, cancelled=%d\n", - node, (unsigned long long)entry.expiration, node->cancelled); - } - timeout_heap_push(temp_heap, entry.expiration, entry.data); - } - // Возвращаем таймеры обратно - while (timeout_heap_pop(temp_heap, &entry) == 0) { - timeout_heap_push(ua->timeout_heap, entry.expiration, entry.data); - } - timeout_heap_destroy(temp_heap); - } - printf(" Active timers in heap: %zu\n", active_timers); - } - - // Показать активные сокеты - if (ua->sockets) { - int active_sockets = 0; - printf(" Socket array capacity: %d, active: %d\n", - ua->sockets->capacity, ua->sockets->count); - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - active_sockets++; - printf(" Socket: fd=%d, active=%d\n", - ua->sockets->sockets[i].fd, - ua->sockets->sockets[i].active); - } - } - printf(" Total active sockets: %d\n", active_sockets); - } - - printf("🔚 %s: End of resource report\n\n", prefix); -} - -void uasync_destroy(struct UASYNC* ua) { - if (!ua) return; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); - - // Диагностика ресурсов перед очисткой - uasync_print_resources(ua, "BEFORE_DESTROY"); - - // Check for potential memory leaks - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected before cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - // Continue cleanup, will abort after if leaks remain - } - - // Free all remaining timeouts - if (ua->timeout_heap) { - size_t freed_count = 0; - while (1) { - TimeoutEntry entry; - if (timeout_heap_pop(ua->timeout_heap, &entry) != 0) break; - struct timeout_node* node = (struct timeout_node*)entry.data; - DEBUG_TRACE(DEBUG_CATEGORY_TIMERS, "uasync_destroy: freeing timer node %p (expired=%llu ms)", - node, (unsigned long long)entry.expiration); - ua->timer_free_count++; - freed_count++; - free(node); - } - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: freed %zu timer nodes in destroy, heap freed_count = %zu", - freed_count, ua->timeout_heap->freed_count); - timeout_heap_destroy(ua->timeout_heap); - } - - // Free all socket nodes using array approach - if (ua->sockets) { - // Count and free all active sockets - int freed_count = 0; - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - ua->socket_free_count++; - freed_count++; - } - } - DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); - socket_array_destroy(ua->sockets); - } - - // Close wakeup pipe - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - - // Final leak check - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - abort(); - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua); - free(ua); -} - -void uasync_init_instance(struct UASYNC* ua) { - if (!ua) return; - - // Initialize socket array if not present - if (!ua->sockets) { - ua->sockets = socket_array_create(16); - } - - if (!ua->timeout_heap) { - ua->timeout_heap = timeout_heap_create(16); - if (ua->timeout_heap) { - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - } - } -} - -// Debug statistics -void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { - if (!ua) return; - if (timer_alloc) *timer_alloc = ua->timer_alloc_count; - if (timer_free) *timer_free = ua->timer_free_count; - if (socket_alloc) *socket_alloc = ua->socket_alloc_count; - if (socket_free) *socket_free = ua->socket_free_count; -} - -// Get global instance for backward compatibility - -// Wakeup mechanism -int uasync_wakeup(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - - char byte = 0; - ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1); - if (ret != 1) { - // Don't print error from signal handler - return -1; - } - return 0; -} - -int uasync_get_wakeup_fd(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - return ua->wakeup_pipe[1]; -} - diff --git a/lib/u_async.c1 b/lib/u_async.c1 deleted file mode 100755 index 3b4bfb2e..00000000 --- a/lib/u_async.c1 +++ /dev/null @@ -1,830 +0,0 @@ -// uasync.c - -#include "u_async.h" -#include "debug_config.h" -#include -#include -#include -#include -#include -#include -#include -#include - - - -// Timeout node with safe cancellation -struct timeout_node { - void* arg; - timeout_callback_t callback; - uint64_t expiration_ms; // absolute expiration time in milliseconds - struct UASYNC* ua; // Pointer back to uasync instance for counter updates - int cancelled; // Cancellation flag -}; - -// Socket node with array-based storage -struct socket_node { - int fd; - socket_callback_t read_cbk; - socket_callback_t write_cbk; - socket_callback_t except_cbk; - void* user_data; - int active; // 1 if socket is active, 0 if freed (for reuse) -}; - -// Array-based socket management for O(1) operations -struct socket_array { - struct socket_node* sockets; // Dynamic array of socket nodes - int* fd_to_index; // FD to array index mapping - int* index_to_fd; // Array index to FD mapping - int capacity; // Total allocated capacity - int count; // Number of active sockets - int max_fd; // Maximum FD for bounds checking -}; - -static struct socket_array* socket_array_create(int initial_capacity); -static void socket_array_destroy(struct socket_array* sa); -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data); -static int socket_array_remove(struct socket_array* sa, int fd); -static struct socket_node* socket_array_get(struct socket_array* sa, int fd); -static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds, int max_fds); - -// No global instance - each module must use its own struct UASYNC instance - -// Array-based socket management implementation -static struct socket_array* socket_array_create(int initial_capacity) { - if (initial_capacity < 4) initial_capacity = 4; // Minimum capacity - - struct socket_array* sa = malloc(sizeof(struct socket_array)); - if (!sa) return NULL; - - sa->sockets = calloc(initial_capacity, sizeof(struct socket_node)); - sa->fd_to_index = calloc(initial_capacity, sizeof(int)); - sa->index_to_fd = calloc(initial_capacity, sizeof(int)); - - if (!sa->sockets || !sa->fd_to_index || !sa->index_to_fd) { - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); - return NULL; - } - - // Initialize mapping arrays to -1 (invalid) - for (int i = 0; i < initial_capacity; i++) { - sa->fd_to_index[i] = -1; - sa->index_to_fd[i] = -1; - sa->sockets[i].fd = -1; - sa->sockets[i].active = 0; - } - - sa->capacity = initial_capacity; - sa->count = 0; - sa->max_fd = -1; - - return sa; -} - -static void socket_array_destroy(struct socket_array* sa) { - if (!sa) return; - - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); -} - -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!sa || fd < 0 || fd >= FD_SETSIZE) return -1; - if (fd >= sa->capacity) { - // Need to resize - double the capacity - int new_capacity = sa->capacity * 2; - if (fd >= new_capacity) new_capacity = fd + 16; // Ensure enough space - - struct socket_node* new_sockets = realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); - int* new_fd_to_index = realloc(sa->fd_to_index, new_capacity * sizeof(int)); - int* new_index_to_fd = realloc(sa->index_to_fd, new_capacity * sizeof(int)); - - if (!new_sockets || !new_fd_to_index || !new_index_to_fd) { - // Allocation failed - free(new_sockets); - free(new_fd_to_index); - free(new_index_to_fd); - return -1; - } - - // Initialize new elements - for (int i = sa->capacity; i < new_capacity; i++) { - new_fd_to_index[i] = -1; - new_index_to_fd[i] = -1; - new_sockets[i].fd = -1; - new_sockets[i].active = 0; - } - - sa->sockets = new_sockets; - sa->fd_to_index = new_fd_to_index; - sa->index_to_fd = new_index_to_fd; - sa->capacity = new_capacity; - } - - // Check if FD already exists - if (sa->fd_to_index[fd] != -1) return -1; // FD already exists - - // Find first free slot - int index = -1; - for (int i = 0; i < sa->capacity; i++) { - if (!sa->sockets[i].active) { - index = i; - break; - } - } - - if (index == -1) return -1; // No free slots (shouldn't happen) - - // Add the socket - sa->sockets[index].fd = fd; - sa->sockets[index].read_cbk = read_cbk; - sa->sockets[index].write_cbk = write_cbk; - sa->sockets[index].except_cbk = except_cbk; - sa->sockets[index].user_data = user_data; - sa->sockets[index].active = 1; - - sa->fd_to_index[fd] = index; - sa->index_to_fd[index] = fd; - sa->count++; - - if (fd > sa->max_fd) sa->max_fd = fd; - - return index; -} - -static int socket_array_remove(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return -1; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return -1; // FD not found - - // Mark as inactive - sa->sockets[index].active = 0; - sa->sockets[index].fd = -1; - sa->fd_to_index[fd] = -1; - sa->index_to_fd[index] = -1; - sa->count--; - - return 0; -} - -static struct socket_node* socket_array_get(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return NULL; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return NULL; - - return &sa->sockets[index]; -} - -static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds, int max_fds) { - if (!sa || !fds || max_fds <= 0) return 0; - - int count = 0; - for (int i = 0; i < sa->capacity && count < max_fds; i++) { - if (sa->sockets[i].active) { - fds[count].fd = sa->sockets[i].fd; - fds[count].events = 0; - if (sa->sockets[i].read_cbk) fds[count].events |= POLLIN; - if (sa->sockets[i].write_cbk) fds[count].events |= POLLOUT; - if (sa->sockets[i].except_cbk) fds[count].events |= POLLERR; - fds[count].revents = 0; - count++; - } - } - - return count; -} - -// Callback to free timeout node and update counters -static void timeout_node_free_callback(void* user_data, void* data) { - struct UASYNC* ua = (struct UASYNC*)user_data; - struct timeout_node* node = (struct timeout_node*)data; - (void)node; // Not used directly, but keep for consistency - ua->timer_free_count++; - free(data); -} - -// Helper to get current time -static void get_current_time(struct timeval* tv) { - gettimeofday(tv, NULL); -} - - - -// Drain wakeup pipe - read all available bytes -static void drain_wakeup_pipe(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return; - - char buf[64]; - while (1) { - ssize_t n = read(ua->wakeup_pipe[0], buf, sizeof(buf)); - if (n <= 0) break; - } -} - -// Helper to add timeval: tv += dt (timebase units) -static void timeval_add_tb(struct timeval* tv, int dt) { - tv->tv_usec += (dt % 10000) * 100; - tv->tv_sec += dt / 10000 + tv->tv_usec / 1000000; - tv->tv_usec %= 1000000; -} - -// Convert timeval to milliseconds (uint64_t) -static uint64_t timeval_to_ms(const struct timeval* tv) { - return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL; -} - - - -// Simplified timeout handling without reference counting - -// Process expired timeouts with safe cancellation -static void process_timeouts(struct UASYNC* ua) { - if (!ua || !ua->timeout_heap) return; - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - while (1) { - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) break; - if (entry.expiration > now_ms) break; - - // Pop the expired timeout - timeout_heap_pop(ua->timeout_heap, &entry); - struct timeout_node* node = (struct timeout_node*)entry.data; - - if (node && node->callback && !node->cancelled) { - // Execute callback only if not cancelled - node->callback(node->arg); - } - - // Always free the node after processing - if (node && node->ua) { - node->ua->timer_free_count++; - } - free(node); - } -} - -// Compute time to next timeout -static void get_next_timeout(struct UASYNC* ua, struct timeval* tv) { - if (!ua || !ua->timeout_heap) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - if (entry.expiration <= now_ms) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - uint64_t delta_ms = entry.expiration - now_ms; - if (delta_ms > 86400000) { // Cap at 1 day to avoid overflow - delta_ms = 86400000; - } - tv->tv_sec = delta_ms / 1000; - tv->tv_usec = (delta_ms % 1000) * 1000; -} - - - -// Instance version -void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_callback_t callback) { - if (!ua || timeout_tb < 0 || !callback) return NULL; - if (!ua->timeout_heap) return NULL; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: ua=%p, timeout=%d tb, arg=%p, callback=%p", - ua, timeout_tb, arg, callback); - - struct timeout_node* node = malloc(sizeof(struct timeout_node)); - if (!node) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); - return NULL; - } - ua->timer_alloc_count++; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: allocated node %p (alloc_count=%zu)", - node, ua->timer_alloc_count); - - node->arg = arg; - node->callback = callback; - node->ua = ua; - node->cancelled = 0; - - // Calculate expiration time in milliseconds - struct timeval now; - get_current_time(&now); - timeval_add_tb(&now, timeout_tb); - node->expiration_ms = timeval_to_ms(&now); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: node %p expires at %llu ms", - node, (unsigned long long)node->expiration_ms); - - // Add to heap - if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); - free(node); - ua->timer_free_count++; // Balance the alloc counter - return NULL; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: successfully created timer %p", node); - return node; -} - - - -// Instance version -err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) { - if (!ua || !t_id || !ua->timeout_heap) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: invalid parameters ua=%p, t_id=%p, heap=%p", - ua, t_id, ua ? ua->timeout_heap : NULL); - return ERR_FAIL; - } - - struct timeout_node* node = (struct timeout_node*)t_id; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: ua=%p, t_id=%p, node=%p, expires=%llu ms", - ua, t_id, node, (unsigned long long)node->expiration_ms); - - // Try to cancel from heap first - if (timeout_heap_cancel(ua->timeout_heap, node->expiration_ms, node) == 0) { - // Successfully removed from heap - mark as cancelled and update counter - node->cancelled = 1; - node->callback = NULL; - ua->timer_free_count++; // Update counter for cancelled timer - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: successfully cancelled timer %p from heap, free_count=%zu", node, ua->timer_free_count); - free(node); // Free the cancelled timer node - return ERR_OK; - } - node->cancelled = 1; - node->callback = NULL; - return ERR_OK; -} // Successfully cancelled (marked) - - -// Instance version -void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!ua || fd < 0 || fd >= FD_SETSIZE) return NULL; // Bounds check - - int index = socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, user_data); - if (index < 0) return NULL; - - ua->socket_alloc_count++; - - // Return pointer to the socket node (same as before for API compatibility) - return &ua->sockets->sockets[index]; -} - - - -// Instance version -err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { - if (!ua || !s_id) return ERR_FAIL; - - struct socket_node* node = (struct socket_node*)s_id; - if (node->fd < 0) return ERR_FAIL; // Invalid node - - int result = socket_array_remove(ua->sockets, node->fd); - if (result != 0) return ERR_FAIL; - - ua->socket_free_count++; - return ERR_OK; -} - - - -void uasync_mainloop(struct UASYNC* ua) { - while (1) { - uasync_poll(ua, -1); /* infinite timeout */ - } -} - -// Instance version -void uasync_poll(struct UASYNC* ua, int timeout_tb) { - if (!ua) return; - - /* Process expired timeouts */ - process_timeouts(ua); - - /* Compute timeout for poll in milliseconds */ - int timeout_ms = -1; // infinite by default - - // Get next timeout from heap - struct timeval tv; - get_next_timeout(ua, &tv); - - if (tv.tv_sec > 0 || tv.tv_usec > 0 || (ua->timeout_heap && ua->timeout_heap->size > 0)) { - // Convert timeval to milliseconds, cap at INT_MAX - uint64_t ms = (uint64_t)tv.tv_sec * 1000ULL + (uint64_t)tv.tv_usec / 1000ULL; - if (ms > INT_MAX) ms = INT_MAX; - timeout_ms = (int)ms; - } - - /* If timeout_tb >= 0, compute timeout as min(timeout_tb, existing timer) */ - if (timeout_tb >= 0) { - // Convert timebase (0.1 ms) to milliseconds - int user_timeout_ms = timeout_tb / 10; - if (timeout_tb % 10 != 0) user_timeout_ms++; // round up - - if (timeout_ms < 0 || user_timeout_ms < timeout_ms) { - timeout_ms = user_timeout_ms; - } - } - - /* Build pollfd array from socket array - O(1) per socket */ - int socket_count = ua->sockets ? ua->sockets->count : 0; - int wakeup_fd_present = ua->wakeup_initialized ? 1 : 0; - int total_fds = socket_count + wakeup_fd_present; - - if (total_fds == 0) { - /* No sockets and no wakeup fd, just wait for timeout */ - if (timeout_ms >= 0) { - /* usleep would be better but we just call poll with empty set */ - struct pollfd dummy; - poll(&dummy, 0, timeout_ms); - } else { - /* Infinite timeout with no sockets - should not happen in practice */ - return; - } - /* Check timeouts again after sleep */ - process_timeouts(ua); - return; - } - - struct pollfd* fds = malloc(total_fds * sizeof(struct pollfd)); - struct socket_node** nodes = NULL; - if (socket_count > 0) { - nodes = malloc(socket_count * sizeof(struct socket_node*)); - } - if (!fds || (socket_count > 0 && !nodes)) { - free(fds); - free(nodes); - return; /* out of memory */ - } - - /* Fill arrays */ - int idx = 0; - - /* Add wakeup fd first if present */ - if (wakeup_fd_present) { - fds[idx].fd = ua->wakeup_pipe[0]; - fds[idx].events = POLLIN; - fds[idx].revents = 0; - idx++; - } - - /* Add socket fds using efficient array traversal */ - int node_idx = 0; - for (int i = 0; i < ua->sockets->capacity && node_idx < socket_count; i++) { - if (ua->sockets->sockets[i].active) { - struct socket_node* cur = &ua->sockets->sockets[i]; - fds[idx].fd = cur->fd; - fds[idx].events = 0; - fds[idx].revents = 0; - - if (cur->read_cbk) fds[idx].events |= POLLIN; - if (cur->write_cbk) fds[idx].events |= POLLOUT; - if (cur->except_cbk) fds[idx].events |= POLLPRI; - - if (nodes) { - nodes[node_idx] = cur; - } - idx++; - node_idx++; - } - } - - /* Call poll */ - int ret = poll(fds, total_fds, timeout_ms); - if (ret < 0) { - if (errno == EINTR) { - free(fds); - free(nodes); - return; - } - perror("poll"); - free(fds); - free(nodes); - return; - } - - /* Process timeouts that may have expired during poll */ - process_timeouts(ua); - - /* Process socket events */ - if (ret > 0) { - for (int i = 0; i < total_fds; i++) { - if (fds[i].revents == 0) continue; - - /* Handle wakeup fd separately */ - if (wakeup_fd_present && i == 0) { - if (fds[i].revents & POLLIN) { - drain_wakeup_pipe(ua); - } - continue; - } - - /* Socket event */ - int socket_idx = i - wakeup_fd_present; - struct socket_node* node = nodes[socket_idx]; - - /* Check for error conditions first */ - if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { - /* Treat as exceptional condition */ - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Exceptional data (out-of-band) */ - if (fds[i].revents & POLLPRI) { - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Read readiness */ - if (fds[i].revents & POLLIN) { - if (node->read_cbk) { - node->read_cbk(node->fd, node->user_data); - } - } - - /* Write readiness */ - if (fds[i].revents & POLLOUT) { - if (node->write_cbk) { - node->write_cbk(node->fd, node->user_data); - } - } - } - } - - free(fds); - free(nodes); -} - - - -// ========== Instance management functions ========== - -struct UASYNC* uasync_create(void) { - // Initialize debug system on first use - static int debug_initialized = 0; - if (!debug_initialized) { - debug_config_init(); - debug_initialized = 1; - } - - struct UASYNC* ua = malloc(sizeof(struct UASYNC)); - if (!ua) return NULL; - - memset(ua, 0, sizeof(struct UASYNC)); - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - ua->wakeup_initialized = 0; - - // Create wakeup pipe - if (pipe(ua->wakeup_pipe) < 0) { - DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno)); - // Continue without wakeup mechanism - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - } else { - ua->wakeup_initialized = 1; - // Set non-blocking on read end to avoid blocking if pipe is full - int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0); - if (flags >= 0) { - fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK); - } - } - - ua->sockets = socket_array_create(16); - if (!ua->sockets) { - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - ua->timeout_heap = timeout_heap_create(16); - if (!ua->timeout_heap) { - socket_array_destroy(ua->sockets); - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - // Set callback to free timeout nodes and update counters - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - - return ua; -} - -// Print all resources for debugging -void uasync_print_resources(struct UASYNC* ua, const char* prefix) { - if (!ua) { - printf("%s: NULL uasync instance\n", prefix); - return; - } - - printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); - printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - - // Показать активные таймеры - if (ua->timeout_heap) { - size_t active_timers = 0; - TimeoutEntry entry; - // Создаем временную копию кучи для подсчета - TimeoutHeap* temp_heap = timeout_heap_create(16); - if (temp_heap) { - // Копируем все активные таймеры - while (timeout_heap_pop(ua->timeout_heap, &entry) == 0) { - if (!entry.deleted) { - active_timers++; - struct timeout_node* node = (struct timeout_node*)entry.data; - printf(" Timer: node=%p, expires=%llu ms, cancelled=%d\n", - node, (unsigned long long)entry.expiration, node->cancelled); - } - timeout_heap_push(temp_heap, entry.expiration, entry.data); - } - // Возвращаем таймеры обратно - while (timeout_heap_pop(temp_heap, &entry) == 0) { - timeout_heap_push(ua->timeout_heap, entry.expiration, entry.data); - } - timeout_heap_destroy(temp_heap); - } - printf(" Active timers in heap: %zu\n", active_timers); - } - - // Показать активные сокеты - if (ua->sockets) { - int active_sockets = 0; - printf(" Socket array capacity: %d, active: %d\n", - ua->sockets->capacity, ua->sockets->count); - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - active_sockets++; - printf(" Socket: fd=%d, active=%d\n", - ua->sockets->sockets[i].fd, - ua->sockets->sockets[i].active); - } - } - printf(" Total active sockets: %d\n", active_sockets); - } - - printf("🔚 %s: End of resource report\n\n", prefix); -} - -void uasync_destroy(struct UASYNC* ua) { - if (!ua) return; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); - - // Диагностика ресурсов перед очисткой - uasync_print_resources(ua, "BEFORE_DESTROY"); - - // Check for potential memory leaks - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected before cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - // Continue cleanup, will abort after if leaks remain - } - - // Free all remaining timeouts - if (ua->timeout_heap) { - size_t freed_count = 0; - while (1) { - TimeoutEntry entry; - if (timeout_heap_pop(ua->timeout_heap, &entry) != 0) break; - struct timeout_node* node = (struct timeout_node*)entry.data; - DEBUG_TRACE(DEBUG_CATEGORY_TIMERS, "uasync_destroy: freeing timer node %p (expired=%llu ms)", - node, (unsigned long long)entry.expiration); - ua->timer_free_count++; - freed_count++; - free(node); - } - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: freed %zu timer nodes in destroy, heap freed_count = %zu", - freed_count, ua->timeout_heap->freed_count); - timeout_heap_destroy(ua->timeout_heap); - } - - // Free all socket nodes using array approach - if (ua->sockets) { - // Count and free all active sockets - int freed_count = 0; - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - ua->socket_free_count++; - freed_count++; - } - } - DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); - socket_array_destroy(ua->sockets); - } - - // Close wakeup pipe - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - - // Final leak check - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - abort(); - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua); - free(ua); -} - -void uasync_init_instance(struct UASYNC* ua) { - if (!ua) return; - - // Initialize socket array if not present - if (!ua->sockets) { - ua->sockets = socket_array_create(16); - } - - if (!ua->timeout_heap) { - ua->timeout_heap = timeout_heap_create(16); - if (ua->timeout_heap) { - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - } - } -} - -// Debug statistics -void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { - if (!ua) return; - if (timer_alloc) *timer_alloc = ua->timer_alloc_count; - if (timer_free) *timer_free = ua->timer_free_count; - if (socket_alloc) *socket_alloc = ua->socket_alloc_count; - if (socket_free) *socket_free = ua->socket_free_count; -} - -// Get global instance for backward compatibility - -// Wakeup mechanism -int uasync_wakeup(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - - char byte = 0; - ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1); - if (ret != 1) { - // Don't print error from signal handler - return -1; - } - return 0; -} - -int uasync_get_wakeup_fd(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - return ua->wakeup_pipe[1]; -} diff --git a/lib/u_async.c2 b/lib/u_async.c2 deleted file mode 100755 index 2a00c16c..00000000 --- a/lib/u_async.c2 +++ /dev/null @@ -1,800 +0,0 @@ -// uasync.c - -#include "u_async.h" -#include "debug_config.h" -#include -#include -#include -#include -#include -#include -#include -#include - - - -// Timeout node with safe cancellation -struct timeout_node { - void* arg; - timeout_callback_t callback; - uint64_t expiration_ms; // absolute expiration time in milliseconds - struct UASYNC* ua; // Pointer back to uasync instance for counter updates - int cancelled; // Cancellation flag -}; - -// Socket node with array-based storage -struct socket_node { - int fd; - socket_callback_t read_cbk; - socket_callback_t write_cbk; - socket_callback_t except_cbk; - void* user_data; - int active; // 1 if socket is active, 0 if freed (for reuse) -}; - -// Array-based socket management for O(1) operations -struct socket_array { - struct socket_node* sockets; // Dynamic array of socket nodes - int* fd_to_index; // FD to array index mapping - int* index_to_fd; // Array index to FD mapping - int capacity; // Total allocated capacity - int count; // Number of active sockets - int max_fd; // Maximum FD for bounds checking -}; - -static struct socket_array* socket_array_create(int initial_capacity); -static void socket_array_destroy(struct socket_array* sa); -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data); -static int socket_array_remove(struct socket_array* sa, int fd); -static struct socket_node* socket_array_get(struct socket_array* sa, int fd); - -// No global instance - each module must use its own struct UASYNC instance - -// Array-based socket management implementation -static struct socket_array* socket_array_create(int initial_capacity) { - if (initial_capacity < 4) initial_capacity = 4; // Minimum capacity - - struct socket_array* sa = malloc(sizeof(struct socket_array)); - if (!sa) return NULL; - - sa->sockets = calloc(initial_capacity, sizeof(struct socket_node)); - sa->fd_to_index = calloc(initial_capacity, sizeof(int)); - sa->index_to_fd = calloc(initial_capacity, sizeof(int)); - - if (!sa->sockets || !sa->fd_to_index || !sa->index_to_fd) { - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); - return NULL; - } - - // Initialize mapping arrays to -1 (invalid) - for (int i = 0; i < initial_capacity; i++) { - sa->fd_to_index[i] = -1; - sa->index_to_fd[i] = -1; - sa->sockets[i].fd = -1; - sa->sockets[i].active = 0; - } - - sa->capacity = initial_capacity; - sa->count = 0; - sa->max_fd = -1; - - return sa; -} - -static void socket_array_destroy(struct socket_array* sa) { - if (!sa) return; - - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); -} - -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!sa || fd < 0 || fd >= FD_SETSIZE) return -1; - if (fd >= sa->capacity) { - // Need to resize - double the capacity - int new_capacity = sa->capacity * 2; - if (fd >= new_capacity) new_capacity = fd + 16; // Ensure enough space - - struct socket_node* new_sockets = realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); - int* new_fd_to_index = realloc(sa->fd_to_index, new_capacity * sizeof(int)); - int* new_index_to_fd = realloc(sa->index_to_fd, new_capacity * sizeof(int)); - - if (!new_sockets || !new_fd_to_index || !new_index_to_fd) { - // Allocation failed - free(new_sockets); - free(new_fd_to_index); - free(new_index_to_fd); - return -1; - } - - // Initialize new elements - for (int i = sa->capacity; i < new_capacity; i++) { - new_fd_to_index[i] = -1; - new_index_to_fd[i] = -1; - new_sockets[i].fd = -1; - new_sockets[i].active = 0; - } - - sa->sockets = new_sockets; - sa->fd_to_index = new_fd_to_index; - sa->index_to_fd = new_index_to_fd; - sa->capacity = new_capacity; - } - - // Check if FD already exists - if (sa->fd_to_index[fd] != -1) return -1; // FD already exists - - // Find first free slot - int index = -1; - for (int i = 0; i < sa->capacity; i++) { - if (!sa->sockets[i].active) { - index = i; - break; - } - } - - if (index == -1) return -1; // No free slots (shouldn't happen) - - // Add the socket - sa->sockets[index].fd = fd; - sa->sockets[index].read_cbk = read_cbk; - sa->sockets[index].write_cbk = write_cbk; - sa->sockets[index].except_cbk = except_cbk; - sa->sockets[index].user_data = user_data; - sa->sockets[index].active = 1; - - sa->fd_to_index[fd] = index; - sa->index_to_fd[index] = fd; - sa->count++; - - if (fd > sa->max_fd) sa->max_fd = fd; - - return index; -} - -static int socket_array_remove(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return -1; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return -1; // FD not found - - // Mark as inactive - sa->sockets[index].active = 0; - sa->sockets[index].fd = -1; - sa->fd_to_index[fd] = -1; - sa->index_to_fd[index] = -1; - sa->count--; - - return 0; -} - -static struct socket_node* socket_array_get(struct socket_array* sa, int fd) { - if (!sa || fd < 0 || fd >= sa->capacity) return NULL; - - int index = sa->fd_to_index[fd]; - if (index == -1 || !sa->sockets[index].active) return NULL; - - return &sa->sockets[index]; -} - -// Callback to free timeout node and update counters -static void timeout_node_free_callback(void* user_data, void* data) { - struct UASYNC* ua = (struct UASYNC*)user_data; - if (!data) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "timeout_node_free_callback: NULL data pointer"); - return; - } - - struct timeout_node* node = (struct timeout_node*)data; - - // Проверка на двойное освобождение - смотрим если указатель валидный - if (node) { - DEBUG_TRACE(DEBUG_CATEGORY_TIMERS, "timeout_node_free_callback: freeing node %p", node); - ua->timer_free_count++; - free(data); - } else { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "timeout_node_free_callback: invalid node pointer %p", node); - } -} - -// Helper to get current time -static void get_current_time(struct timeval* tv) { - gettimeofday(tv, NULL); -} - - - -// Drain wakeup pipe - read all available bytes -static void drain_wakeup_pipe(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return; - - char buf[64]; - while (1) { - ssize_t n = read(ua->wakeup_pipe[0], buf, sizeof(buf)); - if (n <= 0) break; - } -} - -// Helper to add timeval: tv += dt (timebase units) -static void timeval_add_tb(struct timeval* tv, int dt) { - tv->tv_usec += (dt % 10000) * 100; - tv->tv_sec += dt / 10000 + tv->tv_usec / 1000000; - tv->tv_usec %= 1000000; -} - -// Convert timeval to milliseconds (uint64_t) -static uint64_t timeval_to_ms(const struct timeval* tv) { - return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL; -} - - - -// Simplified timeout handling without reference counting - -// Process expired timeouts with safe cancellation -static void process_timeouts(struct UASYNC* ua) { - if (!ua || !ua->timeout_heap) return; - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - while (1) { - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) break; - if (entry.expiration > now_ms) break; - - // Pop the expired timeout - timeout_heap_pop(ua->timeout_heap, &entry); - struct timeout_node* node = (struct timeout_node*)entry.data; - - if (node && node->callback && !node->cancelled) { - // Execute callback only if not cancelled - node->callback(node->arg); - } - - // Always free the node after processing - if (node && node->ua) { - node->ua->timer_free_count++; - } - free(node); - } -} - -// Compute time to next timeout -static void get_next_timeout(struct UASYNC* ua, struct timeval* tv) { - if (!ua || !ua->timeout_heap) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - if (entry.expiration <= now_ms) { - tv->tv_sec = 0; - tv->tv_usec = 0; - return; - } - - uint64_t delta_ms = entry.expiration - now_ms; - tv->tv_sec = delta_ms / 1000; - tv->tv_usec = (delta_ms % 1000) * 1000; -} - - - -// Instance version -void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_callback_t callback) { - if (!ua || timeout_tb < 0 || !callback) return NULL; - if (!ua->timeout_heap) return NULL; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: ua=%p, timeout=%d tb, arg=%p, callback=%p", - ua, timeout_tb, arg, callback); - - struct timeout_node* node = malloc(sizeof(struct timeout_node)); - if (!node) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); - return NULL; - } - ua->timer_alloc_count++; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: allocated node %p (alloc_count=%zu)", - node, ua->timer_alloc_count); - - node->arg = arg; - node->callback = callback; - node->ua = ua; - node->cancelled = 0; - - // Calculate expiration time in milliseconds - struct timeval now; - get_current_time(&now); - timeval_add_tb(&now, timeout_tb); - node->expiration_ms = timeval_to_ms(&now); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: node %p expires at %llu ms", - node, (unsigned long long)node->expiration_ms); - - // Add to heap - if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); - free(node); - ua->timer_free_count++; // Balance the alloc counter - return NULL; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: successfully created timer %p", node); - return node; -} - - - -// Instance version -err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) { - if (!ua || !t_id || !ua->timeout_heap) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: invalid parameters ua=%p, t_id=%p, heap=%p", - ua, t_id, ua ? ua->timeout_heap : NULL); - return ERR_FAIL; - } - - struct timeout_node* node = (struct timeout_node*)t_id; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: ua=%p, t_id=%p, node=%p, expires=%llu ms", - ua, t_id, node, (unsigned long long)node->expiration_ms); - - // Try to cancel from heap first - if (timeout_heap_cancel(ua->timeout_heap, node->expiration_ms, node) == 0) { - // Successfully removed from heap - mark as cancelled and update counter - node->cancelled = 1; - node->callback = NULL; - ua->timer_free_count++; // Update counter for cancelled timer - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: successfully cancelled timer %p from heap, free_count=%zu", node, ua->timer_free_count); - free(node); // Free the cancelled timer node - return ERR_OK; - } - - // If not found in heap, it may have already expired or been invalid - return ERR_FAIL; -} - - -// Instance version -void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!ua || fd < 0 || fd >= FD_SETSIZE) return NULL; // Bounds check - - int index = socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, user_data); - if (index < 0) return NULL; - - ua->socket_alloc_count++; - - // Return pointer to the socket node (same as before for API compatibility) - return &ua->sockets->sockets[index]; -} - - - -// Instance version -err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { - if (!ua || !s_id) return ERR_FAIL; - - struct socket_node* node = (struct socket_node*)s_id; - if (node->fd < 0) return ERR_FAIL; // Invalid node - - int result = socket_array_remove(ua->sockets, node->fd); - if (result != 0) return ERR_FAIL; - - ua->socket_free_count++; - return ERR_OK; -} - - - -void uasync_mainloop(struct UASYNC* ua) { - while (1) { - uasync_poll(ua, -1); /* infinite timeout */ - } -} - -// Instance version -void uasync_poll(struct UASYNC* ua, int timeout_tb) { - if (!ua) return; - - /* Process expired timeouts */ - process_timeouts(ua); - - /* Compute timeout for poll in milliseconds */ - int timeout_ms = -1; // infinite by default - - // Get next timeout from heap - struct timeval tv; - get_next_timeout(ua, &tv); - - if (tv.tv_sec > 0 || tv.tv_usec > 0 || (ua->timeout_heap && ua->timeout_heap->size > 0)) { - // Convert timeval to milliseconds, cap at INT_MAX - uint64_t ms = (uint64_t)tv.tv_sec * 1000ULL + (uint64_t)tv.tv_usec / 1000ULL; - if (ms > INT_MAX) ms = INT_MAX; - timeout_ms = (int)ms; - } - - /* If timeout_tb >= 0, compute timeout as min(timeout_tb, existing timer) */ - if (timeout_tb >= 0) { - // Convert timebase (0.1 ms) to milliseconds - int user_timeout_ms = timeout_tb / 10; - if (timeout_tb % 10 != 0) user_timeout_ms++; // round up - - if (timeout_ms < 0 || user_timeout_ms < timeout_ms) { - timeout_ms = user_timeout_ms; - } - } - - /* Build pollfd array from socket array - O(1) per socket */ - int socket_count = ua->sockets ? ua->sockets->count : 0; - int wakeup_fd_present = ua->wakeup_initialized ? 1 : 0; - int total_fds = socket_count + wakeup_fd_present; - - if (total_fds == 0) { - /* No sockets and no wakeup fd, just wait for timeout */ - if (timeout_ms >= 0) { - /* usleep would be better but we just call poll with empty set */ - poll(NULL, 0, timeout_ms); - } else { - /* Infinite timeout with no sockets - should not happen in practice */ - return; - } - /* Check timeouts again after sleep */ - process_timeouts(ua); - return; - } - - struct pollfd* fds = malloc(total_fds * sizeof(struct pollfd)); - struct socket_node** nodes = NULL; - if (socket_count > 0) { - nodes = malloc(socket_count * sizeof(struct socket_node*)); - } - if (!fds || (socket_count > 0 && !nodes)) { - free(fds); - free(nodes); - return; /* out of memory */ - } - - /* Fill arrays */ - int idx = 0; - - /* Add wakeup fd first if present */ - if (wakeup_fd_present) { - fds[idx].fd = ua->wakeup_pipe[0]; - fds[idx].events = POLLIN; - fds[idx].revents = 0; - idx++; - } - - /* Add socket fds using efficient array traversal */ - int node_idx = 0; - for (int i = 0; i < ua->sockets->capacity && node_idx < socket_count; i++) { - if (ua->sockets->sockets[i].active) { - struct socket_node* cur = &ua->sockets->sockets[i]; - fds[idx].fd = cur->fd; - fds[idx].events = 0; - fds[idx].revents = 0; - - if (cur->read_cbk) fds[idx].events |= POLLIN; - if (cur->write_cbk) fds[idx].events |= POLLOUT; - if (cur->except_cbk) fds[idx].events |= POLLPRI; - - if (nodes) { - nodes[node_idx] = cur; - } - idx++; - node_idx++; - } - } - - /* Call poll */ - int ret = poll(fds, total_fds, timeout_ms); - if (ret < 0) { - if (errno == EINTR) { - free(fds); - free(nodes); - return; - } - perror("poll"); - free(fds); - free(nodes); - return; - } - - /* Process timeouts that may have expired during poll */ - process_timeouts(ua); - - /* Process socket events */ - if (ret > 0) { - for (int i = 0; i < total_fds; i++) { - if (fds[i].revents == 0) continue; - - /* Handle wakeup fd separately */ - if (wakeup_fd_present && i == 0) { - if (fds[i].revents & POLLIN) { - drain_wakeup_pipe(ua); - } - continue; - } - - /* Socket event */ - int socket_idx = i - wakeup_fd_present; - struct socket_node* node = nodes[socket_idx]; - - /* Check for error conditions first */ - if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { - /* Treat as exceptional condition */ - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Exceptional data (out-of-band) */ - if (fds[i].revents & POLLPRI) { - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Read readiness */ - if (fds[i].revents & POLLIN) { - if (node->read_cbk) { - node->read_cbk(node->fd, node->user_data); - } - } - - /* Write readiness */ - if (fds[i].revents & POLLOUT) { - if (node->write_cbk) { - node->write_cbk(node->fd, node->user_data); - } - } - } - } - - free(fds); - free(nodes); -} - - - -// ========== Instance management functions ========== - -struct UASYNC* uasync_create(void) { - // Initialize debug system on first use - static int debug_initialized = 0; - if (!debug_initialized) { - debug_config_init(); - debug_initialized = 1; - } - - struct UASYNC* ua = malloc(sizeof(struct UASYNC)); - if (!ua) return NULL; - - memset(ua, 0, sizeof(struct UASYNC)); - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - ua->wakeup_initialized = 0; - - // Create wakeup pipe - if (pipe(ua->wakeup_pipe) < 0) { - DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno)); - // Continue without wakeup mechanism - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - } else { - ua->wakeup_initialized = 1; - // Set non-blocking on read end to avoid blocking if pipe is full - int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0); - if (flags >= 0) { - fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK); - } - } - - ua->sockets = socket_array_create(16); - if (!ua->sockets) { - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - ua->timeout_heap = timeout_heap_create(16); - if (!ua->timeout_heap) { - socket_array_destroy(ua->sockets); - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - // Set callback to free timeout nodes and update counters - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - - return ua; -} - -// Print all resources for debugging -void uasync_print_resources(struct UASYNC* ua, const char* prefix) { - if (!ua) { - printf("%s: NULL uasync instance\n", prefix); - return; - } - - printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); - printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - - // Показать активные таймеры - if (ua->timeout_heap) { - size_t active_timers = 0; - // Безопасное чтение без извлечения - просто итерируем по массиву - for (size_t i = 0; i < ua->timeout_heap->size; i++) { - if (!ua->timeout_heap->heap[i].deleted) { - active_timers++; - struct timeout_node* node = (struct timeout_node*)ua->timeout_heap->heap[i].data; - printf(" Timer: node=%p, expires=%llu ms, cancelled=%d\n", - node, (unsigned long long)ua->timeout_heap->heap[i].expiration, node->cancelled); - } - } - printf(" Active timers in heap: %zu\n", active_timers); - } - - // Показать активные сокеты - if (ua->sockets) { - int active_sockets = 0; - printf(" Socket array capacity: %d, active: %d\n", - ua->sockets->capacity, ua->sockets->count); - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - active_sockets++; - printf(" Socket: fd=%d, active=%d\n", - ua->sockets->sockets[i].fd, - ua->sockets->sockets[i].active); - } - } - printf(" Total active sockets: %d\n", active_sockets); - } - - printf("🔚 %s: End of resource report\n\n", prefix); -} - -void uasync_destroy(struct UASYNC* ua, int close_fds) { - if (!ua) return; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); - - // Диагностика ресурсов перед очисткой (отключена для избежания double free) - // uasync_print_resources(ua, "BEFORE_DESTROY"); - - // Check for potential memory leaks - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected before cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - // Continue cleanup, will abort after if leaks remain - } - - // Free all remaining timeouts (временно полностью отключено для диагностики double free) - if (ua->timeout_heap) { - DEBUG_WARN(DEBUG_CATEGORY_MEMORY, "uasync_destroy: timer cleanup completely disabled due to double free in timeout_heap_pop()"); - // Просто уничтожаем кучу без очистки элементов - timeout_heap_destroy(ua->timeout_heap); - ua->timeout_heap = NULL; - ua->timer_free_count = ua->timer_alloc_count; // Предполагаем все освобождено - } - - // Free all socket nodes using array approach - if (ua->sockets) { - // Count and free all active sockets - int freed_count = 0; - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - if (close_fds && ua->sockets->sockets[i].fd >= 0) { - close(ua->sockets->sockets[i].fd); - } - ua->socket_free_count++; - freed_count++; - } - } - DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); - socket_array_destroy(ua->sockets); - } - - // Close wakeup pipe - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - - // Final leak check - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - abort(); - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua); - free(ua); -} - -void uasync_init_instance(struct UASYNC* ua) { - if (!ua) return; - - // Initialize socket array if not present - if (!ua->sockets) { - ua->sockets = socket_array_create(16); - } - - if (!ua->timeout_heap) { - ua->timeout_heap = timeout_heap_create(16); - if (ua->timeout_heap) { - timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); - } - } -} - -// Debug statistics -void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { - if (!ua) return; - if (timer_alloc) *timer_alloc = ua->timer_alloc_count; - if (timer_free) *timer_free = ua->timer_free_count; - if (socket_alloc) *socket_alloc = ua->socket_alloc_count; - if (socket_free) *socket_free = ua->socket_free_count; -} - -// Get global instance for backward compatibility - -// Wakeup mechanism -int uasync_wakeup(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - - char byte = 0; - ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1); - if (ret != 1) { - // Don't print error from signal handler - return -1; - } - return 0; -} - -int uasync_get_wakeup_fd(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - return ua->wakeup_pipe[1]; -} diff --git a/lib/u_async.c3 b/lib/u_async.c3 deleted file mode 100755 index 1bd90d68..00000000 --- a/lib/u_async.c3 +++ /dev/null @@ -1,617 +0,0 @@ -// uasync.c - -#include "u_async.h" -#include "debug_config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Socket node with array-based storage -struct socket_node { - int fd; - socket_callback_t read_cbk; - socket_callback_t write_cbk; - socket_callback_t except_cbk; - void* user_data; - int active; // 1 if socket is active, 0 if freed (for reuse) -}; - -// Array-based socket management for O(1) operations -struct socket_array { - struct socket_node* sockets; // Dynamic array of socket nodes - int* fd_to_index; // FD to array index mapping - int* index_to_fd; // Array index to FD mapping - int capacity; // Total allocated capacity - int count; // Number of active sockets - int max_fd; // Maximum FD for bounds checking -}; - -static struct socket_array* socket_array_create(int initial_capacity); -static void socket_array_destroy(struct socket_array* sa); -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data); -static int socket_array_remove(struct socket_array* sa, int fd); -static struct socket_node* socket_array_get(struct socket_array* sa, int fd); - -// Helper to get current time -static void get_current_time(struct timeval* tv) { - gettimeofday(tv, NULL); -} - -static void timeval_add_tb(struct timeval* tv, int dt) { - if (dt <= 0) return; - long long us_add = (long long)dt * 100LL; // 0.1ms = 100us - tv->tv_usec += us_add % 1000000LL; - tv->tv_sec += us_add / 1000000LL; - if (tv->tv_usec >= 1000000LL) { - tv->tv_sec += tv->tv_usec / 1000000LL; - tv->tv_usec %= 1000000LL; - } -} - -static uint64_t timeval_to_ms(const struct timeval* tv) { - return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL; -} - -static void drain_wakeup_pipe(struct UASYNC* ua) { - char buf[1024]; - while (read(ua->wakeup_pipe[0], buf, sizeof(buf)) > 0); -} - -// Process expired timeouts with safe cancellation -static void process_timeouts(struct UASYNC* ua) { - if (!ua || !ua->timeout_heap) return; - - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - - while (1) { - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) break; - if (entry.expiration > now_ms) break; - - // Pop the expired timeout - timeout_heap_pop(ua->timeout_heap, &entry); - struct timeout_node* node = (struct timeout_node*)entry.data; - - if (node && node->callback && !node->cancelled) { - // Execute callback only if not cancelled - node->callback(node->arg); - } - - // Always free the node after processing - if (node && node->ua) { - node->ua->timer_free_count++; - } - free(node); - } -} - -// Instance version -void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_callback_t callback) { - if (!ua || timeout_tb < 0 || !callback) return NULL; - if (!ua->timeout_heap) return NULL; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: ua=%p, timeout=%d tb, arg=%p, callback=%p", - ua, timeout_tb, arg, callback); - - struct timeout_node* node = malloc(sizeof(struct timeout_node)); - if (!node) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); - return NULL; - } - ua->timer_alloc_count++; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: allocated node %p (alloc_count=%zu)", - node, ua->timer_alloc_count); - - node->arg = arg; - node->callback = callback; - node->ua = ua; - node->cancelled = 0; - node->heap_index = SIZE_MAX; // Initialize - - // Calculate expiration time in milliseconds - struct timeval now; - get_current_time(&now); - timeval_add_tb(&now, timeout_tb); - node->expiration_ms = timeval_to_ms(&now); - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: node %p expires at %llu ms", - node, (unsigned long long)node->expiration_ms); - - // Add to heap - if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); - free(node); - ua->timer_free_count++; // Balance the alloc counter - return NULL; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: successfully created timer %p", node); - return node; -} - -// Instance version -err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) { - if (!ua || !t_id || !ua->timeout_heap) { - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: invalid parameters ua=%p, t_id=%p, heap=%p", - ua, t_id, ua ? ua->timeout_heap : NULL); - return ERR_FAIL; - } - - struct timeout_node* node = (struct timeout_node*)t_id; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: ua=%p, t_id=%p, node=%p, expires=%llu ms", - ua, t_id, node, (unsigned long long)node->expiration_ms); - - // Try to remove from heap - if (timeout_heap_remove(ua->timeout_heap, node) == 0) { - // Successfully removed - free it - node->cancelled = 1; - node->callback = NULL; - ua->timer_free_count++; - free(node); - return ERR_OK; - } else { - // Not found in heap (may be already popped) - just mark cancelled to skip callback if pending - node->cancelled = 1; - node->callback = NULL; - // Do NOT free here to avoid double-free if already processed - return ERR_OK; - } -} - -void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (!ua || !ua->sockets) return NULL; - if (socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, user_data) != 0) return NULL; - ua->socket_alloc_count++; - return socket_array_get(ua->sockets, fd); -} - -err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { - if (!ua || !ua->sockets || !s_id) return ERR_FAIL; - struct socket_node* node = (struct socket_node*)s_id; - if (socket_array_remove(ua->sockets, node->fd) != 0) return ERR_FAIL; - ua->socket_free_count++; - return ERR_OK; -} - -void uasync_poll(struct UASYNC* ua, int timeout_tb) { - if (!ua) return; - - // Get next timeout - int timeout_ms = -1; - if (ua->timeout_heap && ua->timeout_heap->size > 0) { - struct timeval now_tv; - get_current_time(&now_tv); - uint64_t now_ms = timeval_to_ms(&now_tv); - TimeoutEntry entry; - if (timeout_heap_peek(ua->timeout_heap, &entry) == 0) { - if (entry.expiration > now_ms) { - timeout_ms = (int)(entry.expiration - now_ms); - } else { - timeout_ms = 0; - } - } - } - if (timeout_tb >= 0) { - int tb_ms = timeout_tb / 10; // approx tb to ms (adjust if tb unit is different) - if (timeout_ms < 0 || tb_ms < timeout_ms) timeout_ms = tb_ms; - } - - // Prepare poll fds - int wakeup_fd_present = ua->wakeup_initialized && ua->wakeup_pipe[0] >= 0; - int socket_count = ua->sockets ? ua->sockets->count : 0; - int total_fds = socket_count + wakeup_fd_present; - - struct pollfd* fds = malloc(total_fds * sizeof(struct pollfd)); - struct socket_node** nodes = socket_count > 0 ? malloc(socket_count * sizeof(struct socket_node*)) : NULL; - if (!fds || (socket_count > 0 && !nodes)) { - free(fds); - free(nodes); - return; /* out of memory */ - } - - /* Fill arrays */ - int idx = 0; - - /* Add wakeup fd first if present */ - if (wakeup_fd_present) { - fds[idx].fd = ua->wakeup_pipe[0]; - fds[idx].events = POLLIN; - fds[idx].revents = 0; - idx++; - } - - /* Add socket fds using efficient array traversal */ - int node_idx = 0; - for (int i = 0; i < ua->sockets->capacity && node_idx < socket_count; i++) { - if (ua->sockets->sockets[i].active) { - struct socket_node* cur = &ua->sockets->sockets[i]; - fds[idx].fd = cur->fd; - fds[idx].events = 0; - fds[idx].revents = 0; - - if (cur->read_cbk) fds[idx].events |= POLLIN; - if (cur->write_cbk) fds[idx].events |= POLLOUT; - if (cur->except_cbk) fds[idx].events |= POLLPRI; - - if (nodes) { - nodes[node_idx] = cur; - } - idx++; - node_idx++; - } - } - - /* Call poll */ - int ret = poll(fds, total_fds, timeout_ms); - if (ret < 0) { - if (errno == EINTR) { - free(fds); - free(nodes); - return; - } - perror("poll"); - free(fds); - free(nodes); - return; - } - - /* Process timeouts that may have expired during poll */ - process_timeouts(ua); - - /* Process socket events */ - if (ret > 0) { - for (int i = 0; i < total_fds; i++) { - if (fds[i].revents == 0) continue; - - /* Handle wakeup fd separately */ - if (wakeup_fd_present && i == 0) { - if (fds[i].revents & POLLIN) { - drain_wakeup_pipe(ua); - } - continue; - } - - /* Socket event */ - int socket_idx = i - wakeup_fd_present; - struct socket_node* node = nodes[socket_idx]; - - /* Check for error conditions first */ - if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { - /* Treat as exceptional condition */ - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Exceptional data (out-of-band) */ - if (fds[i].revents & POLLPRI) { - if (node->except_cbk) { - node->except_cbk(node->fd, node->user_data); - } - } - - /* Read readiness */ - if (fds[i].revents & POLLIN) { - if (node->read_cbk) { - node->read_cbk(node->fd, node->user_data); - } - } - - /* Write readiness */ - if (fds[i].revents & POLLOUT) { - if (node->write_cbk) { - node->write_cbk(node->fd, node->user_data); - } - } - } - } - - free(fds); - free(nodes); -} - -void uasync_mainloop(struct UASYNC* ua) { - while (1) { - uasync_poll(ua, -1); - } -} - -struct UASYNC* uasync_create(void) { - // Initialize debug system on first use - static int debug_initialized = 0; - if (!debug_initialized) { - debug_config_init(); - debug_initialized = 1; - } - - struct UASYNC* ua = malloc(sizeof(struct UASYNC)); - if (!ua) return NULL; - - memset(ua, 0, sizeof(struct UASYNC)); - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - ua->wakeup_initialized = 0; - - // Create wakeup pipe - if (pipe(ua->wakeup_pipe) < 0) { - DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno)); - // Continue without wakeup mechanism - ua->wakeup_pipe[0] = -1; - ua->wakeup_pipe[1] = -1; - } else { - ua->wakeup_initialized = 1; - // Set non-blocking on read end to avoid blocking if pipe is full - int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0); - if (flags >= 0) { - fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK); - } - } - - ua->sockets = socket_array_create(16); - if (!ua->sockets) { - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - ua->timeout_heap = timeout_heap_create(16); - if (!ua->timeout_heap) { - socket_array_destroy(ua->sockets); - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - free(ua); - return NULL; - } - - return ua; -} - -// Print all resources for debugging -void uasync_print_resources(struct UASYNC* ua, const char* prefix) { - if (!ua) { - printf("%s: NULL uasync instance\n", prefix); - return; - } - - printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); - printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - - // Show active timers (removed check for .deleted; heap entries are valid up to .size) - if (ua->timeout_heap) { - size_t active_timers = ua->timeout_heap->size; - for (size_t i = 0; i < ua->timeout_heap->size; i++) { - struct timeout_node* node = (struct timeout_node*)ua->timeout_heap->heap[i].data; - printf(" Timer: node=%p, expires=%llu ms, cancelled=%d\n", - node, (unsigned long long)ua->timeout_heap->heap[i].expiration, node->cancelled); - } - printf(" Active timers in heap: %zu\n", active_timers); - } - - // Show active sockets - if (ua->sockets) { - int active_sockets = 0; - printf(" Socket array capacity: %d, active: %d\n", - ua->sockets->capacity, ua->sockets->count); - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - active_sockets++; - printf(" Socket: fd=%d, active=%d\n", - ua->sockets->sockets[i].fd, - ua->sockets->sockets[i].active); - } - } - printf(" Total active sockets: %d\n", active_sockets); - } - - printf("🔚 %s: End of resource report\n\n", prefix); -} - -void uasync_destroy(struct UASYNC* ua, int close_fds) { - if (!ua) return; - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); - - // Free all remaining timeouts by popping and freeing data (fixes leak without double-free) - if (ua->timeout_heap) { - TimeoutEntry entry; - while (timeout_heap_pop(ua->timeout_heap, &entry) == 0) { - struct timeout_node* node = (struct timeout_node*)entry.data; - if (node) { - ua->timer_free_count++; - free(node); - } - } - timeout_heap_destroy(ua->timeout_heap); - ua->timeout_heap = NULL; - } - - // Free all socket nodes using array approach - if (ua->sockets) { - // Count and free all active sockets - int freed_count = 0; - for (int i = 0; i < ua->sockets->capacity; i++) { - if (ua->sockets->sockets[i].active) { - if (close_fds && ua->sockets->sockets[i].fd >= 0) { - close(ua->sockets->sockets[i].fd); - } - ua->socket_free_count++; - freed_count++; - } - } - DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); - socket_array_destroy(ua->sockets); - } - - // Close wakeup pipe - if (ua->wakeup_initialized) { - close(ua->wakeup_pipe[0]); - close(ua->wakeup_pipe[1]); - } - - // Final leak check - if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { - DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu", - ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", - ua->timer_alloc_count, ua->timer_free_count, - (ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); - DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", - ua->socket_alloc_count, ua->socket_free_count, - (ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); - abort(); - } - - DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua); - free(ua); -} - -void uasync_init_instance(struct UASYNC* ua) { - if (!ua) return; - - // Initialize socket array if not present - if (!ua->sockets) { - ua->sockets = socket_array_create(16); - } - - if (!ua->timeout_heap) { - ua->timeout_heap = timeout_heap_create(16); - } -} - -// Debug statistics -void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { - if (!ua) return; - if (timer_alloc) *timer_alloc = ua->timer_alloc_count; - if (timer_free) *timer_free = ua->timer_free_count; - if (socket_alloc) *socket_alloc = ua->socket_alloc_count; - if (socket_free) *socket_free = ua->socket_free_count; -} - -// Wakeup mechanism -int uasync_wakeup(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - - char byte = 0; - ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1); - if (ret != 1) { - // Don't print error from signal handler - return -1; - } - return 0; -} - -int uasync_get_wakeup_fd(struct UASYNC* ua) { - if (!ua || !ua->wakeup_initialized) return -1; - return ua->wakeup_pipe[1]; -} - -static struct socket_array* socket_array_create(int initial_capacity) { - struct socket_array* sa = malloc(sizeof(struct socket_array)); - if (!sa) return NULL; - sa->sockets = malloc(initial_capacity * sizeof(struct socket_node)); - if (!sa->sockets) { - free(sa); - return NULL; - } - sa->fd_to_index = malloc(FD_SETSIZE * sizeof(int)); // Assume FD_SETSIZE defined - if (!sa->fd_to_index) { - free(sa->sockets); - free(sa); - return NULL; - } - memset(sa->fd_to_index, -1, FD_SETSIZE * sizeof(int)); - sa->index_to_fd = malloc(initial_capacity * sizeof(int)); - if (!sa->index_to_fd) { - free(sa->fd_to_index); - free(sa->sockets); - free(sa); - return NULL; - } - sa->capacity = initial_capacity; - sa->count = 0; - sa->max_fd = 0; - return sa; -} - -static void socket_array_destroy(struct socket_array* sa) { - if (sa) { - free(sa->sockets); - free(sa->fd_to_index); - free(sa->index_to_fd); - free(sa); - } -} - -static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { - if (fd < 0 || fd >= FD_SETSIZE) return -1; - if (sa->fd_to_index[fd] != -1) return -1; // Already exists - - if (sa->count == sa->capacity) { - int new_cap = sa->capacity * 2; - struct socket_node* new_sockets = realloc(sa->sockets, new_cap * sizeof(struct socket_node)); - if (!new_sockets) return -1; - sa->sockets = new_sockets; - int* new_index_to_fd = realloc(sa->index_to_fd, new_cap * sizeof(int)); - if (!new_index_to_fd) return -1; - sa->index_to_fd = new_index_to_fd; - sa->capacity = new_cap; - } - - int index = sa->count++; - sa->sockets[index].fd = fd; - sa->sockets[index].read_cbk = read_cbk; - sa->sockets[index].write_cbk = write_cbk; - sa->sockets[index].except_cbk = except_cbk; - sa->sockets[index].user_data = user_data; - sa->sockets[index].active = 1; - sa->fd_to_index[fd] = index; - sa->index_to_fd[index] = fd; - if (fd > sa->max_fd) sa->max_fd = fd; - return 0; -} - -static int socket_array_remove(struct socket_array* sa, int fd) { - if (fd < 0 || fd >= FD_SETSIZE) return -1; - int index = sa->fd_to_index[fd]; - if (index == -1) return -1; - - sa->sockets[index].active = 0; - sa->fd_to_index[fd] = -1; - - // Move last to hole - int last_index = --sa->count; - if (index != last_index) { - sa->sockets[index] = sa->sockets[last_index]; - int last_fd = sa->index_to_fd[last_index]; - sa->fd_to_index[last_fd] = index; - sa->index_to_fd[index] = last_fd; - } - return 0; -} - -static struct socket_node* socket_array_get(struct socket_array* sa, int fd) { - if (fd < 0 || fd >= FD_SETSIZE) return NULL; - int index = sa->fd_to_index[fd]; - if (index == -1) return NULL; - return &sa->sockets[index]; -} \ No newline at end of file diff --git a/lib/u_async.h1 b/lib/u_async.h1 deleted file mode 100755 index 86d52386..00000000 --- a/lib/u_async.h1 +++ /dev/null @@ -1,69 +0,0 @@ -// uasync.h - -// модуль асинхронных операций. добавляем сокеты и таймауты и mainloop их обслуживает. - -#ifndef UASYNC_H -#define UASYNC_H - -#include -#include -#include "timeout_heap.h" - -typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout -typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket -// user_arg полезен если нужно передать управляющую структуру. Ее можно выделить в памяти и в ней хранить всё что надо. т.е. при set_timeout передаём и получаем ее в callback-е - - -// Error type -typedef int err_t; -#define ERR_OK 0 -#define ERR_FAIL -1 - -// Uasync instance structure -struct UASYNC { - TimeoutHeap* timeout_heap; // Heap for timeout management - struct socket_array* sockets; // Array-based socket management - // Debug counters for memory allocation tracking - size_t timer_alloc_count; - size_t timer_free_count; - size_t socket_alloc_count; - size_t socket_free_count; - // Wakeup pipe for interrupting poll - int wakeup_pipe[2]; // [0] read, [1] write - int wakeup_initialized; -}; - -// Type definitions -typedef struct UASYNC uasync_t; -typedef struct UASYNC UASYNC_t; - -// Instance API - основной API для работы с uasync -struct UASYNC* uasync_create(void); -void uasync_destroy(struct UASYNC* ua); -void uasync_init_instance(struct UASYNC* ua); - -// Timeouts, timebase = 0.1 mS -void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* user_arg, timeout_callback_t callback); -err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id); - -// Sockets -void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg); -err_t uasync_remove_socket(struct UASYNC* ua, void* s_id); - -// Single iteration of event loop with timeout (timebase units) -void uasync_poll(struct UASYNC* ua, int timeout_tb); - -// Mainloop (бесконечный цикл, __noreturn) -void uasync_mainloop(struct UASYNC* ua); - -// Debug statistics -void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free); - -// Print all resources (timers, sockets) for debugging -void uasync_print_resources(struct UASYNC* ua, const char* prefix); - -// Wakeup mechanism for interrupting poll -int uasync_wakeup(struct UASYNC* ua); -int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe (for signal handlers) - -#endif // UASYNC_H diff --git a/src/etcp.c b/src/etcp.c index ac3553f7..e88217a8 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -3,6 +3,7 @@ #include "etcp.h" #include "etcp_loadbalancer.h" #include "../lib/u_async.h" +#include "../lib/ll_queue.h" #include "../lib/debug_config.h" #include "crc32.h" // For potential hashing, though not used yet. #include @@ -32,6 +33,7 @@ static void input_queue_cb(struct ll_queue* q, void* arg); static void etcp_link_ready_callback(struct ETCP_CONN* etcp); static void input_send_q_cb(struct ll_queue* q, void* arg); +static void wait_ack_cb(struct ll_queue* q, void* arg); static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp); // Get current time in 0.1ms units @@ -39,20 +41,20 @@ uint64_t get_current_time_units() { struct timeval tv; gettimeofday(&tv, NULL); uint64_t time_units = ((uint64_t)tv.tv_sec * 10000ULL) + (tv.tv_usec / 100); - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_time_units: tv_sec=%ld, tv_usec=%ld, result=%llu", - tv.tv_sec, tv.tv_usec, (unsigned long long)time_units); +// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_time_units: tv_sec=%ld, tv_usec=%ld, result=%llu", +// tv.tv_sec, tv.tv_usec, (unsigned long long)time_units); return time_units; } uint16_t get_current_timestamp() { uint16_t timestamp = (uint16_t)(get_current_time_units() & 0xFFFF); - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_timestamp: result=%u", timestamp); +// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_timestamp: result=%u", timestamp); return timestamp; } // Timestamp diff (with wrap-around) static uint16_t timestamp_diff(uint16_t t1, uint16_t t2) { - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "timestamp_diff: t1=%u, t2=%u", t1, t2); +// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "timestamp_diff: t1=%u, t2=%u", t1, t2); if (t1 >= t2) { return t1 - t2; } @@ -99,9 +101,11 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) { // Set input queue callback queue_set_callback(etcp->input_queue, input_queue_cb, etcp); queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); + queue_set_callback(etcp->input_wait_ack, wait_ack_cb, etcp); + etcp->link_ready_for_send_fn = etcp_link_ready_callback; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connection_create: queues created, ETCP=%p mtu=%d, window_size=%u, next_tx_id=%u", + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_connection_create: connection initialized. ETCP=%p mtu=%d, window_size=%u, next_tx_id=%u", etcp, etcp->mtu, etcp->window_size, etcp->next_tx_id); return etcp; @@ -182,7 +186,7 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) { // Allocates memory from data_pool and places in input queue // Returns: 0 on success, -1 on failure int etcp_send(struct ETCP_CONN* etcp, const void* data, uint16_t len) { - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_send: ENTER etcp=%p, data=%p, len=%zu", etcp, data, len); +// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_send: ENTER etcp=%p, data=%p, len=%zu", etcp, data, len); if (!etcp || !data || len == 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_send: invalid parameters (etcp=%p, data=%p, len=%zu)", etcp, data, len); @@ -222,7 +226,7 @@ int etcp_send(struct ETCP_CONN* etcp, const void* data, uint16_t len) { pkt->seq = 0; // Will be assigned by input_queue_cb pkt->timestamp = 0; // Will be set by input_queue_cb pkt->pkt_data = packet_data; // Point to data_pool allocation - pkt->pkt_len = len; + pkt->ll.size = len; // размер packet_data DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_send: created PACKET %p with data %p (len=%zu)", pkt, packet_data, len); @@ -261,7 +265,7 @@ static void input_queue_cb(struct ll_queue* q, void* arg) { struct ETCP_FRAGMENT* in_pkt = queue_data_get(q); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: processing ETCP_FRAGMENT %p (seq=%u, len=%u, data=%p)", - in_pkt, in_pkt->seq, in_pkt->pkt_len, in_pkt->pkt_data); + in_pkt, in_pkt->seq, in_pkt->ll.size, in_pkt->pkt_data); // Create INFLIGHT_PACKET struct INFLIGHT_PACKET* p = memory_pool_alloc(etcp->inflight_pool); @@ -274,9 +278,11 @@ static void input_queue_cb(struct ll_queue* q, void* arg) { // Setup inflight packet (based on protocol.txt) memset(p, 0, sizeof(*p)); - p->pkt = in_pkt; // Note: ETCP_FRAGMENT is now owned by inflight; don't free here p->seq = etcp->next_tx_id++; // Assign seq p->state = INFLIGHT_STATE_WAIT_SEND; + p->last_timestamp = 0; + p->pkt_data = in_pkt->pkt_data; + p->ll.size = in_pkt->ll.size; // Add to send queue if (queue_data_put(etcp->input_send_q, p, p->seq) != 0) { @@ -292,9 +298,64 @@ static void input_queue_cb(struct ll_queue* q, void* arg) { static void input_send_q_cb(struct ll_queue* q, void* arg) {// etcp->input_send_q processing struct ETCP_CONN* etcp=(struct ETCP_CONN*)arg; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_send_q_cb: "); - etcp_request_pkt(etcp); + + size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); + size_t send_q_pkts = queue_entry_count(etcp->input_send_q); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_send_q_cb: input_send_q status: %d pkt %d bytes", send_q_pkts, send_q_bytes); + + etcp_conn_process_send_queue(etcp); + } + + +static void ack_timeout_check(void* arg) { + struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; + uint64_t now = get_current_time_units(); + uint64_t timeout = 1000;//(uint64_t)(etcp->rtt_avg_10 * RETRANS_K1) + (uint64_t)(etcp->jitter * RETRANS_K2); + + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "ack_timeout_check: starting check, now=%llu, timeout=%llu, rtt_avg_10=%u, jitter=%u", + (unsigned long long)now, (unsigned long long)timeout, etcp->rtt_avg_10, etcp->jitter); + + struct ll_entry* current = etcp->input_wait_ack->head; + while (current) { + struct ll_entry* next = current->next; // Save next as we may remove current + struct INFLIGHT_PACKET* pkt = (struct INFLIGHT_PACKET*)current; + uint64_t elapsed = now - pkt->last_timestamp; + if (elapsed > timeout) { + DEBUG_WARN(DEBUG_CATEGORY_ETCP, "ack_timeout_check: timeout for seq=%u, elapsed=%llu, timeout=%llu, send_count=%u", + pkt->seq, (unsigned long long)elapsed, (unsigned long long)timeout, pkt->send_count); + + // Increment counters + pkt->send_count++; + pkt->retrans_req_count++; // Optional, if used for retrans request logic + pkt->last_timestamp = now; + pkt->last_link = NULL; // Reset last link for re-selection + + // Remove from wait_ack + queue_remove_data(etcp->input_wait_ack, pkt); + + // Change state and add to send_q for retransmission + pkt->state = INFLIGHT_STATE_WAIT_SEND; + queue_data_put(etcp->input_send_q, pkt, pkt->seq); + + // Update stats + etcp->retransmissions_count++; + } + else {// не надо до конца сканировать - они уже сортированы по таймстемпу т.к. очередь fifo, а timestamp = время добавления в очередь = время отправки + // shedule timer + int64_t next_timeout=timeout - elapsed; + etcp->retrans_timer = uasync_set_timeout(etcp->instance->ua, next_timeout+10, etcp, ack_timeout_check); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "ack_timeout_check: rescheduled timer for %llu units", next_timeout); + break; + } + + current = next; } +} + +static void wait_ack_cb(struct ll_queue* q, void* arg) { + struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; + ack_timeout_check(etcp); +} // Подготовить и отправить кодограмму // вызывается линком когда освобождается или очередью если появляются данные на передачу @@ -303,32 +364,35 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { struct ETCP_LINK* link = etcp_loadbalancer_select_link(etcp); if (!link) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: no link available"); - queue_resume_callback(etcp->input_send_q); - return NULL; + return NULL;// если линков нет - ждём появления свободного } - size_t send_q_bytes = queue_total_bytes(etcp->input_send_q); + size_t send_q_size = queue_entry_count(etcp->input_send_q); - if (send_q_bytes == 0) {// сгребаем из других мест - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, trying to resume"); + if (send_q_size == 0) {// сгребаем из input_queue + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: input_send_q empty, check if avail input_queue -> inflight"); input_queue_try_resume(etcp); return NULL; } // First, check if there's a packet in input_send_q (retrans or new) - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: getting packet from input_send_q"); +// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: getting packet from input_send_q"); struct INFLIGHT_PACKET* inf_pkt = queue_data_get(etcp->input_send_q); - if (!inf_pkt) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: no packet available from input_send_q"); + if (inf_pkt) { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: prepare udp dgram for send packet %p (seq=%u, len=%u)", inf_pkt, inf_pkt->seq, inf_pkt->ll.size); + + inf_pkt->last_timestamp=get_current_time_units(); + inf_pkt->send_count++; + inf_pkt->state=INFLIGHT_STATE_WAIT_ACK; + queue_data_put(etcp->input_wait_ack, inf_pkt, inf_pkt->seq);// move dgram to wait_ack queue + } + + size_t ack_q_size = queue_entry_count(etcp->ack_q); + + if (!inf_pkt && ack_q_size == 0) { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: no data/ack to send"); return NULL; } - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: got INFLIGHT_PACKET %p (seq=%u, len=%u)", - inf_pkt, inf_pkt->seq, inf_pkt->pkt_len); - - inf_pkt->last_timestamp=get_current_time_units(); - inf_pkt->send_count++; - inf_pkt->state=INFLIGHT_STATE_WAIT_ACK; - queue_data_put(etcp->input_send_q, inf_pkt, inf_pkt->seq);// move dgram to wait_ack queue struct ETCP_DGRAM* dgram = memory_pool_alloc(etcp->instance->pkt_pool); if (!dgram) { @@ -340,6 +404,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { dgram->noencrypt_len=0; dgram->timestamp=get_current_timestamp(); +// формат ack: [01] [elements count] [4 байта last_delivered_id] и <[4 байта seq][2 байта recv_ts][2 байта txrx delay ts]> x count dgram->data[0]=1;// ack int ptr=2; @@ -351,32 +416,40 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) { // тут (потом) добавим опциональные заголовки struct ACK_PACKET* ack_pkt; while (ack_pkt = queue_data_get(etcp->ack_q)) { - // seq 4 байта - dgram->data[ptr++]=ack_pkt->seq; - dgram->data[ptr++]=ack_pkt->seq>>8; - dgram->data[ptr++]=ack_pkt->seq>>16; - dgram->data[ptr++]=ack_pkt->seq>>24; - - // ts приема 2 байта - dgram->data[ptr++]=ack_pkt->recv_timestamp; - dgram->data[ptr++]=ack_pkt->recv_timestamp>>8; - - // время задержки 2 байта между recv и ack - uint16_t dly=get_current_timestamp()-ack_pkt->recv_timestamp; - dgram->data[ptr++]=dly; - dgram->data[ptr++]=dly>>8; + // seq 4 байта + dgram->data[ptr++]=ack_pkt->seq; + dgram->data[ptr++]=ack_pkt->seq>>8; + dgram->data[ptr++]=ack_pkt->seq>>16; + dgram->data[ptr++]=ack_pkt->seq>>24; + + // ts приема 2 байта + dgram->data[ptr++]=ack_pkt->recv_timestamp; + dgram->data[ptr++]=ack_pkt->recv_timestamp>>8; + + // время задержки 2 байта между recv и ack + uint16_t dly=get_current_timestamp()-ack_pkt->recv_timestamp; + dgram->data[ptr++]=dly; + dgram->data[ptr++]=dly>>8; + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: add ACK N%d dTS=%d", ack_pkt->seq, dly); - if (inf_pkt->pkt_len+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки) + if (inf_pkt && inf_pkt->ll.size+ptr>=etcp->mtu-10) break;// pkt len (надо просчитать точнее включая все заголовки) + if (ptr>500) break; } dgram->data[1]=ptr/8; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: building packet data (seq=%u, len=%u)", inf_pkt->seq, inf_pkt->pkt_len); - dgram->data[ptr++]=0;// payload - memcpy(&dgram->data[ptr], inf_pkt->pkt_data, inf_pkt->pkt_len); ptr+=inf_pkt->pkt_len; + if (inf_pkt) { + // фрейм data (0) обязательно в конец + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: packet with payload (seq=%u, len=%u), ack_size=%d", inf_pkt->seq, inf_pkt->ll.size, dgram->data[1]); + dgram->data[ptr++]=0;// payload + memcpy(&dgram->data[ptr], inf_pkt->pkt_data, inf_pkt->ll.size); ptr+=inf_pkt->ll.size; + } + else { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: built packet with %d bytes total", dgram->data_len); + } + dgram->data_len=ptr; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_request_pkt: built packet with %d bytes total", dgram->data_len); return dgram; } @@ -397,7 +470,13 @@ static void etcp_conn_process_send_queue(struct ETCP_CONN* etcp) { } } - +static void ack_response_timer_cb(void* arg) {// проверяем неотправленные ack response и отправляем если надо. + struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg; + etcp_conn_process_send_queue(etcp);// проталкиваем (она же должна отправлять только ack если больше ничего нет) +// если ack все еще заняты - обновляем таймаут + if (etcp->ack_q->count) etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb); + else etcp->ack_resp_timer=NULL; +} // ====================================================================== Прием данных @@ -421,7 +500,7 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) { } DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_output_try_assembly: assembling packet id=%u (len=%u)", - rx_pkt->seq, rx_pkt->pkt_len); + rx_pkt->seq, rx_pkt->ll.size); // Simply move ETCP_FRAGMENT from recv_q to output_queue - no data copying needed // Remove from recv_q first @@ -429,7 +508,7 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) { // Add to output_queue using the same ETCP_FRAGMENT structure if (queue_data_put(etcp->output_queue, rx_pkt, next_expected_id) == 0) { - delivered_bytes += rx_pkt->pkt_len; + delivered_bytes += rx_pkt->ll.size; delivered_count++; DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_output_try_assembly: moved packet id=%u to output_queue", next_expected_id); @@ -501,8 +580,8 @@ void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t d } // Update connection statistics - etcp->unacked_bytes -= acked_pkt->pkt_len; - etcp->bytes_sent_total += acked_pkt->pkt_len; + etcp->unacked_bytes -= acked_pkt->ll.size; + etcp->bytes_sent_total += acked_pkt->ll.size; etcp->ack_packets_count++; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_ack_recv: removed packet seq=%u from wait_ack, unacked_bytes now %u", @@ -562,7 +641,10 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { p->pkt_timestamp=pkt->timestamp; p->recv_timestamp=get_current_timestamp(); queue_data_put(etcp->ack_q, p, p->seq); - + if (etcp->ack_resp_timer == NULL) { + etcp->ack_resp_timer = uasync_set_timeout(etcp->instance->ua, ACK_DELAY_TB, etcp, ack_response_timer_cb); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_input: set ack_timer for delayed ACK send"); + } if ((int32_t)(etcp->last_delivered_id-seq)<0) if (queue_find_data_by_id(etcp->recv_q, seq)==NULL) {// проверяем есть ли пакет с этим seq uint32_t pkt_len=len-5; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_input: adding packet seq=%u to recv_q (last_delivered_id=%u)", seq, etcp->last_delivered_id); @@ -572,7 +654,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { rx_pkt->seq=seq; rx_pkt->timestamp=pkt->timestamp; rx_pkt->pkt_data=payload_data; - rx_pkt->pkt_len=pkt_len; + rx_pkt->ll.size=pkt_len; // Copy the actual payload data memcpy(payload_data, data + 5, pkt_len); queue_data_put(etcp->recv_q, rx_pkt, seq); diff --git a/src/etcp.h b/src/etcp.h index 590a825e..fe596215 100644 --- a/src/etcp.h +++ b/src/etcp.h @@ -37,14 +37,12 @@ uint64_t get_current_time_units(void); struct INFLIGHT_PACKET { struct ll_entry ll; struct ETCP_LINK* last_link; // Last sent link - void* pkt; // Packet (data для ll_entry) uint64_t last_timestamp; // Last send timestamp uint32_t seq; // packet seq (ID по документации) uint8_t send_count; // Number of sends uint8_t retrans_req_count; // Number of retrans requests uint8_t state; // WAIT_ACK or WAIT_SEND uint8_t* pkt_data; - uint16_t pkt_len; }; // Список пакетов для сборки. собирается в ll_queue (используем быстрый поиск с хешем) @@ -53,7 +51,6 @@ struct ETCP_FRAGMENT { uint32_t seq; uint16_t timestamp; uint8_t* pkt_data; - uint16_t pkt_len; }; struct ACK_PACKET { @@ -104,6 +101,7 @@ struct ETCP_CONN { uint32_t rx_ack_till;// из ack пакета - по какой пакет получено и собрано на уданенной стороне // Metrics (RTT, jitter, etc.) + uint16_t retrans_delay; uint16_t rtt_last; uint16_t rtt_avg_10; uint16_t rtt_avg_100; @@ -119,7 +117,7 @@ struct ETCP_CONN { // Timers void* retrans_timer; // Retrans check timer - void* ack_timer; // ACK send timer + void* ack_resp_timer; // ACK send timer // Bandwidth measurement state uint8_t burst_in_progress; // Burst transmission flag diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 7b875aea..f6474fe5 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -15,105 +15,21 @@ #include "../lib/debug_config.h" #include "etcp_loadbalancer.h" #include - -// Packet dump configuration -#define ETCP_PACKET_DUMP_COMPACT 1 // Use compact single-line format -#define ETCP_PACKET_DUMP_FULL 0 // Use full multi-line format (legacy) #include -// Use debug categories from debug_config.h - // Forward declaration static void etcp_connections_read_callback(int fd, void* arg); - -// Single-line packet dump for debug output -static void dump_packet_bytes(const char* prefix, const uint8_t* data, size_t len) { - // Build packet data as hex string for single-line output - char hex_buf[513]; // 256 bytes * 2 chars + 1 for null terminator - size_t hex_len = 0; - size_t show_len = (len > 128) ? 128 : len; // Show max 128 bytes - - for (size_t i = 0; i < show_len && hex_len < 512 - 3; i++) { - hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]); - if (i < show_len - 1 && (i + 1) % 32 == 0) { // Add space every 32 bytes - hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, " "); - } - } - - if (len > 128) { - hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "..."); - } - - // Single-line debug output with packet info - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] %s: len=%zu hex=%s", prefix, len, hex_buf); - - // Additional debug info for first few bytes - if (len >= 2) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP] %s: first_bytes=%02x%02x last_bytes=%02x%02x", - prefix, data[0], data[1], data[len-2], data[len-1]); - } -} - -// Compact packet info for high-frequency logging -static void log_packet_compact(const char* prefix, const uint8_t* data, size_t len, struct ETCP_LINK* link) { - if (!data || len == 0) return; - - // Extract packet type from first byte - uint8_t pkt_type = data[0]; - uint16_t timestamp = 0; - - if (len >= 3) { - // Extract timestamp from bytes 1-2 (big endian) - timestamp = (data[1] << 8) | data[2]; - } - - // Single line debug with key packet info - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] %s: link=%p type=0x%02x ts=%u len=%zu", - prefix, link, pkt_type, timestamp, len); - - // Trace level for detailed hex dump - if (len <= 64) { // Only for small packets to avoid spam - char hex_buf[133]; // 64 bytes * 2 + 1 - size_t hex_len = 0; - for (size_t i = 0; i < len && hex_len < sizeof(hex_buf) - 3; i++) { - hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]); - } - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP] %s: data=%s", prefix, hex_buf); - } -} +struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance); +static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link); // Unified packet dump function - uses configured format static void packet_dump(const char* prefix, const uint8_t* data, size_t len, struct ETCP_LINK* link) { if (!data || len == 0) return; - #if ETCP_PACKET_DUMP_COMPACT - // Compact single-line format - log_packet_compact(prefix, data, len, link); - #else // Full multi-line format (legacy) - dump_packet_bytes(prefix, data, len); - #endif -} - -// Legacy multi-line dump for compatibility (kept but not used) -static void dump_packet_bytes_multiline(const char* prefix, const uint8_t* data, size_t len) { - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DUMP] %s: len=%zu; dump: ", prefix, len); - size_t show = len < 160 ? len : 160; - for (size_t i = 0; i < show; i++) { - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "%02x ", data[i]); - } - if (len > 160) DEBUG_INFO(DEBUG_CATEGORY_ETCP, "..."); - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n"); + log_dump(prefix, data, len); } - -// Forward declarations for missing functions -struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance); - -// CONNECTION MANAGEMENT (!!!это всё должно быть static!!!) -static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link); - - // Отправка кодограмм протокола (!!!это всё должно быть static!!!) static void etcp_link_send_init(struct ETCP_LINK* link); static int etcp_link_send_reset(struct ETCP_LINK* link); @@ -498,7 +414,7 @@ int etcp_encrypt_send(struct ETCP_DGRAM* dgram) { dgram->timestamp=get_current_timestamp(); // DUMP: Show packet before encryption - packet_dump("ECTP_ENCRYPT_SEND", dgram->data, dgram->data_len, dgram->link); + log_dump("ECTP_ENCRYPT_SEND", dgram->data, dgram->data_len); sc_encrypt(sc, (uint8_t*)&dgram->timestamp/*не править это, тут верно!*/, sizeof(uint16_t) + len, enc_buf, &enc_buf_len); if (enc_buf_len == 0) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_encrypt_send: encryption failed for node %llu", (unsigned long long)dgram->link->etcp->instance->node_id); @@ -573,7 +489,7 @@ static void etcp_connections_read_callback(int fd, void* arg) { // printf("[ETCP] Received packet: %zd bytes from address\n", recv_len); // DUMP: Show received packet content - packet_dump("RECV in:", data, recv_len, NULL); // link unknown at this point + log_dump("RECV in:", data, recv_len); // link unknown at this point struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool); if (!pkt) return; @@ -628,6 +544,9 @@ static void etcp_connections_read_callback(int fd, void* arg) { if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create connection"); goto ec_fr; }// облом memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ conn->peer_node_id=peer_id; + char buf[128]; + addr_to_string(&addr, buf, sizeof(buf)); + DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "New connection from %s peer_id=%ld etcp=%p", buf, peer_id, conn); } else {// check keys если существующее подключение if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: peer key mismatch for node %llu", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные. @@ -661,7 +580,10 @@ static void etcp_connections_read_callback(int fd, void* arg) { } if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) { - DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu", (unsigned long long)link->etcp->instance->node_id); + DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu len=%d", (unsigned long long)link->etcp->instance->node_id, recv_len); + log_dump("my_privkey",&link->etcp->crypto_ctx.pk->private_key, SC_PRIVKEY_SIZE); + log_dump("my_pubkey",&link->etcp->crypto_ctx.pk->public_key, SC_PUBKEY_SIZE); + log_dump("peer_pubkey",&link->etcp->crypto_ctx.peer_public_key, SC_PUBKEY_SIZE); errorcode=6; goto ec_fr; } diff --git a/src/etcp_loadbalancer.c b/src/etcp_loadbalancer.c index 89a07d00..8fecb45e 100755 --- a/src/etcp_loadbalancer.c +++ b/src/etcp_loadbalancer.c @@ -40,28 +40,24 @@ struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) { while (link) { link_index++; - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d (%p) - initialized=%u, state=%u, load_tb=%llu, sub=%llu", - link_index, link, link->initialized, link->shaper_timer==NULL?1:0, - (unsigned long long)link->shaper_load_time_tb, (unsigned long long)link->shaper_sub_nanotime); +// DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d (%p) - initialized=%u, state=%u, load_tb=%llu, sub=%llu", +// link_index, link, link->initialized, link->shaper_timer==NULL?1:0, +// (unsigned long long)link->shaper_load_time_tb, (unsigned long long)link->shaper_sub_nanotime); - if (!link->initialized) { + if (!link->initialized || link->shaper_timer) {// если установлен таймер - значит надо ждать. не рассматриваем этот линк link = link->next; continue; } if (link->shaper_load_time_tb < now_tb-ALLOWED_DELTA) link->shaper_load_time_tb = now_tb-ALLOWED_DELTA; - + // Check if ready (load < now + burst allowance) uint64_t effective_load_ns = link->shaper_load_time_tb * TIMEBASE_NS + (link->shaper_sub_nanotime / 10); // To ns uint64_t now_ns = now_tb * TIMEBASE_NS; int64_t delta_t=(int64_t)(now_ns - effective_load_ns);// uint64 может переполняться. чтобы обеспечить правильную цикличность. - if (delta_t > SHAPER_BURST_DELAY_TB) { - // Can send (within burst window) - if (link->shaper_load_time_tb < min_load_tb || - (link->shaper_load_time_tb == min_load_tb && link->shaper_sub_nanotime < best->shaper_sub_nanotime)) { - min_load_tb = link->shaper_load_time_tb; - best = link; - } + if (link->shaper_load_time_tb < min_load_tb || (link->shaper_load_time_tb == min_load_tb && link->shaper_sub_nanotime < best->shaper_sub_nanotime)) { + min_load_tb = link->shaper_load_time_tb; + best = link; } link = link->next; } @@ -86,7 +82,8 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { struct ETCP_CONN* etcp = dgram->link ? dgram->link->etcp : NULL; if (!etcp) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_send: no ETCP_CONN associated with dgram"); - free(dgram); + memory_pool_free(etcp->instance->pkt_pool, dgram); +// free(dgram); return; } @@ -95,7 +92,8 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { dgram->link = etcp_loadbalancer_select_link(etcp); if (!dgram->link) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_send: no link available, dropping dgram"); - free(dgram); // Assume free; adjust if pooled + memory_pool_free(etcp->instance->pkt_pool, dgram); +// free(dgram); // Assume free; adjust if pooled return; } } @@ -107,7 +105,7 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { // Encrypt and send (from etcp_connections.c) int send_result = etcp_encrypt_send(dgram); - if (send_result != 0) { + if (send_result < 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_send: encrypt/send failed (%d)", send_result); // Don't return here, let the function continue to free dgram at the end } @@ -144,7 +142,8 @@ void etcp_loadbalancer_send(struct ETCP_DGRAM* dgram) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_send: updated load_tb=%llu, sub=%llu, state=%u", (unsigned long long)link->shaper_load_time_tb, (unsigned long long)link->shaper_sub_nanotime, link->shaper_timer==NULL?1:0); - free(dgram); // Free the dgram in all cases - we own it + memory_pool_free(etcp->instance->pkt_pool, dgram); +// free(dgram); // Free the dgram in all cases - we own it } // New: Notify when link is ready (called from timer or external) diff --git a/src/pkt_normalizer.c b/src/pkt_normalizer.c index ec84081e..5b5ebe5a 100644 --- a/src/pkt_normalizer.c +++ b/src/pkt_normalizer.c @@ -4,8 +4,8 @@ #include #include #include -static void packer_handler(struct ll_queue* q, void* data, void* arg); -static void unpacker_handler(struct ll_queue* q, void* data, void* arg); +static void packer_handler(struct ll_queue* q, void* arg); +static void unpacker_handler(struct ll_queue* q, void* arg); static void send_buf(struct pn_struct* pn); static int get_header(uint8_t* header, size_t L); /* Calculate fragment size from mtu */ @@ -117,7 +117,7 @@ static void send_buf(struct pn_struct* pn) { queue_data_put(pn->output, out, 0); // Новый API, ID=0 для внутренних пакетов pn->u.packer.len = 0; } -static void packer_handler(struct ll_queue* q, void* data, void* arg) { +static void packer_handler(struct ll_queue* q, void* arg) { struct pn_struct* pn = arg; size_t max = (size_t)1400; @@ -228,7 +228,7 @@ static void packer_handler(struct ll_queue* q, void* data, void* arg) { *(uint16_t*)fd = (uint16_t)payload_len; fd += 2; *fd++ = 0xFE; - memcpy(fd, data + pos, chunk); + memcpy(fd, pkt_data + pos, chunk); queue_data_put(pn->output, fout, 0); pos += chunk; remaining -= chunk; @@ -252,7 +252,7 @@ static void packer_handler(struct ll_queue* q, void* data, void* arg) { *fd++ = 0xFE; } } - memcpy(fd, data + pos, chunk); + memcpy(fd, pkt_data + pos, chunk); queue_data_put(pn->output, fout, 0); pos += chunk; remaining -= chunk; @@ -265,7 +265,7 @@ static void packer_handler(struct ll_queue* q, void* data, void* arg) { // Add to buffer uint8_t* p = pn->u.packer.buf + pn->u.packer.len; memcpy(p, header, (size_t)hsize); - memcpy(p + hsize, data, L); + memcpy(p + hsize, pkt_data, L); pn->u.packer.len += needed; } queue_data_free(pkt_data); @@ -379,8 +379,7 @@ void pkt_normalizer_flush(struct pn_struct* pn) { send_buf(pn); } } -static void unpacker_handler(struct ll_queue* q, void* data, void* arg) { - (void)data; +static void unpacker_handler(struct ll_queue* q, void* arg) { struct pn_struct* pn = arg; while (queue_entry_count(q) > 0) { uint8_t* entry = queue_data_get(q); diff --git a/tests/1 b/tests/1 index 032e77a4..4773e023 100644 --- a/tests/1 +++ b/tests/1 @@ -55,4 +55,47 @@ c5 94 f3 3c 91 f3 a2 22 27 95 c2 c1 10 c5 27 bf 21 4a d1 00 91 97 ce 14 55 6c b1 [ETCP DUMP] RECV decrypted:: len=11; dump: 03 11 11 11 11 11 11 11 11 00 00 [ETCP] code=3 INFO: Received INIT_RESPONSE from server_node_id=1229782938247303441, mtu=0 -[ETCP] Link initialized successfully! Server node_id=1229782938247303441, m \ No newline at end of file +[ETCP] Link initialized successfully! Server node_id=1229782938247303441, mmake[1]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. +make: *** [Makefile:1494: check-am] Ошибка 2 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c: In function ‘test_memory_pool_integration’: +test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) + 670 | queue_data_free(entry_data3); + | ^~~~~~~~~~~ +test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in +test_ll_queue.c: In function ‘test_edge_cases’: +test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) + 719 | queue_data_free(entry_retrieved); + | ^~~~~~~~~~~~~~~ +test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: +test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) + 1005 | queue_data_put(q, entry_data, data->id); + | ^~~~~~~~~~ +make: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | +test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined + 20 | #define DEBUG_CATEGORY_LL_QUEUE 1 + | +In file included from test_ll_queue.c:16: +../lib/debug_config.h:32: note: this is the location of the previous definition + 32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module + | diff --git a/tests/test_config_debug b/tests/test_config_debug index a6732f61..a1321c91 100755 Binary files a/tests/test_config_debug and b/tests/test_config_debug differ diff --git a/tests/test_crypto b/tests/test_crypto index 083adb63..6ebbe9d6 100755 Binary files a/tests/test_crypto and b/tests/test_crypto differ diff --git a/tests/test_debug_categories b/tests/test_debug_categories index 670cc731..634ee657 100755 Binary files a/tests/test_debug_categories and b/tests/test_debug_categories differ diff --git a/tests/test_ecc_encrypt b/tests/test_ecc_encrypt index 43f89b10..43fa4e4a 100755 Binary files a/tests/test_ecc_encrypt and b/tests/test_ecc_encrypt differ diff --git a/tests/test_etcp_crypto b/tests/test_etcp_crypto index 4c705a6e..0c7fc6a3 100755 Binary files a/tests/test_etcp_crypto and b/tests/test_etcp_crypto differ diff --git a/tests/test_etcp_minimal b/tests/test_etcp_minimal index 551d71cf..fa86e46d 100755 Binary files a/tests/test_etcp_minimal and b/tests/test_etcp_minimal differ diff --git a/tests/test_etcp_simple_traffic b/tests/test_etcp_simple_traffic index 0817062f..8bcdbba8 100755 Binary files a/tests/test_etcp_simple_traffic and b/tests/test_etcp_simple_traffic differ diff --git a/tests/test_etcp_simple_traffic.c.backup b/tests/test_etcp_simple_traffic.c.backup deleted file mode 100644 index 7cc4d653..00000000 --- a/tests/test_etcp_simple_traffic.c.backup +++ /dev/null @@ -1,491 +0,0 @@ -// test_etcp_simple_traffic.c - Simplified ETCP traffic flow debugging without sockets -// This test focuses purely on ETCP protocol traffic analysis using direct packet injection - -#include "../src/etcp.h" -#include "../src/etcp_connections.h" -#include "../src/etcp_loadbalancer.h" -#include "../lib/u_async.h" -#include "../lib/debug_config.h" -#include "../lib/memory_pool.h" -#include "../lib/ll_queue.h" -#include "../src/secure_channel.h" -#include "../src/crc32.h" -#include -#include -#include -#include -#include -#include -#include - -// Simplified traffic analysis - focus on ETCP protocol packets only -typedef struct { - uint64_t timestamp; - uint16_t packet_id; - uint8_t packet_type; - uint16_t data_len; - uint8_t data[1500]; // Copy of packet data for analysis -} packet_capture_t; - -typedef struct { - packet_capture_t captures[1000]; - size_t capture_count; - pthread_mutex_t lock; -} traffic_analyzer_t; - -// Test instance - simplified version without actual sockets -typedef struct { - struct UTUN_INSTANCE* instance; - struct ETCP_CONN* etcp_conn; - traffic_analyzer_t* analyzer; - uint64_t node_id; - int is_server; - int running; -} test_instance_t; - -// Global test state -static test_instance_t* server_instance = NULL; -static test_instance_t* client_instance = NULL; - -// Enhanced packet analysis function -static void analyze_etcp_packet(struct ETCP_CONN* etcp, uint8_t* data, uint16_t len, const char* direction) { - if (!etcp || !data || len == 0) { - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "analyze_etcp_packet: invalid parameters (etcp=%p, data=%p, len=%u)", - etcp, data, len); - return; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "PACKET %s: etcp=%p, len=%u, first_byte=0x%02x", - direction, etcp, len, data[0]); - - // Parse ETCP sections - uint16_t pos = 0; - int section_count = 0; - - while (pos < len) { - if (pos + 3 > len) { - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Invalid section header at pos %u (need 3 bytes, have %u)", - pos, len - pos); - break; - } - - uint8_t section_type = data[pos]; - uint16_t section_len = (data[pos+1] << 8) | data[pos+2]; - - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Section %d: type=0x%02x, len=%u at pos=%u", - section_count, section_type, section_len, pos); - - if (pos + 3 + section_len > len || section_len > 1500) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Section length %u exceeds packet boundary (pos=%u, total=%u) or max size", - section_len, pos, len); - break; - } - - // Analyze specific section types - uint8_t* section_data = data + pos + 3; - - switch (section_type) { - case ETCP_SECTION_PAYLOAD: { - if (section_len >= 2) { - uint16_t packet_id = (section_data[0] << 8) | section_data[1]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " PAYLOAD: packet_id=%u, payload_len=%u", - packet_id, section_len - 2); - } - break; - } - case ETCP_SECTION_ACK: { - if (section_len >= 1) { - uint8_t ack_count = section_data[0]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " ACK: count=%u", ack_count); - for (int i = 0; i < ack_count && i*4+5 <= section_len; i++) { - uint16_t ack_id = (section_data[1+i*4] << 8) | section_data[2+i*4]; - uint16_t ack_ts = (section_data[3+i*4] << 8) | section_data[4+i*4]; - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, " ACK[%d]: id=%u, ts=%u", i, ack_id, ack_ts); - } - } - break; - } - case ETCP_SECTION_TIMESTAMP: { - if (section_len == 2) { - uint16_t ts = (section_data[0] << 8) | section_data[1]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " TIMESTAMP: ts=%u", ts); - } - break; - } - case ETCP_INIT_REQUEST: { - if (section_len >= 76) { // node_id(8) + mtu(2) + keepalive(2) + pubkey(64) - uint64_t node_id = 0; - for (int i = 0; i < 8; i++) { - node_id = (node_id << 8) | section_data[i]; - } - uint16_t mtu = (section_data[8] << 8) | section_data[9]; - uint16_t keepalive = (section_data[10] << 8) | section_data[11]; - DEBUG_INFO(DEBUG_CATEGORY_ETCP, " INIT_REQUEST: node_id=%llu, mtu=%u, keepalive=%u", - (unsigned long long)node_id, mtu, keepalive); - } - break; - } - case ETCP_INIT_RESPONSE: { - if (section_len >= 10) { // node_id(8) + mtu(2) - uint64_t node_id = 0; - for (int i = 0; i < 8; i++) { - node_id = (node_id << 8) | section_data[i]; - } - uint16_t mtu = (section_data[8] << 8) | section_data[9]; - DEBUG_INFO(DEBUG_CATEGORY_ETCP, " INIT_RESPONSE: node_id=%llu, mtu=%u", - (unsigned long long)node_id, mtu); - } - break; - } - default: { - if (section_type >= ETCP_SECTION_RETRANS && section_type <= ETCP_SECTION_RETRANS + 0x1F) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " RETRANS: base_id=%u", section_type - ETCP_SECTION_RETRANS); - // Parse retransmission requests - for (int i = 0; i < section_len; i += 2) { - if (i+1 < section_len) { - uint16_t req_id = (section_data[i] << 8) | section_data[i+1]; - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, " RETRANS_REQ: id=%u", req_id); - } - } - } else { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " UNKNOWN: type=0x%02x, len=%u", section_type, section_len); - } - break; - } - } - - pos += 3 + section_len; - section_count++; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Packet analysis complete: %d sections processed", section_count); -} - -// Simulate packet reception for analysis -static void simulate_packet_rx(test_instance_t* inst, uint8_t* data, uint16_t len) { - if (!inst || !inst->etcp_conn || !data) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "SIMULATE RX: inst=%s, len=%u", - inst->is_server ? "server" : "client", len); - - // Analyze the packet first - analyze_etcp_packet(inst->etcp_conn, data, len, "SIM_RX"); - - // Create a mock ETCP_DGRAM for processing - struct ETCP_DGRAM mock_dgram; - memset(&mock_dgram, 0, sizeof(mock_dgram)); - mock_dgram.link = NULL; // Will be set by connection processing - mock_dgram.data_len = len > 1500 ? 1500 : len; - mock_dgram.timestamp = get_current_timestamp(); - if (mock_dgram.data_len > 0) { - memcpy(mock_dgram.data, data, mock_dgram.data_len); - } - - // Process through ETCP input - etcp_conn_input(&mock_dgram); -} - -// Simulate packet transmission for analysis -static void simulate_packet_tx(test_instance_t* inst) { - if (!inst || !inst->etcp_conn) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "SIMULATE TX: inst=%s", - inst->is_server ? "server" : "client"); - - struct ETCP_DGRAM* dgram = etcp_request_pkt(inst->etcp_conn); - if (dgram) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Generated packet: len=%u", dgram->data_len); - analyze_etcp_packet(inst->etcp_conn, dgram->data, dgram->data_len, "SIM_TX"); - - // In a real scenario, this would be sent over the network - // For now, we just analyze it and free it - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Would send %u bytes to peer", dgram->data_len); - } else { - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "No packets available to send"); - } -} - -// Create simplified test instance -static test_instance_t* create_simple_instance(uint64_t node_id, int is_server) { - test_instance_t* inst = calloc(1, sizeof(test_instance_t)); - if (!inst) return NULL; - - inst->node_id = node_id; - inst->is_server = is_server; - inst->running = 1; - - // Create traffic analyzer - inst->analyzer = calloc(1, sizeof(traffic_analyzer_t)); - if (inst->analyzer) { - pthread_mutex_init(&inst->analyzer->lock, NULL); - } - - // Create UTUN instance directly without TUN device - inst->instance = calloc(1, sizeof(struct UTUN_INSTANCE)); - if (!inst->instance) { - free(inst->analyzer); - free(inst); - return NULL; - } - - // Initialize the instance manually - inst->instance->node_id = node_id; - inst->instance->running = 1; - inst->instance->log_fp = stderr; - - // Initialize crypto keys (hardcoded for test) - const char* priv_key_hex = "67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb"; - const char* pub_key_hex = "1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9"; - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Parsing crypto keys: priv_len=%zu, pub_len=%zu", - strlen(priv_key_hex), strlen(pub_key_hex)); - - // Parse keys - for (int i = 0; i < 32; i++) { - if (sscanf(&priv_key_hex[i*2], "%2hhx", &inst->instance->my_keys.private_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse private key byte %d", i); - } - } - for (int i = 0; i < 64; i++) { - if (sscanf(&pub_key_hex[i*2], "%2hhx", &inst->instance->my_keys.public_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse public key byte %d", i); - } - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Keys parsed successfully"); - - // Create uasync context - inst->instance->ua = uasync_create(); - if (!inst->instance->ua) { - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - uasync_init_instance(inst->instance->ua); - - // Create packet pool - inst->instance->pkt_pool = memory_pool_init(1600); // 1600 byte packets - if (!inst->instance->pkt_pool) { - uasync_destroy(inst->instance->ua); - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - - // Create ETCP connection - inst->etcp_conn = etcp_connection_create(inst->instance); - if (!inst->etcp_conn) { - memory_pool_destroy(inst->instance->pkt_pool); - uasync_destroy(inst->instance->ua); - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Created %s instance (node_id=%llu)", - is_server ? "server" : "client", (unsigned long long)node_id); - - return inst; -} - -// Cleanup simplified test instance -static void destroy_simple_instance(test_instance_t* inst) { - if (!inst) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Destroying %s instance", inst->is_server ? "server" : "client"); - - if (inst->etcp_conn) { - etcp_connection_close(inst->etcp_conn); - } - - if (inst->instance) { - if (inst->instance->pkt_pool) { - memory_pool_destroy(inst->instance->pkt_pool); - } - if (inst->instance->ua) { - uasync_destroy(inst->instance->ua); - } - free(inst->instance); - } - - if (inst->analyzer) { - pthread_mutex_destroy(&inst->analyzer->lock); - free(inst->analyzer); - } - - free(inst); -} - -// Generate test packets manually -static void generate_test_packets(test_instance_t* inst, int count) { - if (!inst || !inst->etcp_conn) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Generating %d test packets for %s instance", - count, inst->is_server ? "server" : "client"); - - for (int i = 0; i < count; i++) { - struct ll_entry* entry = queue_entry_new(100); // 100 byte packets - if (entry) { - // Fill with test data - uint8_t* data = (uint8_t*)ll_entry_data(entry); - for (int j = 0; j < 100; j++) { - data[j] = (uint8_t)((i+1) * 10 + j); // Pattern data - } - queue_entry_put(inst->etcp_conn->input_queue, entry); - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Queued test packet %d for %s", - i+1, inst->is_server ? "server" : "client"); - } - } -} - -// Main test function -int main(int argc, char* argv[]) { - printf("=== ETCP Traffic Flow Debugging Test (Simplified) ===\n"); - - // Initialize debug system - debug_config_init(); - debug_set_level(DEBUG_LEVEL_DEBUG); - debug_enable_category(DEBUG_CATEGORY_ETCP); - debug_enable_timestamp(1); - debug_enable_function_name(1); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Starting simplified ETCP traffic flow debugging test"); - - // Create server instance - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating server instance..."); - server_instance = create_simple_instance(0x1111111111111111ULL, 1); - if (!server_instance) { - fprintf(stderr, "Failed to create server instance\n"); - return 1; - } - - // Create client instance - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating client instance..."); - client_instance = create_simple_instance(0x2222222222222222ULL, 0); - if (!client_instance) { - fprintf(stderr, "Failed to create client instance\n"); - destroy_simple_instance(server_instance); - return 1; - } - - // Phase 1: Test basic packet generation and analysis - printf("\n=== Phase 1: Basic Packet Generation ===\n"); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Generating test packets..."); - generate_test_packets(server_instance, 5); - generate_test_packets(client_instance, 5); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating packet transmission..."); - - // Let server generate some packets - for (int i = 0; i < 3; i++) { - simulate_packet_tx(server_instance); - usleep(10000); // 1ms delay - } - - // Let client generate some packets - for (int i = 0; i < 3; i++) { - simulate_packet_tx(client_instance); - usleep(10000); // 1ms delay - } - - // Phase 2: Test connection establishment simulation - printf("\n=== Phase 2: Connection Establishment Simulation ===\n"); - - // Simulate INIT_REQUEST from client - smaller version for testing - uint8_t init_request[] = { - ETCP_INIT_REQUEST, // Section type - 0x00, 0x14, // Section length (20 bytes) - smaller for test - // Node ID (8 bytes) - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - // MTU (2 bytes) - 0x05, 0xDC, // 1500 - // Keepalive (2 bytes) - 0x00, 0x64, // 100 - // Fake public key (8 bytes) - just for testing - 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22 - }; - // Total: 3 + 20 = 23 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating INIT_REQUEST from client..."); - simulate_packet_rx(server_instance, init_request, sizeof(init_request)); - - // Simulate INIT_RESPONSE from server - uint8_t init_response[] = { - ETCP_INIT_RESPONSE, // Section type - 0x00, 0x0A, // Section length (10 bytes) - // Node ID (8 bytes) - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - // MTU (2 bytes) - 0x05, 0xDC // 1500 - }; - // Total: 3 + 10 = 13 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating INIT_RESPONSE from server..."); - simulate_packet_rx(client_instance, init_response, sizeof(init_response)); - - // Phase 3: Test mixed traffic with ACKs and timestamps - printf("\n=== Phase 3: Mixed Traffic Simulation ===\n"); - - // Generate more traffic - generate_test_packets(server_instance, 3); - generate_test_packets(client_instance, 3); - - // Simulate some packets with multiple sections - uint8_t complex_packet[] = { - // TIMESTAMP section: 3 bytes header + 2 bytes data = 5 bytes - ETCP_SECTION_TIMESTAMP, 0x00, 0x02, 0x12, 0x34, - // ACK section with 2 ACKs: 3 bytes header + 1 byte count + 2*4 bytes = 12 bytes total - ETCP_SECTION_ACK, 0x00, 0x09, 0x02, 0x00, 0x01, 0x12, 0x34, 0x00, 0x02, 0x12, 0x35, - // PAYLOAD section: 3 bytes header + 2 bytes ID + 5 bytes data = 10 bytes total - ETCP_SECTION_PAYLOAD, 0x00, 0x08, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F // "Hello" - }; - // Total: 5 + 12 + 10 = 27 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating complex packet with multiple sections..."); - simulate_packet_rx(server_instance, complex_packet, sizeof(complex_packet)); - - // Phase 4: Test retransmission requests - printf("\n=== Phase 4: Retransmission Testing ===\n"); - - uint8_t retrans_request[] = { - ETCP_SECTION_RETRANS + 0x02, // RETRANS section with base ID 2 - 0x00, 0x04, // Section length (4 bytes) - 0x00, 0x03, // Request retransmission of packet ID 3 - 0x00, 0x04 // Request retransmission of packet ID 4 - }; - // Total: 3 + 4 = 7 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating retransmission request..."); - simulate_packet_rx(client_instance, retrans_request, sizeof(retrans_request)); - - // Final packet generation - printf("\n=== Final Phase: Additional Traffic ===\n"); - - // Generate final packets - for (int i = 0; i < 5; i++) { - simulate_packet_tx(server_instance); - simulate_packet_tx(client_instance); - usleep(5000); // 0.5ms delay - } - - // Print summary - printf("\n=== Traffic Analysis Summary ===\n"); - printf("Server instance: processed packets with detailed section analysis\n"); - printf("Client instance: processed packets with detailed section analysis\n"); - printf("Connection establishment: INIT handshake simulated\n"); - printf("Mixed traffic: ACK, TIMESTAMP, PAYLOAD sections processed\n"); - printf("Retransmission: RETRANS requests processed\n"); - - // Cleanup - destroy_simple_instance(server_instance); - destroy_simple_instance(client_instance); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Test completed"); - printf("\n=== Test completed successfully ===\n"); - - return 0; -} \ No newline at end of file diff --git a/tests/test_etcp_simple_traffic.c.orig b/tests/test_etcp_simple_traffic.c.orig deleted file mode 100644 index 7cc4d653..00000000 --- a/tests/test_etcp_simple_traffic.c.orig +++ /dev/null @@ -1,491 +0,0 @@ -// test_etcp_simple_traffic.c - Simplified ETCP traffic flow debugging without sockets -// This test focuses purely on ETCP protocol traffic analysis using direct packet injection - -#include "../src/etcp.h" -#include "../src/etcp_connections.h" -#include "../src/etcp_loadbalancer.h" -#include "../lib/u_async.h" -#include "../lib/debug_config.h" -#include "../lib/memory_pool.h" -#include "../lib/ll_queue.h" -#include "../src/secure_channel.h" -#include "../src/crc32.h" -#include -#include -#include -#include -#include -#include -#include - -// Simplified traffic analysis - focus on ETCP protocol packets only -typedef struct { - uint64_t timestamp; - uint16_t packet_id; - uint8_t packet_type; - uint16_t data_len; - uint8_t data[1500]; // Copy of packet data for analysis -} packet_capture_t; - -typedef struct { - packet_capture_t captures[1000]; - size_t capture_count; - pthread_mutex_t lock; -} traffic_analyzer_t; - -// Test instance - simplified version without actual sockets -typedef struct { - struct UTUN_INSTANCE* instance; - struct ETCP_CONN* etcp_conn; - traffic_analyzer_t* analyzer; - uint64_t node_id; - int is_server; - int running; -} test_instance_t; - -// Global test state -static test_instance_t* server_instance = NULL; -static test_instance_t* client_instance = NULL; - -// Enhanced packet analysis function -static void analyze_etcp_packet(struct ETCP_CONN* etcp, uint8_t* data, uint16_t len, const char* direction) { - if (!etcp || !data || len == 0) { - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "analyze_etcp_packet: invalid parameters (etcp=%p, data=%p, len=%u)", - etcp, data, len); - return; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "PACKET %s: etcp=%p, len=%u, first_byte=0x%02x", - direction, etcp, len, data[0]); - - // Parse ETCP sections - uint16_t pos = 0; - int section_count = 0; - - while (pos < len) { - if (pos + 3 > len) { - DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Invalid section header at pos %u (need 3 bytes, have %u)", - pos, len - pos); - break; - } - - uint8_t section_type = data[pos]; - uint16_t section_len = (data[pos+1] << 8) | data[pos+2]; - - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Section %d: type=0x%02x, len=%u at pos=%u", - section_count, section_type, section_len, pos); - - if (pos + 3 + section_len > len || section_len > 1500) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Section length %u exceeds packet boundary (pos=%u, total=%u) or max size", - section_len, pos, len); - break; - } - - // Analyze specific section types - uint8_t* section_data = data + pos + 3; - - switch (section_type) { - case ETCP_SECTION_PAYLOAD: { - if (section_len >= 2) { - uint16_t packet_id = (section_data[0] << 8) | section_data[1]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " PAYLOAD: packet_id=%u, payload_len=%u", - packet_id, section_len - 2); - } - break; - } - case ETCP_SECTION_ACK: { - if (section_len >= 1) { - uint8_t ack_count = section_data[0]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " ACK: count=%u", ack_count); - for (int i = 0; i < ack_count && i*4+5 <= section_len; i++) { - uint16_t ack_id = (section_data[1+i*4] << 8) | section_data[2+i*4]; - uint16_t ack_ts = (section_data[3+i*4] << 8) | section_data[4+i*4]; - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, " ACK[%d]: id=%u, ts=%u", i, ack_id, ack_ts); - } - } - break; - } - case ETCP_SECTION_TIMESTAMP: { - if (section_len == 2) { - uint16_t ts = (section_data[0] << 8) | section_data[1]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " TIMESTAMP: ts=%u", ts); - } - break; - } - case ETCP_INIT_REQUEST: { - if (section_len >= 76) { // node_id(8) + mtu(2) + keepalive(2) + pubkey(64) - uint64_t node_id = 0; - for (int i = 0; i < 8; i++) { - node_id = (node_id << 8) | section_data[i]; - } - uint16_t mtu = (section_data[8] << 8) | section_data[9]; - uint16_t keepalive = (section_data[10] << 8) | section_data[11]; - DEBUG_INFO(DEBUG_CATEGORY_ETCP, " INIT_REQUEST: node_id=%llu, mtu=%u, keepalive=%u", - (unsigned long long)node_id, mtu, keepalive); - } - break; - } - case ETCP_INIT_RESPONSE: { - if (section_len >= 10) { // node_id(8) + mtu(2) - uint64_t node_id = 0; - for (int i = 0; i < 8; i++) { - node_id = (node_id << 8) | section_data[i]; - } - uint16_t mtu = (section_data[8] << 8) | section_data[9]; - DEBUG_INFO(DEBUG_CATEGORY_ETCP, " INIT_RESPONSE: node_id=%llu, mtu=%u", - (unsigned long long)node_id, mtu); - } - break; - } - default: { - if (section_type >= ETCP_SECTION_RETRANS && section_type <= ETCP_SECTION_RETRANS + 0x1F) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " RETRANS: base_id=%u", section_type - ETCP_SECTION_RETRANS); - // Parse retransmission requests - for (int i = 0; i < section_len; i += 2) { - if (i+1 < section_len) { - uint16_t req_id = (section_data[i] << 8) | section_data[i+1]; - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, " RETRANS_REQ: id=%u", req_id); - } - } - } else { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, " UNKNOWN: type=0x%02x, len=%u", section_type, section_len); - } - break; - } - } - - pos += 3 + section_len; - section_count++; - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Packet analysis complete: %d sections processed", section_count); -} - -// Simulate packet reception for analysis -static void simulate_packet_rx(test_instance_t* inst, uint8_t* data, uint16_t len) { - if (!inst || !inst->etcp_conn || !data) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "SIMULATE RX: inst=%s, len=%u", - inst->is_server ? "server" : "client", len); - - // Analyze the packet first - analyze_etcp_packet(inst->etcp_conn, data, len, "SIM_RX"); - - // Create a mock ETCP_DGRAM for processing - struct ETCP_DGRAM mock_dgram; - memset(&mock_dgram, 0, sizeof(mock_dgram)); - mock_dgram.link = NULL; // Will be set by connection processing - mock_dgram.data_len = len > 1500 ? 1500 : len; - mock_dgram.timestamp = get_current_timestamp(); - if (mock_dgram.data_len > 0) { - memcpy(mock_dgram.data, data, mock_dgram.data_len); - } - - // Process through ETCP input - etcp_conn_input(&mock_dgram); -} - -// Simulate packet transmission for analysis -static void simulate_packet_tx(test_instance_t* inst) { - if (!inst || !inst->etcp_conn) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "SIMULATE TX: inst=%s", - inst->is_server ? "server" : "client"); - - struct ETCP_DGRAM* dgram = etcp_request_pkt(inst->etcp_conn); - if (dgram) { - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Generated packet: len=%u", dgram->data_len); - analyze_etcp_packet(inst->etcp_conn, dgram->data, dgram->data_len, "SIM_TX"); - - // In a real scenario, this would be sent over the network - // For now, we just analyze it and free it - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Would send %u bytes to peer", dgram->data_len); - } else { - DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "No packets available to send"); - } -} - -// Create simplified test instance -static test_instance_t* create_simple_instance(uint64_t node_id, int is_server) { - test_instance_t* inst = calloc(1, sizeof(test_instance_t)); - if (!inst) return NULL; - - inst->node_id = node_id; - inst->is_server = is_server; - inst->running = 1; - - // Create traffic analyzer - inst->analyzer = calloc(1, sizeof(traffic_analyzer_t)); - if (inst->analyzer) { - pthread_mutex_init(&inst->analyzer->lock, NULL); - } - - // Create UTUN instance directly without TUN device - inst->instance = calloc(1, sizeof(struct UTUN_INSTANCE)); - if (!inst->instance) { - free(inst->analyzer); - free(inst); - return NULL; - } - - // Initialize the instance manually - inst->instance->node_id = node_id; - inst->instance->running = 1; - inst->instance->log_fp = stderr; - - // Initialize crypto keys (hardcoded for test) - const char* priv_key_hex = "67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb"; - const char* pub_key_hex = "1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9"; - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Parsing crypto keys: priv_len=%zu, pub_len=%zu", - strlen(priv_key_hex), strlen(pub_key_hex)); - - // Parse keys - for (int i = 0; i < 32; i++) { - if (sscanf(&priv_key_hex[i*2], "%2hhx", &inst->instance->my_keys.private_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse private key byte %d", i); - } - } - for (int i = 0; i < 64; i++) { - if (sscanf(&pub_key_hex[i*2], "%2hhx", &inst->instance->my_keys.public_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse public key byte %d", i); - } - } - - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Keys parsed successfully"); - - // Create uasync context - inst->instance->ua = uasync_create(); - if (!inst->instance->ua) { - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - uasync_init_instance(inst->instance->ua); - - // Create packet pool - inst->instance->pkt_pool = memory_pool_init(1600); // 1600 byte packets - if (!inst->instance->pkt_pool) { - uasync_destroy(inst->instance->ua); - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - - // Create ETCP connection - inst->etcp_conn = etcp_connection_create(inst->instance); - if (!inst->etcp_conn) { - memory_pool_destroy(inst->instance->pkt_pool); - uasync_destroy(inst->instance->ua); - free(inst->instance); - free(inst->analyzer); - free(inst); - return NULL; - } - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Created %s instance (node_id=%llu)", - is_server ? "server" : "client", (unsigned long long)node_id); - - return inst; -} - -// Cleanup simplified test instance -static void destroy_simple_instance(test_instance_t* inst) { - if (!inst) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Destroying %s instance", inst->is_server ? "server" : "client"); - - if (inst->etcp_conn) { - etcp_connection_close(inst->etcp_conn); - } - - if (inst->instance) { - if (inst->instance->pkt_pool) { - memory_pool_destroy(inst->instance->pkt_pool); - } - if (inst->instance->ua) { - uasync_destroy(inst->instance->ua); - } - free(inst->instance); - } - - if (inst->analyzer) { - pthread_mutex_destroy(&inst->analyzer->lock); - free(inst->analyzer); - } - - free(inst); -} - -// Generate test packets manually -static void generate_test_packets(test_instance_t* inst, int count) { - if (!inst || !inst->etcp_conn) return; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Generating %d test packets for %s instance", - count, inst->is_server ? "server" : "client"); - - for (int i = 0; i < count; i++) { - struct ll_entry* entry = queue_entry_new(100); // 100 byte packets - if (entry) { - // Fill with test data - uint8_t* data = (uint8_t*)ll_entry_data(entry); - for (int j = 0; j < 100; j++) { - data[j] = (uint8_t)((i+1) * 10 + j); // Pattern data - } - queue_entry_put(inst->etcp_conn->input_queue, entry); - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Queued test packet %d for %s", - i+1, inst->is_server ? "server" : "client"); - } - } -} - -// Main test function -int main(int argc, char* argv[]) { - printf("=== ETCP Traffic Flow Debugging Test (Simplified) ===\n"); - - // Initialize debug system - debug_config_init(); - debug_set_level(DEBUG_LEVEL_DEBUG); - debug_enable_category(DEBUG_CATEGORY_ETCP); - debug_enable_timestamp(1); - debug_enable_function_name(1); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Starting simplified ETCP traffic flow debugging test"); - - // Create server instance - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating server instance..."); - server_instance = create_simple_instance(0x1111111111111111ULL, 1); - if (!server_instance) { - fprintf(stderr, "Failed to create server instance\n"); - return 1; - } - - // Create client instance - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Creating client instance..."); - client_instance = create_simple_instance(0x2222222222222222ULL, 0); - if (!client_instance) { - fprintf(stderr, "Failed to create client instance\n"); - destroy_simple_instance(server_instance); - return 1; - } - - // Phase 1: Test basic packet generation and analysis - printf("\n=== Phase 1: Basic Packet Generation ===\n"); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Generating test packets..."); - generate_test_packets(server_instance, 5); - generate_test_packets(client_instance, 5); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating packet transmission..."); - - // Let server generate some packets - for (int i = 0; i < 3; i++) { - simulate_packet_tx(server_instance); - usleep(10000); // 1ms delay - } - - // Let client generate some packets - for (int i = 0; i < 3; i++) { - simulate_packet_tx(client_instance); - usleep(10000); // 1ms delay - } - - // Phase 2: Test connection establishment simulation - printf("\n=== Phase 2: Connection Establishment Simulation ===\n"); - - // Simulate INIT_REQUEST from client - smaller version for testing - uint8_t init_request[] = { - ETCP_INIT_REQUEST, // Section type - 0x00, 0x14, // Section length (20 bytes) - smaller for test - // Node ID (8 bytes) - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - // MTU (2 bytes) - 0x05, 0xDC, // 1500 - // Keepalive (2 bytes) - 0x00, 0x64, // 100 - // Fake public key (8 bytes) - just for testing - 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22 - }; - // Total: 3 + 20 = 23 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating INIT_REQUEST from client..."); - simulate_packet_rx(server_instance, init_request, sizeof(init_request)); - - // Simulate INIT_RESPONSE from server - uint8_t init_response[] = { - ETCP_INIT_RESPONSE, // Section type - 0x00, 0x0A, // Section length (10 bytes) - // Node ID (8 bytes) - 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, - // MTU (2 bytes) - 0x05, 0xDC // 1500 - }; - // Total: 3 + 10 = 13 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating INIT_RESPONSE from server..."); - simulate_packet_rx(client_instance, init_response, sizeof(init_response)); - - // Phase 3: Test mixed traffic with ACKs and timestamps - printf("\n=== Phase 3: Mixed Traffic Simulation ===\n"); - - // Generate more traffic - generate_test_packets(server_instance, 3); - generate_test_packets(client_instance, 3); - - // Simulate some packets with multiple sections - uint8_t complex_packet[] = { - // TIMESTAMP section: 3 bytes header + 2 bytes data = 5 bytes - ETCP_SECTION_TIMESTAMP, 0x00, 0x02, 0x12, 0x34, - // ACK section with 2 ACKs: 3 bytes header + 1 byte count + 2*4 bytes = 12 bytes total - ETCP_SECTION_ACK, 0x00, 0x09, 0x02, 0x00, 0x01, 0x12, 0x34, 0x00, 0x02, 0x12, 0x35, - // PAYLOAD section: 3 bytes header + 2 bytes ID + 5 bytes data = 10 bytes total - ETCP_SECTION_PAYLOAD, 0x00, 0x08, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F // "Hello" - }; - // Total: 5 + 12 + 10 = 27 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating complex packet with multiple sections..."); - simulate_packet_rx(server_instance, complex_packet, sizeof(complex_packet)); - - // Phase 4: Test retransmission requests - printf("\n=== Phase 4: Retransmission Testing ===\n"); - - uint8_t retrans_request[] = { - ETCP_SECTION_RETRANS + 0x02, // RETRANS section with base ID 2 - 0x00, 0x04, // Section length (4 bytes) - 0x00, 0x03, // Request retransmission of packet ID 3 - 0x00, 0x04 // Request retransmission of packet ID 4 - }; - // Total: 3 + 4 = 7 bytes - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Simulating retransmission request..."); - simulate_packet_rx(client_instance, retrans_request, sizeof(retrans_request)); - - // Final packet generation - printf("\n=== Final Phase: Additional Traffic ===\n"); - - // Generate final packets - for (int i = 0; i < 5; i++) { - simulate_packet_tx(server_instance); - simulate_packet_tx(client_instance); - usleep(5000); // 0.5ms delay - } - - // Print summary - printf("\n=== Traffic Analysis Summary ===\n"); - printf("Server instance: processed packets with detailed section analysis\n"); - printf("Client instance: processed packets with detailed section analysis\n"); - printf("Connection establishment: INIT handshake simulated\n"); - printf("Mixed traffic: ACK, TIMESTAMP, PAYLOAD sections processed\n"); - printf("Retransmission: RETRANS requests processed\n"); - - // Cleanup - destroy_simple_instance(server_instance); - destroy_simple_instance(client_instance); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Test completed"); - printf("\n=== Test completed successfully ===\n"); - - return 0; -} \ No newline at end of file diff --git a/tests/test_etcp_two_instances b/tests/test_etcp_two_instances index 06b4bc12..83e75eb0 100755 Binary files a/tests/test_etcp_two_instances and b/tests/test_etcp_two_instances differ diff --git a/tests/test_etcp_two_instances.c.backup b/tests/test_etcp_two_instances.c.backup deleted file mode 100644 index 85f14c76..00000000 --- a/tests/test_etcp_two_instances.c.backup +++ /dev/null @@ -1,323 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../src/etcp.h" -#include "../src/etcp_connections.h" -#include "../src/config_parser.h" -#include "../src/utun_instance.h" -#include "../src/routing.h" -#include "../src/tun_if.h" -#include "../src/secure_channel.h" -#include "../lib/u_async.h" - -#define TEST_TIMEOUT_MS 3000 // Reduced to 3 seconds for faster testing - -static struct UTUN_INSTANCE* server_instance = NULL; -static struct UTUN_INSTANCE* client_instance = NULL; -static int test_completed = 0; // 0 = running, 1 = success, 2 = timeout/failure -static void* monitor_timeout_id = NULL; -static void* test_timeout_id = NULL; - -// For debug: enable in etcp_connections.c -extern void etcp_connections_read_callback(int fd, void* arg); -extern int etcp_encrypt_send(struct ETCP_DGRAM* dgram); - -static void monitor_connections(void* arg) { - (void)arg; - - if (test_completed) { - // НЕ перезапускать таймер если тест завершен - printf("[MONITOR] Test completed, stopping monitor timer\n"); - monitor_timeout_id = NULL; - return; - } - - // Быстрый выход если уже есть подключение - int server_links = 0; - int client_links = 0; - int client_initialized = 0; - - // Check server - if (server_instance) { - struct ETCP_CONN* conn = server_instance->connections; - while (conn) { - struct ETCP_LINK* link = conn->links; - while (link) { - server_links++; - printf("[SERVER] Link: peer=%llx initialized=%d type=%s\n", - (unsigned long long)conn->peer_node_id, - link->initialized, - link->is_server ? "server" : "client"); - link = link->next; - } - conn = conn->next; - } - } - - // Check client - if (client_instance) { - struct ETCP_CONN* conn = client_instance->connections; - while (conn) { - struct ETCP_LINK* link = conn->links; - while (link) { - client_links++; - if (link->is_server == 0) { // client link - printf("[CLIENT] Link: peer=%llx initialized=%d timer=%s retry=%d\n", - (unsigned long long)conn->peer_node_id, - link->initialized, - link->init_timer ? "active" : "null", - link->init_retry_count); - if (link->initialized) { - client_initialized = 1; - } - } - link = link->next; - } - conn = conn->next; - } - } - - // Check if connection established - if (client_initialized) { - printf("\n=== SUCCESS: Client connection established! ===\n"); - test_completed = 1; // Success - - // Add note about TUN status - if (server_instance->tun.fd < 0 || client_instance->tun.fd < 0) { - printf("ℹ️ Note: ETCP connection established successfully with TUN disabled\n"); - printf("✅ Core ETCP protocol functionality verified\n"); - } - - // Отменить таймаут теста для быстрого завершения - if (test_timeout_id) { - printf("[TEST] Cancelling test timeout for immediate exit\n"); - uasync_cancel_timeout(server_instance->ua, test_timeout_id); - test_timeout_id = NULL; - } - return; - } - - printf("[MONITOR] Server links: %d, Client links: %d, Status: %s\n", - server_links, client_links, - client_initialized ? "CONNECTED" : "connecting..."); - - // Schedule next check - if (!test_completed) { - monitor_timeout_id = uasync_set_timeout(server_instance->ua, 200, NULL, monitor_connections); // Check every 200ms for faster detection - } -} - -static void test_timeout(void* arg) { - (void)arg; - if (!test_completed) { - printf("\n=== TIMEOUT: Connection not established in %d seconds ===\n", TEST_TIMEOUT_MS/1000); - test_completed = 2; // Timeout/failure - // Cancel the monitoring timeout since we're done - if (monitor_timeout_id) { - uasync_cancel_timeout(server_instance->ua, monitor_timeout_id); - monitor_timeout_id = NULL; - } - } -} - -int main() { - printf("=== ETCP Two-Instance Connection Test ===\n\n"); - - // Create server instance - printf("Creating server instance...\n"); - struct UASYNC* server_ua = uasync_create(); - server_instance = utun_instance_create(server_ua, "test_server.conf", NULL); - if (!server_instance) { - printf("Failed to create server instance\n"); - return 1; - } - - // Check if TUN is disabled and adjust test expectations - int server_tun_disabled = (server_instance->tun.fd < 0); - if (server_tun_disabled) { - printf("ℹ️ Server TUN disabled - will focus on ETCP protocol testing\n"); - } - - if (utun_instance_init(server_instance) < 0) { - printf("Failed to initialize server instance\n"); - utun_instance_destroy(server_instance); - return 1; - } - - // Try to register sockets, but don't fail if TUN is disabled - if (utun_instance_register_sockets(server_instance) < 0) { - printf("⚠️ Server TUN socket registration failed (TUN disabled), continuing with ETCP-only test\n"); - } - printf("Server instance ready (node_id=%llx)\n\n", (unsigned long long)server_instance->node_id); - - // Create client instance - printf("Creating client instance...\n"); - struct UASYNC* client_ua = uasync_create(); - client_instance = utun_instance_create(client_ua, "test_client.conf", NULL); - if (!client_instance) { - printf("Failed to create client instance\n"); - utun_instance_destroy(server_instance); - return 1; - } - - // Check if TUN is disabled and run simplified test - int tun_disabled = (server_instance->tun.fd < 0) || (client_instance->tun.fd < 0); - - if (tun_disabled) { - printf("ℹ️ TUN disabled - running simplified ETCP functionality test\n"); - printf("✅ Server instance created (node_id=%llx)\n", (unsigned long long)server_instance->node_id); - printf("✅ Client instance created (node_id=%llx)\n", (unsigned long long)client_instance->node_id); - printf("✅ Both instances configured successfully\n"); - printf("✅ Crypto keys initialized\n"); - printf("✅ ETCP configuration loaded\n"); - printf("✅ Routing tables created\n"); - - // Clean up and exit successfully - printf("\n=== TEST PASSED ===\n"); - printf("✅ ETCP core infrastructure verified with TUN disabled\n"); - printf("✅ Instance creation and configuration successful\n"); - printf("✅ All core components initialized correctly\n"); - - // Skip to cleanup - goto cleanup; - } - - // Check if TUN is disabled - int client_tun_disabled = (client_instance->tun.fd < 0); - if (client_tun_disabled) { - printf("ℹ️ Client TUN disabled - will focus on ETCP protocol testing\n"); - } - - if (utun_instance_init(client_instance) < 0) { - printf("Failed to initialize client instance\n"); - utun_instance_destroy(server_instance); - utun_instance_destroy(client_instance); - return 1; - } - - // Try to register sockets, but don't fail if TUN is disabled - if (utun_instance_register_sockets(client_instance) < 0) { - printf("⚠️ Client TUN socket registration failed (TUN disabled), continuing with ETCP-only test\n"); - } - printf("Client instance ready (node_id=%llx)\n\n", (unsigned long long)client_instance->node_id); - - printf("\n=== TEST PASSED ===\n"); - printf("✅ ETCP core infrastructure verified with TUN disabled\n"); - printf("✅ Instance creation and configuration successful\n"); - printf("✅ Crypto system operational\n"); - printf("✅ All core components initialized correctly\n"); - - // Skip the full monitoring cycle for TUN-disabled mode - goto cleanup; - } - - // For TUN-enabled mode, proceed with full initialization and connection testing - if (utun_instance_init(server_instance) < 0) { - printf("Failed to initialize server instance\n"); - utun_instance_destroy(server_instance); - return 1; - } - - // Try to register sockets, but don't fail if TUN is disabled - if (utun_instance_register_sockets(server_instance) < 0) { - printf("⚠️ Server TUN socket registration failed, continuing\n"); - } - - // Main event loop - printf("Running event loop...\n\n"); - - // Give server time to fully initialize before client starts sending - printf("Waiting 0.5 seconds for server initialization...\n"); - for (int i = 0; i < 50; i++) { // Reduced from 200 to 50 iterations (0.5 seconds) - if (server_ua) uasync_poll(server_ua, 10); - if (client_ua) uasync_poll(client_ua, 10); - usleep(10000); // 10ms - } - printf("Starting connection attempts...\n"); - - int elapsed = 0; - int poll_interval = 5; // Reduced from 10ms to 5ms for faster response - while (!test_completed && elapsed < TEST_TIMEOUT_MS + 500) { - if (server_ua) uasync_poll(server_ua, poll_interval); - if (client_ua) uasync_poll(client_ua, poll_interval); - - usleep(poll_interval * 1000); // microseconds - elapsed += poll_interval; - - // Быстрый выход если подключение установлено - if (test_completed == 1) { - printf("[TEST] Connection established, exiting early after %d ms\n", elapsed); - break; - } - } - -cleanup: - // Cleanup - printf("\nCleaning up...\n"); - printf("[CLEANUP] server_ua=%p, client_ua=%p\n", server_ua, client_ua); - printf("[CLEANUP] server_instance=%p, client_instance=%p\n", server_instance, client_instance); - printf("[CLEANUP] monitor_timeout_id=%p, test_timeout_id=%p\n", monitor_timeout_id, test_timeout_id); - - // СНАЧАЛА отменить таймеры пока uasync ещё жив - if (monitor_timeout_id) { - printf("[CLEANUP] Canceling monitor timeout on valid uasync\n"); - uasync_cancel_timeout(server_ua, monitor_timeout_id); - monitor_timeout_id = NULL; - } - if (test_timeout_id) { - printf("[CLEANUP] Canceling test timeout on valid uasync\n"); - uasync_cancel_timeout(server_ua, test_timeout_id); - test_timeout_id = NULL; - } - printf("[CLEANUP] Timeouts canceled\n"); - - // Диагностика ресурсов перед cleanup - if (server_ua) { - printf("[CLEANUP] Server resources before destroy:\n"); - uasync_print_resources(server_ua, "SERVER"); - } - if (client_ua) { - printf("[CLEANUP] Client resources before destroy:\n"); - uasync_print_resources(client_ua, "CLIENT"); - } - - // ПОТОМ уничтожить instances - if (server_instance) { - server_instance->running = 0; - printf("[CLEANUP] Destroying server instance %p\n", server_instance); - utun_instance_destroy(server_instance); - } - if (client_instance) { - client_instance->running = 0; - printf("[CLEANUP] Destroying client instance %p\n", client_instance); - utun_instance_destroy(client_instance); - } - - // Cleanup uasync objects - НЕ НУЖНО, так как utun_instance_destroy уже вызывает uasync_destroy - // if (server_ua) uasync_destroy(server_ua); - // if (client_ua) uasync_destroy(client_ua); - - // Check TUN status before destroying instances - int tun_was_disabled = 0; - if (server_instance || client_instance) { - tun_was_disabled = ((server_instance && server_instance->tun.fd < 0) || - (client_instance && client_instance->tun.fd < 0)); - } - - if (test_completed == 1) { - printf("\n=== TEST PASSED ===\n"); - if (tun_was_disabled) { - printf("✅ ETCP protocol test successful with TUN disabled\n"); - } - return 0; - } else if (test_completed == 2) { - printf("\n=== TEST FAILED: Connection timeout ===\n"); - return 1; - } else { - printf("\n=== TEST FAILED: Unknown error ===\n"); - return 1; - } -} \ No newline at end of file diff --git a/tests/test_fix.patch b/tests/test_fix.patch deleted file mode 100644 index f4adee63..00000000 --- a/tests/test_fix.patch +++ /dev/null @@ -1,26 +0,0 @@ ---- test_etcp_simple_traffic.c.backup -+++ test_etcp_simple_traffic.c -@@ -241,14 +241,20 @@ - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Parsing crypto keys: priv_len=%zu, pub_len=%zu", - strlen(priv_key_hex), strlen(pub_key_hex)); - -+ // Ensure strings are properly null-terminated and have correct length -+ size_t priv_len = strlen(priv_key_hex); -+ size_t pub_len = strlen(pub_key_hex); -+ if (priv_len != 64 || pub_len != 128) { -+ DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Invalid key lengths: priv=%zu, pub=%zu", priv_len, pub_len); -+ } -+ - // Parse keys - for (int i = 0; i < 32; i++) { -- if (sscanf(&priv_key_hex[i*2], "%2hhx", &inst->instance->my_keys.private_key[i]) != 1) { -+ if (sscanf(&priv_key_hex[i*2], "%2hhx", (unsigned char*)&inst->instance->my_keys.private_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse private key byte %d", i); - } - } - for (int i = 0; i < 64; i++) { -- if (sscanf(&pub_key_hex[i*2], "%2hhx", &inst->instance->my_keys.public_key[i]) != 1) { -+ if (sscanf(&pub_key_hex[i*2], "%2hhx", (unsigned char*)&inst->instance->my_keys.public_key[i]) != 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to parse public key byte %d", i); - } - } diff --git a/tests/test_intensive_memory_pool b/tests/test_intensive_memory_pool index 79e587c1..f4d27e93 100755 Binary files a/tests/test_intensive_memory_pool and b/tests/test_intensive_memory_pool differ diff --git a/tests/test_intensive_memory_pool_backup.c b/tests/test_intensive_memory_pool_backup.c deleted file mode 100644 index 5d258899..00000000 --- a/tests/test_intensive_memory_pool_backup.c +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Intensive memory pool test to demonstrate optimization benefits - */ - -#include -#include -#include -#include -#include -#include - -#include "../lib/ll_queue.h" -#include "../lib/memory_pool.h" -#include "../lib/u_async.h" -#include "../lib/debug_config.h" - -static int waiter_callback_count = 0; - -static void intensive_waiter_callback(struct ll_queue* q, void* arg) { - (void)q; (void)arg; - waiter_callback_count++; -} - -// Тест без пулов памяти -static double test_without_pools(int iterations) { - clock_t start = clock(); - - uasync_t* ua = uasync_create(); - struct ll_queue* queue = queue_new(ua, NULL); // Без пулов - - for (int cycle = 0; cycle < iterations; cycle++) { - // Создать много waiters - struct queue_waiter* waiters[32]; - for (int i = 0; i < 32; i++) { - waiters[i] = queue_wait_threshold(queue, i, 0, intensive_waiter_callback, NULL); - } - - // Добавить записи - for (int i = 0; i < 10; i++) { - struct ll_entry* entry = queue_entry_new(64); - queue_entry_put(queue, entry); - } - - // Удалить записи (триггер waiters) - for (int i = 0; i < 10; i++) { - struct ll_entry* retrieved = queue_entry_get(queue); - if (retrieved) { - queue_entry_free(retrieved); - } - } - - // Отменить waiters - for (int i = 0; i < 32; i++) { - if (waiters[i]) { - queue_cancel_wait(queue, waiters[i]); - } - } - } - - queue_free(queue); - uasync_destroy(ua, 0); - - clock_t end = clock(); - return ((double)(end - start)) / CLOCKS_PER_SEC; -} - -// Тест с пулами памяти -static double test_with_pools(int iterations) { - clock_t start = clock(); - - uasync_t* ua = uasync_create(); - struct ll_queue* queue = queue_new(ua, NULL); // С пулами (using memory pool) - - for (int cycle = 0; cycle < iterations; cycle++) { - // Создать много waiters - struct queue_waiter* waiters[32]; - for (int i = 0; i < 32; i++) { - waiters[i] = queue_wait_threshold(queue, i, 0, intensive_waiter_callback, NULL); - } - - // Добавить записи - for (int i = 0; i < 10; i++) { - struct ll_entry* entry = queue_entry_new(64); - queue_entry_put(queue, entry); - } - - // Удалить записи (триггер waiters) - for (int i = 0; i < 10; i++) { - struct ll_entry* retrieved = queue_entry_get(queue); - if (retrieved) { - queue_entry_free(retrieved); - } - } - - // Отменить waiters - for (int i = 0; i < 32; i++) { - if (waiters[i]) { - queue_cancel_wait(queue, waiters[i]); - } - } - } - - // Получить статистику пула - size_t allocations, reuse_count; - queue_get_pool_stats(queue, &allocations, &reuse_count); - - queue_free(queue); - uasync_destroy(ua, 0); - - clock_t end = clock(); - - printf("Pool statistics: allocations=%zu, reuse_count=%zu\n", allocations, reuse_count); - if (allocations > 0) { - printf("Pool efficiency: %.1f%%\n", (100.0 * reuse_count) / allocations); - } - - return ((double)(end - start)) / CLOCKS_PER_SEC; -} - -int main() { - printf("=== Intensive Memory Pool Performance Test ===\n"); - - debug_config_init(); - debug_set_level(DEBUG_LEVEL_INFO); - - const int iterations = 100; // Увеличенное количество итераций - - printf("Running %d iterations...\n\n", iterations); - - // Тест без пулов - printf("Test WITHOUT memory pools:\n"); - double time_without = test_without_pools(iterations); - printf("Time: %.3f seconds\n\n", time_without); - - // Тест с пулами - printf("Test WITH memory pools:\n"); - double time_with = test_with_pools(iterations); - printf("Time: %.3f seconds\n\n", time_with); - - // Сравнение - double speedup = time_without / time_with; - double improvement = ((time_without - time_with) / time_without) * 100; - - printf("Performance comparison:\n"); - printf(" Speedup: %.2fx\n", speedup); - printf(" Improvement: %.1f%%\n", improvement); - printf(" Time saved: %.3f seconds\n", time_without - time_with); - - if (speedup > 1.0) { - printf("✅ Memory pools provide significant performance improvement!\n"); - } else { - printf("⚠️ Memory pools show minimal improvement for this workload\n"); - } - - printf("\n=== Test completed ===\n"); - return 0; -} \ No newline at end of file diff --git a/tests/test_ll_queue b/tests/test_ll_queue index e603d722..243883a4 100755 Binary files a/tests/test_ll_queue and b/tests/test_ll_queue differ diff --git a/tests/test_ll_queue.c b/tests/test_ll_queue.c index 4a36da1e..497f5375 100644 --- a/tests/test_ll_queue.c +++ b/tests/test_ll_queue.c @@ -55,15 +55,13 @@ static uint64_t checksum(const test_data_t *d) { } /* Callback consumer: забирает и освобождает элемент */ -static void queue_cb(struct ll_queue *q, void *data, void *arg) { +static void queue_cb(struct ll_queue *q, void *arg) { int *cnt = arg; (*cnt)++; stats.cb_queue++; - test_data_t *td = data; - ASSERT(checksum(td) == td->checksum, "corruption in cb"); + test_data_t *taken = queue_data_get(q); + ASSERT(checksum(taken) == taken->checksum, "corruption in cb"); - void *taken = queue_data_get(q); - ASSERT(taken == data, "get != passed data"); queue_data_free(taken); queue_resume_callback(q); // next item when ready diff --git a/tests/test_memory_pool_and_config b/tests/test_memory_pool_and_config index 7fe764a2..558cf74a 100755 Binary files a/tests/test_memory_pool_and_config and b/tests/test_memory_pool_and_config differ diff --git a/tests/test_packet_dump b/tests/test_packet_dump index 294aeec8..d445c693 100755 Binary files a/tests/test_packet_dump and b/tests/test_packet_dump differ diff --git a/tests/test_u_async_comprehensive b/tests/test_u_async_comprehensive index e48959fd..f6b8731f 100755 Binary files a/tests/test_u_async_comprehensive and b/tests/test_u_async_comprehensive differ diff --git a/tests/test_u_async_performance b/tests/test_u_async_performance index 407ee050..82719acd 100755 Binary files a/tests/test_u_async_performance and b/tests/test_u_async_performance differ