Lindenii Project Forge
ev::file: initial commit
use errors; use io; use rt; export type file = struct { fd: io::file, }; // Registers a file descriptor with an event loop. export fn register( loop: *loop, fd: io::file, ) (*file | errors::error) = { const file = alloc(file { fd = fd, }); let ev = rt::epoll_event { events = 0, ... }; ev.data.ptr = file; match (rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_ADD, fd, &ev)) { case void => yield; case let err: rt::errno => return errors::errno(err); }; return file; }; // Unregisters a file object with an event loop and frees resources associated // with it. Does not close the underlying file descriptor. export fn unregister(loop: *loop, file: *file) void = { // The only way that this could fail is in the event of a use-after-free // or if the user fucks around and constructs a custom [[file]] which // was never registered, so assert on error. rt::epoll_ctl(loop.fd, rt::EPOLL_CTL_DEL, file.fd, null)!; free(file); };
use errors; use io; use rt; use time; use types; export type loop = struct {
epoll: io::file,
fd: io::file,
events: []rt::epoll_event, }; // Creates a new event loop. The user must pass the return value to // [[loop_finish]] to free associated resources when done using the loop. export fn newloop() (loop | errors::error) = { const fd = match (rt::epoll_create1(rt::EPOLL_CLOEXEC)) { case let fd: int => yield fd: io::file; case let err: rt::errno => return errors::errno(err); }; return loop {
epoll = fd,
fd = fd,
events = [], }; }; // Frees resources associated with an event loop. Must only be called once per // event loop object. export fn loop_finish(loop: *loop) void = {
io::close(loop.epoll)!;
io::close(loop.fd)!;
}; // Returns an [[io::file]] for this event loop which can be polled on when // events are available for processing, for chaining together different event // loops. The exact semantics of this function are platform-specific, and it may // not be available for all implementations. export fn loop_file(loop: *loop) io::file = {
return loop.epoll;
return loop.fd;
}; // Dispatches the event loop. A duration of -1 specifies an indefinite timeout, // and will cause dispatch to block until the next event is available. // // Portable use of the timeout argument supports only millisecond granularity of // up to 24 days (INT_MAX milliseconds). Negative values other than -1 will // cause the program to abort. // // Returns false if there are no events to process. export fn dispatch( loop: *loop, timeout: time::duration, ) (bool | errors::error) = { const millis: int = if (timeout == -1) { yield -1; } else if (timeout < 0) { abort("ev::dispatch: invalid timeout"); } else { yield (timeout / time::MILLISECOND): int; }; if (len(loop.events) == 0) { return false; }; // TODO: Deal with signals const maxev = len(loop.events); assert(maxev <= types::INT_MAX: size, "ev::dispatch: too many events"); const events = rt::epoll_pwait(
loop.epoll,
loop.fd,
&loop.events[0], maxev: int, millis, null)!; return true; };
// Defines events which are available for monitoring. export type event = enum u32 { // Read operations are available IN = 0x00000001, // Write operations are available OUT = 0x00000004, // An error condition has been reported ERR = 0x00000008, // There is exceptional data available in the file descriptor; // equivalent to [[unix::poll::event::POLLPRI]] PRI = 0x00000002, // A hang-up event has occured HUP = 0x00000010, }; // TODO: Add less portable flags like EPOLLWAKEUP