207 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			V
		
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			V
		
	
	
| 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
 | |
| }
 |