aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/os/notify/backend_linux.c.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/os/notify/backend_linux.c.v')
-rw-r--r--v_windows/v/vlib/os/notify/backend_linux.c.v206
1 files changed, 206 insertions, 0 deletions
diff --git a/v_windows/v/vlib/os/notify/backend_linux.c.v b/v_windows/v/vlib/os/notify/backend_linux.c.v
new file mode 100644
index 0000000..1913913
--- /dev/null
+++ b/v_windows/v/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
+}