// tcp_io.h — управление TCP-соединением через uasync + ll_queue // // Один tcp_conn = одно TCP-соединение. Две очереди: read_queue (сокет → данные) и // write_queue (данные → сокет). Обе работают через автозабор ll_queue (deferred). // // Пул-аллокация: // entry_pool — только struct ll_entry (без inline data) // data_pool — буферы данных чтения/записи (max(entry_data_size, write_chunk_size)) // Данные и структуры аллоцируются раздельно — не копируются при recv/send. // // Чтение: // read_cb → memory_pool_alloc(read_pool) → recv() прямо в буфер → entry в read_queue // read_queue автозабор(deferred) → внешний коллбэк (например, отправка в ETCP) // high_water → пауза чтения (убираем EPOLLIN), low_water/waiter → возобновление // FIN: если read_queue не пуст → empty_callback откладывает on_fin; иначе сразу // // Запись: // Внешний код: tcp_conn_push_write(data, len) → аллокация через write_pool → entry в write_queue // write_queue автозабор(deferred): fetch_cb → send() в сокет // EAGAIN → write_buf (из data_pool) + EPOLLOUT ON → write_cb досылает → resume автозабора // Всё отправлено → EPOLLOUT OFF + on_flushed (если установлен) // // Connect: // tcp_conn_create регистрирует сокет с read_cb + write_cb в uasync // После create вызывается connect() (неблокирующий, EINPROGRESS) // write_cb детектит завершение connect через getsockopt(SO_ERROR) // getpeername() сразу после create проверяет pre-connected сокеты (socketpair) // До connect данные копятся в write_queue (автозабор не пытается send на unconnected сокет) #ifndef TCP_IO_H #define TCP_IO_H #include "u_async.h" #include "socket_compat.h" #include "ll_queue.h" #include "memory_pool.h" struct tcp_conn { socket_t sock; struct UASYNC* ua; void* socket_id; struct ll_queue* read_queue; // сокет → данные (блоки до entry_data_size) struct ll_queue* write_queue; // данные → сокет (блоки до write_chunk_size) int read_high_water; int read_low_water; uint8_t read_paused; uint8_t write_monitor; // 1 = EPOLLOUT активен uint8_t connected; uint8_t error; uint8_t fin; // FIN от сокета // Частичная отправка (из write_pool, не в очереди — досылается первой) uint8_t* write_buf; size_t write_len; size_t write_offset; size_t write_chunk_size; // Пулы памяти struct memory_pool* entry_pool; // sizeof(struct ll_entry) struct memory_pool* data_pool; // max(entry_data_size, write_chunk_size) size_t entry_data_size; struct queue_waiter_handle read_waiter; // Коллбэки void (*on_fin)(struct tcp_conn* tc, void* arg); void (*on_error)(struct tcp_conn* tc, int err, void* arg); void (*on_flushed)(struct tcp_conn* tc, void* arg); // все данные записи отправлены void* arg; }; struct tcp_conn* tcp_conn_create( struct UASYNC* ua, socket_t sock, size_t entry_data_size, size_t write_chunk_size, int read_high_water, int read_low_water, void (*on_fin)(struct tcp_conn* tc, void* arg), void (*on_error)(struct tcp_conn* tc, int err, void* arg), void* arg); void tcp_conn_destroy(struct tcp_conn* tc); // Внешний код пишет данные в tc->write_queue напрямую (queue_data_put). // Автозабор write_queue (deferred) сам отправляет когда сокет готов. // Перед push проверять порог: queue_set_threshold в tcp_conn_create (32 entries). // При заполнении — queue_waiter_wait на освобождение. // Одноразовый коллбэк: вызывается когда write_queue + write_buf полностью опустели. // После вызова сбрасывается. Установить повторно можно в любой момент. void tcp_conn_set_flushed(struct tcp_conn* tc, void (*on_flushed)(struct tcp_conn* tc, void* arg)); #endif