tcp_socket владеет shared_ptr<socket_handle>. При move объекта в лямбду до старта async_* последний внешний владелец может исчезнуть → fd закрывается → гонки.
@startuml socket_handle
class tcp_socket {
shared_ptr socket_handle_
}
class socket_handle {
fd
socket_backend*
atomic io_cancelled
cancel_handler
}
tcp_socket *-- socket_handle
@endumlbegin_io()— сброс флага отмены.request_cancel()+fire_cancel(err)— доставка ошибки в ожидающий async.- Деструктор handle закрывает fd, если
fd >= 0.
auto h = sock.io_handle(); // shared_ptr копия
sock = {}; // tcp_socket пуст, fd живёт в h
tcp_socket{h}.async_write_all(...); // OKПравило из INSTALL/GETTING_STARTED: не capture std::move(sock) в колбэк до вызова async на этом же объекте.
Тот же socket_handle и cancel_io. Нет отдельного acceptor.
Coroutines хранят ссылки на tcp_socket& / буферы на стеке кадра — пока sync_wait не вернулся, кадр жив.
vector<char> для write_all_async move в awaitable — владение внутри операции.
@startuml echo_lifetime
participant "server_sock" as S
participant "io_handle" as H
participant Callback
S -> H : io_handle()
S -> Callback : async_read_some([h=io], ...)
note right of H
refcount >= 1 до конца write
end note
Callback -> Callback : tcp_socket{h}.async_write_all(...)
@enduml