Tick server implemented in Go and Hare.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
3.4 KiB

use bufio;
use io;
use log;
use net;
use net::ip;
use net::tcp;
use strings;
use time;
use unix::poll;
// our message to send to clients as a constant
def tick: [5]u8 = ['t', 'i', 'c', 'k', '\n'];
// tryconn attempts to add a client to a pollfd slice.
fn tryconn(fds: []poll::pollfd) void = {
let socket = fds[0];
if (socket.revents & poll::event::POLLIN > 0) {
// try to accept the connection.
let conn = match(net::accept(socket.fd)) {
case let err: net::error =>
log::println("failed to accept connection");
return;
case let sock: net::socket =>
yield sock;
};
// the events we want to listen for
let events = (poll::event::POLLIN | poll::event::POLLPRI);
for (let i = 1z; i < len(fds); i += 1) {
// empty slot is indicated by a
// file descriptor equal to 0
if (fds[i].fd == 0) {
fds[i].fd = conn;
fds[i].events = events;
return;
};
};
// close the connection if there is no room
io::close(conn)!;
};
};
// tryread will attempt to read from all connected clients.
fn tryread(fds: []poll::pollfd) void = {
for (let i = 1z; i < len(fds); i += 1) {
let revents = fds[i].revents;
// just close the file descriptor
// on error or hangup events
if (revents & poll::event::POLLERR
& poll::event::POLLHUP > 0) {
closefd(&fds[i]);
continue;
};
// if there is data to read...
if (revents & poll::event::POLLIN > 0) {
let scanner = bufio::newscanner(fds[i].fd, 1024z);
defer bufio::finish(&scanner);
match(bufio::scan_line(&scanner)) {
case let msg: const str =>
log::printfln("client said: {}", msg);
case =>
// io::EOF, io::error, or utf8::invalid
closefd(&fds[i]);
};
};
};
};
// write sends a 'tick' message to all connected clients.
fn write(fds: []poll::pollfd) void = {
for (let i = 1z; i < len(fds); i += 1) {
if (fds[i].fd > 0) {
io::writeall(fds[i].fd, tick)!;
};
};
};
// closefd closes a provided file descriptor
// and resets all fields to 0.
fn closefd(fd: *poll::pollfd) void = {
match(io::close(fd.fd)) {
case void =>
void;
case io::error =>
log::println("failed to close socket");
};
fd.fd = 0;
fd.events = 0;
fd.revents = 0;
};
// shouldtick returns a boolean representing if a tick
// message should be sent and a time::duration indicating
// when the next tick should be.
fn shouldtick(next: time::instant) (bool, time::duration) = {
let now = time::now(time::clock::MONOTONIC);
if (time::compare(now, next) >= 0) {
return (true, time::SECOND);
};
let diff = time::diff(now, next);
return (false, diff);
};
export fn main() void = {
let socket = tcp::listen(ip::LOCAL_V4, 8080)!;
// initialize all pollfds with zero values
let fds: [15]poll::pollfd = [
poll::pollfd {
fd = 0,
events = 0,
revents = 0,
}
...
];
// overwrite the first pollfd with our socket
fds[0] = poll::pollfd {
fd = socket,
events = (poll::event::POLLIN | poll::event::POLLPRI),
revents = 0
};
let start = time::now(time::clock::MONOTONIC);
let nexttick = time::add(start, time::SECOND);
let wait = time::SECOND;
for (true) {
match(poll::poll(fds, wait)) {
case let n: uint =>
if (n > 0) {
tryconn(fds);
tryread(fds);
};
let (shouldtick, d) = shouldtick(nexttick);
if (shouldtick) {
let now = time::now(time::clock::MONOTONIC);
nexttick = time::add(now, d);
write(fds);
};
wait = d;
case let err: poll::error =>
log::fatal("poll failed");
};
};
};