| 800 | } |
| 801 | |
| 802 | static int connection_read_full(cbm_daemon_ipc_connection_t *connection, void *buffer, |
| 803 | size_t length, uint64_t deadline_ms) { |
| 804 | if (!connection || atomic_load_explicit(&connection->poisoned, memory_order_acquire)) { |
| 805 | return -1; |
| 806 | } |
| 807 | size_t offset = 0; |
| 808 | while (offset < length) { |
| 809 | int ready = poll_until(connection->fd, POLLIN, deadline_ms); |
| 810 | if (ready != 1) { |
| 811 | /* Framing is fail-stop across platforms. Even at offset zero a |
| 812 | * deadline may race transport progress (notably OVERLAPPED I/O on |
| 813 | * Windows), so callers must reconnect instead of reusing a stream |
| 814 | * whose next byte boundary is uncertain. */ |
| 815 | atomic_store_explicit(&connection->poisoned, true, memory_order_release); |
| 816 | return ready; |
| 817 | } |
| 818 | ssize_t received = recv(connection->fd, (uint8_t *)buffer + offset, length - offset, 0); |
| 819 | if (received > 0) { |
| 820 | offset += (size_t)received; |
| 821 | continue; |
| 822 | } |
| 823 | if (received == 0) { |
| 824 | atomic_store_explicit(&connection->poisoned, true, memory_order_release); |
| 825 | return -1; |
| 826 | } |
| 827 | if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { |
| 828 | atomic_store_explicit(&connection->poisoned, true, memory_order_release); |
| 829 | return -1; |
| 830 | } |
| 831 | } |
| 832 | return 1; |
| 833 | } |
| 834 | |
| 835 | static int connection_write_full(cbm_daemon_ipc_connection_t *connection, const void *buffer, |
| 836 | size_t length, uint64_t deadline_ms) { |
no test coverage detected