static void read_stdin(ph_job_t *job, ph_iomask_t why, void *data) { char buf[128]; int x, i; ph_unused_parameter(why); ph_unused_parameter(data); x = read(job->fd, buf, sizeof(buf)); if (x <= 0) { if (x == -1) { ph_log(PH_LOG_ERR, "read(stdin): `Pe%d", errno); } ph_sched_stop(); return; } // Writing to the other job is safe here because we have the // same affinity: we know that it is not executing and mutating // its state for (i = 0; i < x; i++) { if (buf[i] == '\n') { ph_stm_write(remote_sock->stream, "\r\n", 2, NULL); } else { ph_stm_write(remote_sock->stream, buf + i, 1, NULL); } } // Force the sock to wakeup and send the buffer. // FIXME: Need something nicer than this hack ph_sock_enable(remote_sock, false); ph_sock_enable(remote_sock, true); ph_job_set_nbio_timeout_in(job, PH_IOMASK_READ, timeout); }
int main(int argc, char **argv) { ph_stream_t *stm; char namebuf[128]; int fd; char buf[BUFSIZ]; uint64_t amount; int len; ph_unused_parameter(argc); ph_unused_parameter(argv); ph_library_init(); plan_tests(18); strcpy(namebuf, "/tmp/phenomXXXXXX"); fd = ph_mkostemp(namebuf, 0); diag("opened %s -> %d", namebuf, fd); unlink(namebuf); stm = ph_stm_fd_open(fd, 0, PH_STM_BUFSIZE); ph_stm_write(stm, "lemon\n", 6, &amount); is(amount, 6); // Shouldn't see it yet is(0, pread(fd, buf, sizeof(buf), 0)); // Should see it now ph_stm_flush(stm); is(6, pread(fd, buf, sizeof(buf), 0)); ok(!memcmp("lemon\n", buf, 6), "right content"); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); memset(buf, 0, sizeof(buf)); ok(ph_stm_read(stm, buf, 3, &amount), "read ok"); ok(amount == 3, "amount is %" PRIu64, amount); ok(!memcmp("lem", buf, 3), "got prefix"); ok(ph_stm_read(stm, buf, 3, &amount), "read ok"); ok(amount == 3, "amount is %" PRIu64, amount); ok(!memcmp("on\n", buf, 3), "got remainder"); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); len = ph_stm_printf(stm, "testing %d %s!", 1, "two"); ok(14 == len, "printed len %d", len); ok(ph_stm_seek(stm, 0, SEEK_SET, NULL), "seeked"); memset(buf, 0, sizeof(buf)); ok(ph_stm_read(stm, buf, 14, &amount), "read ok"); ok(amount == 14, "len was %" PRIu64, amount); ok(!memcmp("testing 1 two!", buf, 14), "got formatted"); ok(ph_stm_close(stm), "closed"); return exit_status(); }
// Called each time the session wakes up. // The `why` parameter indicates why we were woken up static void echo_processor(ph_sock_t *sock, ph_iomask_t why, void *arg) { struct echo_state *state = arg; ph_buf_t *buf; // If the socket encountered an error, or if the timeout was reached // (there's a default timeout, even if we didn't override it), then // we tear down the session if (why & (PH_IOMASK_ERR|PH_IOMASK_TIME)) { ph_log(PH_LOG_ERR, "disconnecting `P{sockaddr:%p}", (void*)&sock->peername); ph_sock_shutdown(sock, PH_SOCK_SHUT_RDWR); ph_mem_free(mt_state, state); ph_sock_free(sock); return; } // We loop because echo_processor is only triggered by newly arriving // data or events from the kernel. If we have data buffered and only // partially consume it, we won't get woken up until the next data // arrives, if ever. while (1) { // Try to read a line of text. // This returns a slice over the underlying buffer (if the line was // smaller than a buffer) or a freshly made contiguous buffer (if the // line was larger than our buffer segment size). Either way, we // own a reference to the returned buffer and should treat it as // a read-only slice. buf = ph_sock_read_line(sock); if (!buf) { // Not available yet, we'll try again later return; } // We got a line; update our state state->num_lines++; // Send our response. The data is buffered and automatically sent // to the client as it becomes writable, so we don't need to handle // partial writes or EAGAIN here. // If this was a "real" server, we would still check the return value // from the writes and proceed to tear down the session if things failed. // Note that buf includes the trailing CRLF, so our response // will implicitly end with CRLF too. ph_stm_printf(sock->stream, "You said [%d]: ", state->num_lines); ph_stm_write(sock->stream, ph_buf_mem(buf), ph_buf_len(buf), NULL); // We're done with buf, so we must release it ph_buf_delref(buf); } }
bool ph_stm_copy(ph_stream_t *src, ph_stream_t *dest, uint64_t num_bytes, uint64_t *nreadp, uint64_t *nwrotep) { uint64_t nread = 0, nwrote = 0; uint64_t remain = num_bytes; char lbuf[PH_STM_BUFSIZE]; int err = 0; while (remain > 0 && err == 0) { uint64_t r, want = MIN(remain, sizeof(lbuf)); if (!ph_stm_read(src, lbuf, want, &r)) { if (ph_stm_errno(src) == EINTR) { continue; } err = ph_stm_errno(src); break; } if (r == 0) { break; } nread += r; remain -= r; char *buf = lbuf; while (r > 0) { uint64_t w; if (!ph_stm_write(dest, buf, r, &w)) { if (ph_stm_errno(dest) == EINTR) { continue; } err = ph_stm_errno(dest); break; } nwrote += w; buf += w; r -= w; } } if (nreadp) { *nreadp = nread; } if (nwrotep) { *nwrotep = nwrote; } return err == 0; }
static int bio_stm_write(BIO *h, const char *buf, int size) { uint64_t nwrote; ph_stream_t *stm = h->ptr; if (buf == NULL || size == 0 || stm == NULL) { return 0; } BIO_clear_retry_flags(h); if (ph_stm_write(stm, buf, size, &nwrote)) { return (int)nwrote; } if (should_retry(stm)) { BIO_set_retry_write(h); } return -1; }