int call_putflush(struct call *cc, const char *s, unsigned int len) { if (call_put(cc, s, len) == -1) return -1; if (call_flush(cc) == -1) return -1; return 0; }
rstatus_t call_recv(struct context *ctx, struct call *call) { rstatus_t status; struct conn *conn = call->conn; ssize_t n; size_t rcvd; if (call->rsp.rsize == 0) { size_t chunk_size; ASSERT(call->rsp.rcurr > call->rsp.pcurr); /* * Make space in the read buffer by moving the unparsed chunk * at the tail end to the head. */ chunk_size = (size_t)(call->rsp.rcurr - call->rsp.pcurr); mcp_memmove(conn->buf, call->rsp.pcurr, chunk_size); call->rsp.pcurr = conn->buf; call->rsp.rcurr = conn->buf + chunk_size; call->rsp.rsize = sizeof(conn->buf) - chunk_size; } if (call->rsp.rcvd == 0) { ecb_signal(ctx, EVENT_CALL_RECV_START, call); } n = conn_recv(conn, call->rsp.rcurr, call->rsp.rsize); rcvd = n > 0 ? (size_t)n : 0; call->rsp.rcvd += rcvd; call->rsp.rcurr += rcvd; call->rsp.rsize -= rcvd; if (n <= 0) { if (n == 0 || n == MCP_EAGAIN) { return MCP_OK; } return MCP_ERROR; } do { struct call *next_call; /* next call in recv q */ status = call_parse_rsp(ctx, call); if (status != MCP_OK) { if (status == MCP_EAGAIN) { /* incomplete response; parse again when more data arrives */ return MCP_OK; } return status; } next_call = NULL; /* * Spill over unparsed response onto the next call and update * the current call appropriately */ if (call->rsp.rcurr != call->rsp.pcurr) { next_call = STAILQ_NEXT(call, call_tqe); if (next_call == NULL) { log_debug(LOG_ERR, "stray response type %d on c %"PRIu64"", call->rsp.type, conn->id); conn->err = EINVAL; return MCP_ERROR; } ecb_signal(ctx, EVENT_CALL_RECV_START, next_call); next_call->rsp.rcurr = call->rsp.rcurr; next_call->rsp.rsize = call->rsp.rsize; next_call->rsp.pcurr = call->rsp.pcurr; /* * Calculate the exact bytes received on this call and accumulate * the remaining bytes onto the next call */ ASSERT(call->rsp.rcurr > call->rsp.pcurr); next_call->rsp.rcvd = (size_t)(call->rsp.rcurr - call->rsp.pcurr); call->rsp.rcvd -= next_call->rsp.rcvd; } conn->ncall_recvq--; STAILQ_REMOVE(&conn->call_recvq, call, call, call_tqe); call_reset_timer(ctx, call); ecb_signal(ctx, EVENT_CALL_RECV_STOP, call); ecb_signal(ctx, EVENT_CALL_DESTROYED, call); call_put(call); call = next_call; } while (call != NULL); return MCP_OK; }
rstatus_t call_send(struct context *ctx, struct call *call) { struct conn *conn = call->conn; size_t sent; ssize_t n; uint32_t i; ASSERT(call->req.send != 0); if (!call->req.sending) { /* * We might need multiple write events to send a call completely. * Signal the first time we start sending a given call. */ ecb_signal(ctx, EVENT_CALL_SEND_START, call); call->req.sending = 1; } n = conn_sendv(conn, call->req.iov, REQ_IOV_LEN, call->req.send); sent = n > 0 ? (size_t)n : 0; log_debug(LOG_VERB, "send call %"PRIu64" on c %"PRIu64" sd %d %zu of %zu " "bytes", call->id, conn->id, conn->sd, sent, call->req.send); call->req.send -= sent; call->req.sent += sent; for (i = 0; i < REQ_IOV_LEN; i++) { struct iovec *iov = &call->req.iov[i]; if (sent == 0) { break; } if (sent < iov->iov_len) { /* iov element was sent partially; send remaining bytes later */ iov->iov_base = (char *)iov->iov_base + sent; iov->iov_len -= sent; sent = 0; break; } /* iov element was sent completely; mark it empty */ sent -= iov->iov_len; iov->iov_base = NULL; iov->iov_len = 0; } if (call->req.send == 0) { ecb_signal(ctx, EVENT_CALL_SEND_STOP, call); /* * Call has been sent completely; move the call from sendq * to recvq unless it has been marked as noreply. */ conn->ncall_sendq--; STAILQ_REMOVE(&conn->call_sendq, call, call, call_tqe); if (call->req.noreply) { ecb_signal(ctx, EVENT_CALL_DESTROYED, call); call_put(call); } else { STAILQ_INSERT_TAIL(&conn->call_recvq, call, call_tqe); conn->ncall_recvq++; call_start_timer(ctx, call); } } if (n > 0) { return MCP_OK; } return (n == MCP_EAGAIN) ? MCP_OK : MCP_ERROR; }