From 667fc952a02a02dadf36524c2df4cb3c8b6cc091 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Sun, 16 Mar 2025 02:55:16 +0800 Subject: [PATCH] Handle nomem Signed-off-by: Runxi Yu --- cmd/httpserv/main.ha | 10 +++++----- ev/+linux/file.ha | 12 ++++++------ ev/+linux/io.ha | 14 +++++++------- ev/+linux/loop.ha | 18 +++++++++--------- ev/+linux/signal.ha | 6 +++--- ev/+linux/socket.ha | 28 ++++++++++++++-------------- ev/+linux/timers.ha | 2 +- ev/server/http.ha | 24 ++++++++++++------------ ev/server/server.ha | 16 ++++++++-------- diff --git a/cmd/httpserv/main.ha b/cmd/httpserv/main.ha index 6633dbc44ec242423c3fb036f4253490322e622d..e414145ab3fd6e068fde376735885fdbc3a91389 100644 --- a/cmd/httpserv/main.ha +++ b/cmd/httpserv/main.ha @@ -29,17 +29,17 @@ log::fatalf("Error: listen: {}", errors::strerror(err)); case let sock: *ev::file => yield sock; }; - defer ev::close(sock); + defer ev::close(sock)!; let state = state { loop = &loop, ... }; - const server = server::http_serve(sock, &http_serve, &state); - defer server::server_finish(server); + const server = server::http_serve(sock, &http_serve, &state)!; + defer server::server_finish(server)!; const sig = ev::signal(&loop, &signal, signal::sig::INT, signal::sig::TERM)!; - defer ev::close(sig); + defer ev::close(sig)!; ev::setuser(sig, &state); log::println("Listening on 127.0.0.1:8080"); @@ -77,7 +77,7 @@ io::copy(&buf, req.body)!; io::seek(&buf, 0, io::whence::SET)!; - http::response_add_header(&rw, "Content-Type", "text/plain"); + http::response_add_header(&rw, "Content-Type", "text/plain")!; http::response_set_body(&rw, &buf); http::response_write(&rw)!; }; diff --git a/ev/+linux/file.ha b/ev/+linux/file.ha index b412408490d25c806f7a54d36cf5a3a99fc33155..faced17606883a0dbe0dae342cbed24e2163d897 100644 --- a/ev/+linux/file.ha +++ b/ev/+linux/file.ha @@ -64,7 +64,7 @@ export fn register( loop: *loop, fd: io::file, user: nullable *opaque = null, -) (*file | errors::error) = { +) (*file | errors::error | nomem) = { const file = alloc(file { flags = fflags::NONE, fd = fd, @@ -72,7 +72,7 @@ ev = loop, op = op::NONE, user = user, ... - }); + })?; let ev = rt::epoll_event { events = 0, @@ -99,7 +99,7 @@ }; // Unregisters a file object with an event loop and frees resources associated // with it. Does not close the underlying file descriptor. -export fn unregister(file: *file) void = { +export fn unregister(file: *file) (void | nomem) = { const loop = file.ev; if (file.flags & fflags::BLOCKING == 0) { // The only way that this could fail is in the event of a @@ -109,7 +109,7 @@ // error. rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!; }; if (file.op == op::SIGNAL) { - signal_restore(file); + signal_restore(file)?; }; for (let ev &.. loop.events) { @@ -126,9 +126,9 @@ }; // Unregisters a file object with an event loop, frees resources associated with // it, and closes the underlying file descriptor. -export fn close(file: *file) void = { +export fn close(file: *file) (void | nomem) = { const fd = file.fd; - unregister(file); + unregister(file)?; io::close(fd)!; }; diff --git a/ev/+linux/io.ha b/ev/+linux/io.ha index 1758d60e2e76ad3c72293a364d13197b0ffd4f9f..09dc1ab1916ac7700c6b9e2e2668b71c1252c780 100644 --- a/ev/+linux/io.ha +++ b/ev/+linux/io.ha @@ -2,7 +2,7 @@ use io; use rt; // A callback for a [[read]] or [[readv]] operation. -export type readcb = fn(file: *file, result: (size | io::EOF | io::error)) void; +export type readcb = fn(file: *file, result: (size | io::EOF | io::error)) (void | nomem); // Schedules a read operation on a file object. The provided buffer must be // valid for the duration of the read operation. @@ -27,7 +27,7 @@ ) req = { assert(file.op & op::READV == 0); if (file.flags & fflags::BLOCKING != 0) { const r = io::readv(file.fd, vec...); - cb(file, r); + cb(file, r)!; return req { ... }; }; @@ -46,11 +46,11 @@ file.op &= ~op::READV; file_epoll_ctl(file); if (ev.events & rt::EPOLLHUP != 0) { - cb(file, io::EOF); + cb(file, io::EOF)!; } else { const vec = file.rvec: []io::vector; const r = io::readv(file.fd, vec...); - cb(file, r); + cb(file, r)!; }; }; @@ -62,7 +62,7 @@ file_epoll_ctl(file); }; // A callback for a [[write]] or [[writev]] operation. -export type writecb = fn(file: *file, result: (size | io::error)) void; +export type writecb = fn(file: *file, result: (size | io::error)) (void | nomem); // Schedules a write operation on a file object. The provided buffer must be // valid for the duration of the write operation. @@ -87,7 +87,7 @@ ) req = { assert(file.op & op::WRITEV == 0); if (file.flags & fflags::BLOCKING != 0) { const r = io::writev(file.fd, vec...); - cb(file, r); + cb(file, r)!; return req { ... }; }; @@ -106,7 +106,7 @@ const r = io::writev(file.fd, vec...); const cb = file.cb2: *writecb; file.op &= ~op::WRITEV; file_epoll_ctl(file); - cb(file, r); + cb(file, r)!; }; fn writev_cancel(req: *req) void = { diff --git a/ev/+linux/loop.ha b/ev/+linux/loop.ha index 145273adf7db6f9b7949c5e73cef130ec32b5e75..e75e538f45780fe284d3fc4a3d28fd23e9aab13f 100644 --- a/ev/+linux/loop.ha +++ b/ev/+linux/loop.ha @@ -24,7 +24,7 @@ }; // Creates a new event loop. The user must pass the return value to [[finish]] // to free associated resources when done using the loop. -export fn newloop() (loop | errors::error) = { +export fn newloop() (loop | errors::error | nomem) = { const fd = match (rt::epoll_create1(rt::EPOLL_CLOEXEC)) { case let fd: int => yield fd: io::file; @@ -40,7 +40,7 @@ events = 0, data = rt::epoll_data { fd = 0, } - }...], 256), + }...], 256)?, dispatch = [], stop = false, }; @@ -72,13 +72,13 @@ export fn do( loop: *loop, cb: *dispatchcb, user: nullable *opaque = null, -) req = { +) (req | nomem) = { const dispatch = alloc(ondispatch { cb = cb, user = user, loop = loop, - }); - append(loop.dispatch, dispatch); + })?; + append(loop.dispatch, dispatch)?; return mkreq(&do_cancel, dispatch); }; @@ -107,7 +107,7 @@ // Returns false if the loop has been stopped via [[stop]], or true otherwise. export fn dispatch( loop: *loop, timeout: time::duration, -) (bool | errors::error) = { +) (bool | errors::error | nomem) = { const millis: int = if (timeout == -1) { yield -1; } else if (timeout < 0) { @@ -177,11 +177,11 @@ readable_ready(file, ev); case op::WRITABLE => writable_ready(file, ev); case op::ACCEPT => - accept_ready(file, ev); + accept_ready(file, ev)?; case op::CONNECT_TCP => - connect_tcp_ready(file, ev); + connect_tcp_ready(file, ev)?; case op::CONNECT_UNIX => - connect_unix_ready(file, ev); + connect_unix_ready(file, ev)?; case op::SIGNAL => signal_ready(file, ev); case op::TIMER => diff --git a/ev/+linux/signal.ha b/ev/+linux/signal.ha index ddfd07dd5cea4dc794dccf70953e302c0bb01895..2ac36c920e8b4c730321c8a658219b638cb456d7 100644 --- a/ev/+linux/signal.ha +++ b/ev/+linux/signal.ha @@ -17,7 +17,7 @@ export fn signal( loop: *loop, cb: *signalcb, signals: signal::sig... -) (*file | errors::error) = { +) (*file | errors::error | nomem) = { const fd = signal::signalfd(signals...)?; const file = register(loop, fd)?; file.op = op::SIGNAL; @@ -29,14 +29,14 @@ signal::block(signals...); return file; }; -fn signal_restore(file: *file) void = { +fn signal_restore(file: *file) (void | nomem) = { assert(file.op == op::SIGNAL); let buf: [rt::NSIG]signal::sig = [0...]; let signals = buf[..0]; for (let i = 1; i < rt::NSIG; i += 1) { const sig = i: signal::sig; if (signal::sigset_member(&file.sigmask, sig)) { - static append(signals, sig); + static append(signals, sig)?; }; }; signal::unblock(signals...); diff --git a/ev/+linux/socket.ha b/ev/+linux/socket.ha index 6df05ef98686796f842355e4bbec93fd576e4b42..58e65c3cba56e24f5a93481bc68813aae5b66f4d 100644 --- a/ev/+linux/socket.ha +++ b/ev/+linux/socket.ha @@ -13,7 +13,7 @@ loop: *loop, addr: ip::addr, port: u16, opts: tcp::listen_option... -) (*file | net::error | errors::error) = { +) (*file | net::error | errors::error | nomem) = { const sock = tcp::listen(addr, port, opts...)?; return register(loop, sock)?; }; @@ -25,7 +25,7 @@ loop: *loop, addr: ip::addr, port: u16, opts: udp::listen_option... -) (*file | net::error | errors::error) = { +) (*file | net::error | errors::error | nomem) = { const sock = udp::listen(addr, port, opts...)?; return register(loop, sock)?; }; @@ -35,7 +35,7 @@ export fn listen_unix( loop: *loop, addr: unix::addr, opts: unix::listen_option... -) (*file | net::error | errors::error) = { +) (*file | net::error | errors::error | nomem) = { const sock = unix::listen(addr, opts...)?; return register(loop, sock)?; }; @@ -47,7 +47,7 @@ loop: *loop, dest: ip::addr, port: u16, opts: udp::connect_option... -) (*file | net::error | errors::error) = { +) (*file | net::error | errors::error | nomem) = { const sock = udp::connect(dest, port, opts...)?; const file = register(loop, sock)?; return file; @@ -81,7 +81,7 @@ cb: *connectcb, addr: ip::addr, port: u16, opts: (net::sockflag | *opaque)... -) (req | net::error | errors::error) = { +) (req | net::error | errors::error | nomem) = { // XXX: This doesn't let us set keepalive let opt: net::sockflag = 0; let user: nullable *opaque = null; @@ -128,7 +128,7 @@ loop: *loop, cb: *connectcb, addr: unix::addr, opts: (net::sockflag | *opaque)... -) (req | net::error | errors::error) = { +) (req | net::error | errors::error | nomem) = { let opt: net::sockflag = 0; let user: nullable *opaque = null; for (let i = 0z; i < len(opts); i += 1) { @@ -152,7 +152,7 @@ fn connect_tcp_ready( sock: *file, ev: *rt::epoll_event, -) void = { +) (void | nomem) = { assert(sock.op == op::CONNECT_TCP); assert(ev.events & rt::EPOLLOUT != 0); assert(sock.cb != null); @@ -164,7 +164,7 @@ let errno = 0i, optsz = size(int): u32; rt::getsockopt(sock.fd, rt::SOL_SOCKET, rt::SO_ERROR, &errno, &optsz)!; if (errno != 0) { cb(errors::errno(errno), sock.user); - close(sock); + close(sock)?; } else { // XXX: If the user puts NONBLOCK into the opts provided at // [[connect_tcp]] we could try to preserve that here @@ -177,7 +177,7 @@ fn connect_unix_ready( sock: *file, ev: *rt::epoll_event, -) void = { +) (void | nomem) = { assert(sock.op == op::CONNECT_UNIX); assert(ev.events & rt::EPOLLOUT != 0); assert(sock.cb != null); @@ -189,7 +189,7 @@ let errno = 0i, optsz = size(int): u32; rt::getsockopt(sock.fd, rt::SOL_SOCKET, rt::SO_ERROR, &errno, &optsz)!; if (errno != 0) { cb(errors::errno(errno), sock.user); - close(sock); + close(sock)?; } else { // XXX: If the user puts NONBLOCK into the opts provided at // [[connect_unix]] we could try to preserve that here @@ -214,7 +214,7 @@ file_epoll_ctl(sock); }; // A callback for an [[accept]] operation. -export type acceptcb = fn(file: *file, result: (*file | net::error)) void; +export type acceptcb = fn(file: *file, result: (*file | net::error)) (void | nomem); // Schedules an accept operation on a socket. export fn accept( @@ -237,7 +237,7 @@ fn accept_ready( sock: *file, ev: *rt::epoll_event, -) void = { +) (void | nomem) = { assert(sock.op == op::ACCEPT); assert(ev.events & rt::EPOLLIN != 0); assert(sock.cb != null); @@ -250,9 +250,9 @@ match (r) { case let fd: net::socket => // TODO: Bubble up errors from here? const file = register(sock.ev, fd)!; - cb(sock, file); + cb(sock, file)?; case let err: net::error => - cb(sock, err); + cb(sock, err)?; }; }; diff --git a/ev/+linux/timers.ha b/ev/+linux/timers.ha index 6cfa265218680dd564507dd1dc9052200eb8c34b..2d400ce6133f004eda6e5170f0b4ade1a2aa8376 100644 --- a/ev/+linux/timers.ha +++ b/ev/+linux/timers.ha @@ -12,7 +12,7 @@ export fn newtimer( loop: *loop, cb: *timercb, clock: time::clock, -) (*file | errors::error) = { +) (*file | errors::error | nomem) = { const fd = match (rt::timerfd_create(clock, rt::TFD_NONBLOCK | rt::TFD_CLOEXEC)) { case let fd: int => diff --git a/ev/server/http.ha b/ev/server/http.ha index c39f4bdee8f9b170218f88d29cb8637e46f4294f..c05e6d90f32f9c75579bdeec3057f9b4b66c7148 100644 --- a/ev/server/http.ha +++ b/ev/server/http.ha @@ -19,14 +19,14 @@ export fn http_serve( sock: *ev::file, cb: *http_servecb, user: nullable *opaque, -) *server = { - const serv = newserver(sock, cb, user); +) (*server | nomem) = { + const serv = newserver(sock, cb, user)?; ev::setuser(sock, serv); ev::accept(sock, &http_accept); return serv; }; -fn http_accept(sock: *ev::file, r: (*ev::file | net::error)) void = { +fn http_accept(sock: *ev::file, r: (*ev::file | net::error)) (void | nomem) = { const server = ev::getuser(sock): *server; const sock = match (r) { @@ -40,24 +40,24 @@ const client = newclient( server, sock, - ); + )?; ev::setuser(client.sock, client); ev::read(client.sock, &http_read, client.rbuf); - append(server.clients, client); + append(server.clients, client)?; ev::accept(server.sock, &http_accept); }; -fn http_read(sock: *ev::file, r: (size | io::EOF | io::error)) void = { +fn http_read(sock: *ev::file, r: (size | io::EOF | io::error)) (void | nomem) = { const client = ev::getuser(sock): *server_client; const n = match (r) { case let err: io::error => - client_close(client); + client_close(client)?; return; case io::EOF => - client_close(client); + client_close(client)?; return; case let n: size => yield n; @@ -76,7 +76,7 @@ case io::EOF => ev::read(client.sock, &http_read, client.rbuf); return; case let err: (http::protoerr | errors::unsupported | io::error) => - client_close(client); + client_close(client)?; return; }; defer http::parsed_request_finish(&req); @@ -86,7 +86,7 @@ const resp = http::response { version = (1, 1), status = http::STATUS_OK, - reason = strings::dup(http::status_reason(http::STATUS_OK)), + reason = strings::dup(http::status_reason(http::STATUS_OK))?, header = [], body = io::empty, }; @@ -103,12 +103,12 @@ client.wbuf = memio::buffer(&client.buf); ev::write(client.sock, &http_write, client.wbuf); }; -fn http_write(sock: *ev::file, r: (size | io::error)) void = { +fn http_write(sock: *ev::file, r: (size | io::error)) (void | nomem) = { const client = ev::getuser(sock): *server_client; const n = match (r) { case let err: io::error => - client_close(client); + client_close(client)?; return; case let n: size => yield n; diff --git a/ev/server/server.ha b/ev/server/server.ha index 2d5c6abbce357b17374bf5daa878b5dcbb412455..06940251ad541fda96c76ddff647c8e8e9d0894b 100644 --- a/ev/server/server.ha +++ b/ev/server/server.ha @@ -19,33 +19,33 @@ wbuf: []u8, buf: memio::stream, }; -export fn newserver(sock: *ev::file, cb: *opaque, user: nullable *opaque) *server = { +export fn newserver(sock: *ev::file, cb: *opaque, user: nullable *opaque) (*server | nomem) = { return alloc(server { sock = sock, cb = cb, user = user, ... - }); + })?; }; -export fn server_finish(serv: *server) void = { +export fn server_finish(serv: *server) (void | nomem) = { for (let i = 0z; i < len(serv.clients); i += 1) { - client_close(serv.clients[i]); + client_close(serv.clients[i])?; i -= 1; }; free(serv); }; -export fn newclient(serv: *server, sock: *ev::file) *server_client = { +export fn newclient(serv: *server, sock: *ev::file) (*server_client | nomem) = { return alloc(server_client { server = serv, sock = sock, buf = memio::dynamic(), ... - }); + })?; }; -export fn client_close(client: *server_client) void = { +export fn client_close(client: *server_client) (void | nomem) = { const server = client.server; for (let i = 0z; i < len(server.clients); i += 1) { if (server.clients[i] == client) { @@ -53,7 +53,7 @@ delete(server.clients[i]); break; }; }; - ev::close(client.sock); + ev::close(client.sock)?; io::close(&client.buf)!; -- 2.48.1