~sircmpwn/hare unlisted

078fc33bf766986caa93eb0fbd06b2843267ef3d — Drew DeVault 2 months ago cc66165
unix::poll: new module

This also updates net::dns to use unix::poll instead of rt::ppoll
directly.

Signed-off-by: Drew DeVault <sir@cmpwn.com>
5 files changed, 92 insertions(+), 17 deletions(-)

M encoding/base64/base64.ha
M net/dns/query.ha
M scripts/gen-stdlib
M stdlib.mk
A unix/poll/+linux.ha
M encoding/base64/base64.ha => encoding/base64/base64.ha +2 -2
@@ 33,8 33,8 @@ export def PADDING: u8 = '=': u32: u8;

// Indicates that invalid input was found while decoding, either in the form of
// characters outside of the base 64 alphabet, insufficient padding, or trailing
// characters. Contains the index of the first invalid character which may be
// outside of the bounds of the provided input if more input was expected.
// characters. Contains the index of the first invalid character, which may be
// outside of the bounds of a truncated input.
export type invalid = !size;

// Encodes a byte slice using a base 64 encoding alphabet, with padding, and

M net/dns/query.ha => net/dns/query.ha +10 -15
@@ 1,8 1,8 @@
use errors;
use net::ip;
use net::udp;
use rt;
use time;
use unix::poll;
use unix::resolvconf;

// TODO: Let user customize this?


@@ 13,9 13,7 @@ def timeout: time::duration = 3 * time::SECOND;
//
// If no DNS servers are provided, the system default servers (if any) are used.
export fn query(query: *message, servers: ip::addr...) (*message | error) = {
	// TODO:
	// - Swap rt::poll for unix::poll
	// - Use TCP for messages >512 bytes
	// TODO: Use TCP for messages >512 bytes
	if (len(servers) == 0) {
		servers = resolvconf::load();
	};


@@ 31,15 29,15 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = {
	defer udp::close(socket4);
	const socket6 = udp::listen(ip::ANY_V6, 0)?;
	defer udp::close(socket6);
	const pollfd: [_]rt::pollfd = [
		rt::pollfd {
	const pollfd: [_]poll::pollfd = [
		poll::pollfd {
			fd = udp::sockfd(socket4),
			events = rt::POLLIN,
			events = poll::event::POLLIN,
			...
		},
		rt::pollfd {
		poll::pollfd {
			fd = udp::sockfd(socket6),
			events = rt::POLLIN,
			events = poll::event::POLLIN,
			...
		},
	];


@@ 61,19 59,16 @@ export fn query(query: *message, servers: ip::addr...) (*message | error) = {
	let header = header { ... };
	let recvbuf: [512]u8 = [0...];
	for (true) {
		let ts = rt::timespec { ... };
		time::duration_to_timespec(timeout, &ts);
		let nevent = rt::ppoll(&pollfd: *[*]rt::pollfd,
			len(pollfd), &ts, null)!;
		let nevent = poll::poll(pollfd, timeout)!;
		if (nevent == 0) {
			return errors::timeout;
		};

		let src: ip::addr = ip::ANY_V4;
		if (pollfd[0].revents & rt::POLLIN == rt::POLLIN) {
		if (pollfd[0].revents & poll::event::POLLIN != 0) {
			z = udp::recvfrom(socket4, recvbuf, &src, null)?;
		};
		if (pollfd[1].revents & rt::POLLIN == rt::POLLIN) {
		if (pollfd[1].revents & poll::event::POLLIN != 0) {
			z = udp::recvfrom(socket6, recvbuf, &src, null)?;
		};


M scripts/gen-stdlib => scripts/gen-stdlib +7 -0
@@ 798,6 798,12 @@ unix_passwd() {
	gen_ssa unix::passwd bufio io os strconv strings
}

unix_poll() {
	gen_srcs unix::poll \
		'$(PLATFORM)'.ha
	gen_ssa unix::poll rt errors time
}

unix_resolvconf() {
	gen_srcs unix::resolvconf \
        load.ha


@@ 887,6 893,7 @@ unicode
unix
unix::hosts
unix::passwd
unix::poll
unix::resolvconf
unix::tty
uuid"

M stdlib.mk => stdlib.mk +28 -0
@@ 341,6 341,10 @@ hare_stdlib_deps+=$(stdlib_unix_hosts)
stdlib_unix_passwd=$(HARECACHE)/unix/passwd/unix_passwd.o
hare_stdlib_deps+=$(stdlib_unix_passwd)

# gen_lib unix::poll
stdlib_unix_poll=$(HARECACHE)/unix/poll/unix_poll.o
hare_stdlib_deps+=$(stdlib_unix_poll)

# gen_lib unix::resolvconf
stdlib_unix_resolvconf=$(HARECACHE)/unix/resolvconf/unix_resolvconf.o
hare_stdlib_deps+=$(stdlib_unix_resolvconf)


@@ 1147,6 1151,16 @@ $(HARECACHE)/unix/passwd/unix_passwd.ssa: $(stdlib_unix_passwd_srcs) $(stdlib_rt
	@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunix::passwd \
		-t$(HARECACHE)/unix/passwd/unix_passwd.td $(stdlib_unix_passwd_srcs)

# unix::poll
stdlib_unix_poll_srcs= \
	$(STDLIB)/unix/poll/$(PLATFORM).ha

$(HARECACHE)/unix/poll/unix_poll.ssa: $(stdlib_unix_poll_srcs) $(stdlib_rt) $(stdlib_rt) $(stdlib_errors) $(stdlib_time)
	@printf 'HAREC \t$@\n'
	@mkdir -p $(HARECACHE)/unix/poll
	@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunix::poll \
		-t$(HARECACHE)/unix/poll/unix_poll.td $(stdlib_unix_poll_srcs)

# unix::resolvconf
stdlib_unix_resolvconf_srcs= \
	$(STDLIB)/unix/resolvconf/load.ha


@@ 1524,6 1538,10 @@ hare_testlib_deps+=$(testlib_unix_hosts)
testlib_unix_passwd=$(TESTCACHE)/unix/passwd/unix_passwd.o
hare_testlib_deps+=$(testlib_unix_passwd)

# gen_lib unix::poll
testlib_unix_poll=$(TESTCACHE)/unix/poll/unix_poll.o
hare_testlib_deps+=$(testlib_unix_poll)

# gen_lib unix::resolvconf
testlib_unix_resolvconf=$(TESTCACHE)/unix/resolvconf/unix_resolvconf.o
hare_testlib_deps+=$(testlib_unix_resolvconf)


@@ 2354,6 2372,16 @@ $(TESTCACHE)/unix/passwd/unix_passwd.ssa: $(testlib_unix_passwd_srcs) $(testlib_
	@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunix::passwd \
		-t$(TESTCACHE)/unix/passwd/unix_passwd.td $(testlib_unix_passwd_srcs)

# unix::poll
testlib_unix_poll_srcs= \
	$(STDLIB)/unix/poll/$(PLATFORM).ha

$(TESTCACHE)/unix/poll/unix_poll.ssa: $(testlib_unix_poll_srcs) $(testlib_rt) $(testlib_rt) $(testlib_errors) $(testlib_time)
	@printf 'HAREC \t$@\n'
	@mkdir -p $(TESTCACHE)/unix/poll
	@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunix::poll \
		-t$(TESTCACHE)/unix/poll/unix_poll.td $(testlib_unix_poll_srcs)

# unix::resolvconf
testlib_unix_resolvconf_srcs= \
	$(STDLIB)/unix/resolvconf/load.ha

A unix/poll/+linux.ha => unix/poll/+linux.ha +45 -0
@@ 0,0 1,45 @@
use errors;
use time;
use rt;

// Events bitfield for the events and revents field of [[pollfd]].
export type event = enum i16 {
	POLLIN = 1,
	POLLPRI = 2,
	POLLOUT = 4,
	POLLERR = 8,
	POLLHUP = 16,
};

// A single file descriptor to be polled.
export type pollfd = struct {
	// XXX: TODO: Update me to io::file?
	fd: int,
	events: event,
	revents: event,
};

// Pass this [[time::duration]] to [[poll]] to cause it wait indefinitely for
// the next event.
export def INDEF: time::duration = -1;

// Pass this [[time::duration]] to [[poll]] to cause it to return immediately if
// no events are available.
export def NONBLOCK: time::duration = 0;

// Polls for the desired events on a slice of [[pollfds]], blocking until an
// event is available, or the timeout expires. Set the timeout to [[INDEF]] to
// block forever, or [[NONBLOCK]] to return immediately if no events are
// available. Returns the number of [[pollfd]] items which have events, i.e.
// those which have revents set to a nonzero value.
export fn poll(
	fds: []pollfd,
	timeout: time::duration,
) (uint | errors::error) = {
	let ts = rt::timespec { ... };
	time::duration_to_timespec(timeout, &ts);
	return match (rt::ppoll(fds: *[*]pollfd, len(fds), &ts, null)) {
		err: rt::errno => errors::errno(err),
		n: int => n: uint,
	};
};