diff options
Diffstat (limited to 'v_windows/v/old/vlib/os/notify')
-rw-r--r-- | v_windows/v/old/vlib/os/notify/backend_default.c.v | 6 | ||||
-rw-r--r-- | v_windows/v/old/vlib/os/notify/backend_linux.c.v | 206 | ||||
-rw-r--r-- | v_windows/v/old/vlib/os/notify/notify.v | 35 | ||||
-rw-r--r-- | v_windows/v/old/vlib/os/notify/notify_test.v | 155 |
4 files changed, 402 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/os/notify/backend_default.c.v b/v_windows/v/old/vlib/os/notify/backend_default.c.v new file mode 100644 index 0000000..1a35c50 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/backend_default.c.v @@ -0,0 +1,6 @@ +module notify + +// Implement the API +pub fn new() ?FdNotifier { + panic('unsupported') +} diff --git a/v_windows/v/old/vlib/os/notify/backend_linux.c.v b/v_windows/v/old/vlib/os/notify/backend_linux.c.v new file mode 100644 index 0000000..1913913 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/backend_linux.c.v @@ -0,0 +1,206 @@ +module notify + +import time +import os + +#include <sys/epoll.h> + +struct C.epoll_event { + events u32 + data C.epoll_data_t +} + +[typedef] +union C.epoll_data_t { + ptr voidptr + fd int + u32 u32 + u64 u64 +} + +fn C.epoll_create1(int) int + +fn C.epoll_ctl(int, int, int, &C.epoll_event) int + +fn C.epoll_wait(int, &C.epoll_event, int, int) int + +// EpollNotifier provides methods that implement FdNotifier using the +// epoll I/O event notification facility (linux only) +[noinit] +struct EpollNotifier { + epoll_fd int +} + +// EpollEvent describes an event that occurred for a file descriptor in +// the watch list +[noinit] +struct EpollEvent { +pub: + fd int + kind FdEventType +} + +// new creates a new EpollNotifier +// The FdNotifier interface is returned to allow OS specific +// implementations without exposing the concrete type +pub fn new() ?FdNotifier { + fd := C.epoll_create1(0) // 0 indicates default behavior + if fd == -1 { + return error(os.posix_get_error_msg(C.errno)) + } + // Needed to circumvent V limitations + x := &EpollNotifier{ + epoll_fd: fd + } + return x +} + +const ( + epoll_read = u32(C.EPOLLIN) + epoll_write = u32(C.EPOLLOUT) + epoll_peer_hangup = u32(C.EPOLLRDHUP) + epoll_exception = u32(C.EPOLLPRI) + epoll_error = u32(C.EPOLLERR) + epoll_hangup = u32(C.EPOLLHUP) + epoll_edge_trigger = u32(C.EPOLLET) + epoll_one_shot = u32(C.EPOLLONESHOT) + epoll_wake_up = u32(C.EPOLLWAKEUP) + epoll_exclusive = u32(C.EPOLLEXCLUSIVE) +) + +// ctl is a helper method for add, modify, and remove +fn (mut en EpollNotifier) ctl(fd int, op int, mask u32) ? { + event := C.epoll_event{ + events: mask + data: C.epoll_data_t{ + fd: fd + } + } + if C.epoll_ctl(en.epoll_fd, op, fd, &event) == -1 { + return error(os.posix_get_error_msg(C.errno)) + } +} + +// add adds a file descriptor to the watch list +fn (mut en EpollNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ? { + mask := flags_to_mask(events, ...conf) + en.ctl(fd, C.EPOLL_CTL_ADD, mask) ? +} + +// modify sets an existing entry in the watch list to the provided events and configuration +fn (mut en EpollNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ? { + mask := flags_to_mask(events, ...conf) + en.ctl(fd, C.EPOLL_CTL_MOD, mask) ? +} + +// remove removes a file descriptor from the watch list +fn (mut en EpollNotifier) remove(fd int) ? { + en.ctl(fd, C.EPOLL_CTL_DEL, 0) ? +} + +// wait waits to be notified of events on the watch list, +// returns at most 512 events +fn (mut en EpollNotifier) wait(timeout time.Duration) []FdEvent { + // arbitrary 512 limit; events will round robin on successive + // waits if the number exceeds this + // NOTE: we use a fixed size array here for stack allocation; this has + // the added bonus of making EpollNotifier thread safe + events := [512]C.epoll_event{} + // populate events with the new events + to := timeout.sys_milliseconds() + count := C.epoll_wait(en.epoll_fd, &events[0], events.len, to) + + if count > 0 { + mut arr := []FdEvent{cap: count} + for i := 0; i < count; i++ { + fd := unsafe { events[i].data.fd } + kind := event_mask_to_flag(events[i].events) + if kind.is_empty() { + // NOTE: tcc only reports the first event for some + // reason, leaving subsequent structs in the array as 0 + // (or possibly garbage) + panic('encountered an empty event kind; this is most likely due to using tcc') + } + arr << &EpollEvent{ + fd: fd + kind: kind + } + } + return arr + } + return [] +} + +// close closes the EpollNotifier, +// any successive calls to add, modify, remove, and wait should fail +fn (mut en EpollNotifier) close() ? { + if C.close(en.epoll_fd) == -1 { + return error(os.posix_get_error_msg(C.errno)) + } +} + +// event_mask_to_flag is a helper function that converts a bitmask +// returned by epoll_wait to FdEventType +fn event_mask_to_flag(mask u32) FdEventType { + mut flags := FdEventType{} + + if mask & notify.epoll_read != 0 { + flags.set(.read) + } + if mask & notify.epoll_write != 0 { + flags.set(.write) + } + if mask & notify.epoll_peer_hangup != 0 { + flags.set(.peer_hangup) + } + if mask & notify.epoll_exception != 0 { + flags.set(.exception) + } + if mask & notify.epoll_error != 0 { + flags.set(.error) + } + if mask & notify.epoll_hangup != 0 { + flags.set(.hangup) + } + + return flags +} + +// flags_to_mask is a helper function that converts FdEventType and +// FdConfigFlags to a bitmask used by the C functions +fn flags_to_mask(events FdEventType, confs ...FdConfigFlags) u32 { + mut mask := u32(0) + if events.has(.read) { + mask |= notify.epoll_read + } + if events.has(.write) { + mask |= notify.epoll_write + } + if events.has(.peer_hangup) { + mask |= notify.epoll_peer_hangup + } + if events.has(.exception) { + mask |= notify.epoll_exception + } + if events.has(.error) { + mask |= notify.epoll_error + } + if events.has(.hangup) { + mask |= notify.epoll_hangup + } + for conf in confs { + if conf.has(.edge_trigger) { + mask |= notify.epoll_edge_trigger + } + if conf.has(.one_shot) { + mask |= notify.epoll_one_shot + } + if conf.has(.wake_up) { + mask |= notify.epoll_wake_up + } + if conf.has(.exclusive) { + mask |= notify.epoll_exclusive + } + } + return mask +} diff --git a/v_windows/v/old/vlib/os/notify/notify.v b/v_windows/v/old/vlib/os/notify/notify.v new file mode 100644 index 0000000..b49dad3 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/notify.v @@ -0,0 +1,35 @@ +module notify + +import time + +// Backends should provide a `new() ?FdNotifier` function +pub interface FdNotifier { + add(fd int, events FdEventType, conf ...FdConfigFlags) ? + modify(fd int, events FdEventType, conf ...FdConfigFlags) ? + remove(fd int) ? + wait(timeout time.Duration) []FdEvent + close() ? +} + +pub interface FdEvent { + fd int + kind FdEventType +} + +[flag] +pub enum FdEventType { + read + write + peer_hangup + exception + error + hangup +} + +[flag] +pub enum FdConfigFlags { + edge_trigger + one_shot + wake_up + exclusive +} diff --git a/v_windows/v/old/vlib/os/notify/notify_test.v b/v_windows/v/old/vlib/os/notify/notify_test.v new file mode 100644 index 0000000..253c94f --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/notify_test.v @@ -0,0 +1,155 @@ +import os +import os.notify + +// make a pipe and return the (read, write) file descriptors +fn make_pipe() ?(int, int) { + $if linux { + pipefd := [2]int{} + if C.pipe(&pipefd[0]) != 0 { + return error('error $C.errno: ' + os.posix_get_error_msg(C.errno)) + } + return pipefd[0], pipefd[1] + } + return -1, -1 +} + +fn test_level_trigger() ? { + // currently only linux is supported + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + check_read_event(notifier, reader, 'bar') + + assert notifier.wait(0).len == 0 + } +} + +fn test_edge_trigger() ? { + // currently only linux is supported + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read, .edge_trigger) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + + assert notifier.wait(0).len == 0 + + os.fd_write(writer, 'baz') + // we do not get an event because there is still data + // to be read + assert notifier.wait(0).len == 0 + } +} + +fn test_one_shot() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read, .one_shot) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + os.fd_write(writer, 'baz') + + assert notifier.wait(0).len == 0 + + // rearm + notifier.modify(reader, .read) ? + check_read_event(notifier, reader, 'barbaz') + } +} + +fn test_hangup() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + notifier.close() or {} + } + notifier.add(reader, .hangup) ? + + assert notifier.wait(0).len == 0 + + // closing on the writer end of the pipe will + // cause a hangup on the reader end + os.fd_close(writer) + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == reader + assert events[0].kind.has(.hangup) + } +} + +fn test_write() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + + notifier.add(reader, .write) ? + assert notifier.wait(0).len == 0 + + notifier.add(writer, .write) ? + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == writer + assert events[0].kind.has(.write) + } +} + +fn test_remove() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + + // level triggered - will keep getting events while + // there is data to read + notifier.add(reader, .read) ? + os.fd_write(writer, 'foobar') + assert notifier.wait(0).len == 1 + assert notifier.wait(0).len == 1 + + notifier.remove(reader) ? + assert notifier.wait(0).len == 0 + } +} + +fn check_read_event(notifier notify.FdNotifier, reader_fd int, expected string) { + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == reader_fd + assert events[0].kind.has(.read) + s, _ := os.fd_read(events[0].fd, expected.len) + assert s == expected +} |