rust/src/lib/aio.rs
2011-09-12 16:13:28 -07:00

181 lines
5.2 KiB
Rust

import task;
import vec;
import comm;
import comm::{chan, port, send, recv};
import net;
native "rust" mod rustrt {
type socket;
type server;
fn aio_init();
fn aio_run();
fn aio_stop();
fn aio_connect(host: *u8, port: int, connected: chan<socket>);
fn aio_serve(host: *u8, port: int, acceptChan: chan<socket>) -> server;
fn aio_writedata(s: socket, buf: *u8, size: uint, status: chan<bool>);
fn aio_read(s: socket, reader: chan<[u8]>);
fn aio_close_server(s: server, status: chan<bool>);
fn aio_close_socket(s: socket);
fn aio_is_null_client(s: socket) -> bool;
}
// FIXME: these should be unsafe pointers or something, but those aren't
// currently in the sendable kind, so we'll unsafely cast between ints.
type server = rustrt::server;
type client = rustrt::socket;
tag pending_connection { remote(net::ip_addr, int); incoming(server); }
tag socket_event { connected(client); closed; received([u8]); }
tag server_event { pending(chan<chan<socket_event>>); }
tag request {
quit;
connect(pending_connection, chan<socket_event>);
serve(net::ip_addr, int, chan<server_event>, chan<server>);
write(client, [u8], chan<bool>);
close_server(server, chan<bool>);
close_client(client);
}
type ctx = chan<request>;
fn ip_to_sbuf(ip: net::ip_addr) -> *u8 {
// FIXME: This is broken. We're creating a vector, getting a pointer
// to its buffer, then dropping the vector. On top of that, the vector
// created by str::bytes is not null-terminated.
vec::to_ptr(str::bytes(net::format_addr(ip)))
}
fn connect_task(ip: net::ip_addr, portnum: int, evt: chan<socket_event>) {
let connecter = port();
rustrt::aio_connect(ip_to_sbuf(ip), portnum, chan(connecter));
let client = recv(connecter);
new_client(client, evt);
}
fn new_client(client: client, evt: chan<socket_event>) {
// Start the read before notifying about the connect. This avoids a race
// condition where the receiver can close the socket before we start
// reading.
let reader: port<[u8]> = port();
rustrt::aio_read(client, chan(reader));
send(evt, connected(client));
while true {
log "waiting for bytes";
let data: [u8] = recv(reader);
log "got some bytes";
log vec::len::<u8>(data);
if vec::len::<u8>(data) == 0u {
log "got empty buffer, bailing";
break;
}
log "got non-empty buffer, sending";
send(evt, received(data));
log "sent non-empty buffer";
}
log "done reading";
send(evt, closed);
log "close message sent";
}
fn accept_task(client: client, events: chan<server_event>) {
log "accept task was spawned";
let p = port();
send(events, pending(chan(p)));
let evt = recv(p);
new_client(client, evt);
log "done accepting";
}
fn server_task(ip: net::ip_addr, portnum: int, events: chan<server_event>,
server: chan<server>) {
let accepter = port();
send(server, rustrt::aio_serve(ip_to_sbuf(ip), portnum, chan(accepter)));
let client: client;
while true {
log "preparing to accept a client";
client = recv(accepter);
if rustrt::aio_is_null_client(client) {
log "client was actually null, returning";
ret;
} else { task::spawn(bind accept_task(client, events)); }
}
}
fn request_task(c: chan<ctx>) {
// Create a port to accept IO requests on
let p = port();
// Hand of its channel to our spawner
send(c, chan(p));
log "uv run task spawned";
// Spin for requests
let req: request;
while true {
req = recv(p);
alt req {
quit. {
log "got quit message";
log "stopping libuv";
rustrt::aio_stop();
ret;
}
connect(remote(ip, portnum), client) {
task::spawn(bind connect_task(ip, portnum, client));
}
serve(ip, portnum, events, server) {
task::spawn(bind server_task(ip, portnum, events, server));
}
write(socket, v, status) {
rustrt::aio_writedata(socket, vec::unsafe::to_ptr::<u8>(v),
vec::len::<u8>(v), status);
}
close_server(server, status) {
log "closing server";
rustrt::aio_close_server(server, status);
}
close_client(client) {
log "closing client";
rustrt::aio_close_socket(client);
}
}
}
}
fn iotask(c: chan<ctx>) {
log "io task spawned";
// Initialize before accepting requests
rustrt::aio_init();
log "io task init";
// Spawn our request task
let reqtask = task::spawn_joinable(bind request_task(c));
log "uv run task init";
// Enter IO loop. This never returns until aio_stop is called.
rustrt::aio_run();
log "waiting for request task to finish";
task::join(reqtask);
}
fn new() -> ctx {
let p: port<ctx> = port();
task::spawn(bind iotask(chan(p)));
ret recv(p);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End: