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