#include "packet_link_backend.h" #include "u_async.h" #include "ll_queue.h" #include "mem.h" #include "debug_config.h" #include struct packet_link { struct UASYNC* ua; struct packet_link_ops ops; struct packet_link_callbacks callbacks; struct packet_link_state state; void *impl, *arg, *notification; unsigned active; int closing, rx_enabled, sending; }; static void destroy_deferred(void* arg) { struct packet_link* link = arg; link->ops.destroy(link->impl); u_free(link); } static void release(struct packet_link* link) { if (--link->active || !link->closing) return; /* Backend мог вызвать receive из своего I/O callback: его стек тоже должен завершиться. */ if (!uasync_call_soon(link->ua, link, destroy_deferred)) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot defer destruction"); abort(); } } static void notify(void* arg) { struct packet_link* link = arg; link->notification = NULL; link->active++; if (link->callbacks.state_changed) link->callbacks.state_changed(link, link->arg); release(link); } static int schedule(struct packet_link* link) { if (link->notification || link->closing) return 0; link->notification = uasync_call_soon(link->ua, link, notify); if (link->notification) return 0; DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: cannot schedule state notification"); return -1; } static int valid_state(const struct packet_link_state* s) { return s && s->phase >= PACKET_LINK_CONNECTING && s->phase <= PACKET_LINK_TERMINATED && s->max_packet && s->max_packet <= UINT16_MAX && (!s->tx_ready || s->phase == PACKET_LINK_OPEN); } struct packet_link* packet_link_create(struct UASYNC* ua, const struct packet_link_ops* ops, void* impl, const struct packet_link_state* initial, const struct packet_link_callbacks* callbacks, void* arg) { if (!ua || !ops || !ops->send || !ops->stop || !ops->destroy || !ops->set_rx_enabled || !valid_state(initial) || !callbacks || !callbacks->received) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid create arguments"); return NULL; } struct packet_link* link = u_calloc(1, sizeof(*link)); if (!link) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: allocation failed"); return NULL; } link->ua = ua; link->ops = *ops; link->impl = impl; link->state = *initial; link->callbacks = *callbacks; link->arg = arg; if (schedule(link)) { u_free(link); return NULL; } DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p created phase=%d max=%zu ready=%d", (void*)link, initial->phase, initial->max_packet, initial->tx_ready); return link; } struct packet_link_state packet_link_get_state(const struct packet_link* link) { if (!link || link->closing) return (struct packet_link_state){PACKET_LINK_TERMINATED, 0, 0, PACKET_LINK_CLOSED}; return link->state; } void packet_link_backend_set_state(struct packet_link* link, const struct packet_link_state* state) { if (!link || link->closing) return; if (!valid_state(state) || (link->state.phase == PACKET_LINK_TERMINATED && state->phase != PACKET_LINK_TERMINATED)) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid state transition"); return; } struct packet_link_state old = link->state; if (old.phase == state->phase && old.max_packet == state->max_packet && old.tx_ready == state->tx_ready && old.error == state->error) return; link->state = *state; DEBUG_DEBUG(DEBUG_CATEGORY_SYS, "packet link=%p phase=%d ready=%d max=%zu error=%d", (void*)link, state->phase, state->tx_ready, state->max_packet, state->error); /* Потерять обязательное событие нельзя; синхронный callback нарушил бы контракт send. */ if (schedule(link)) abort(); } int packet_link_send(struct packet_link* link, struct ll_entry* packet) { if (!link || link->closing || link->state.phase == PACKET_LINK_TERMINATED) return PACKET_LINK_CLOSED; if (!packet || packet->owner || packet->next || packet->prev || !packet->dgram || !packet->len) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid TX packet"); return PACKET_LINK_INVALID; } if (packet->len > link->state.max_packet) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: oversized TX size=%u max=%zu", packet->len, link->state.max_packet); return PACKET_LINK_TOO_LARGE; } if (!link->state.tx_ready || link->sending) return PACKET_LINK_WOULD_BLOCK; link->active++; link->sending = 1; int result = link->ops.send(link->impl, packet); link->sending = 0; release(link); return result; } void packet_link_set_rx_enabled(struct packet_link* link, int enabled) { if (!link || link->closing || link->rx_enabled == !!enabled) return; link->active++; link->rx_enabled = !!enabled; link->ops.set_rx_enabled(link->impl, !!enabled); release(link); } int packet_link_backend_receive(struct packet_link* link, struct ll_entry* packet) { if (!link || link->closing || link->state.phase == PACKET_LINK_TERMINATED) return PACKET_LINK_CLOSED; if (!packet || packet->owner || !packet->dgram || !packet->len || packet->len > link->state.max_packet) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet link: invalid RX packet"); return PACKET_LINK_INVALID; } if (!link->rx_enabled || link->sending || link->state.phase != PACKET_LINK_OPEN) return PACKET_LINK_WOULD_BLOCK; link->active++; link->callbacks.received(link, packet, link->arg); release(link); return PACKET_LINK_OK; } void packet_link_close(struct packet_link* link) { if (!link || link->closing) return; link->active++; link->closing = 1; if (link->notification) { uasync_call_soon_cancel(link->ua, link->notification); link->notification = NULL; } link->ops.stop(link->impl); release(link); }