| 1 | module notify |
| 2 | |
| 3 | import time |
| 4 | import os |
| 5 | |
| 6 | #insert "@VEXEROOT/vlib/os/notify/kqueue.h" |
| 7 | |
| 8 | pub struct C.kevent { |
| 9 | mut: |
| 10 | ident u32 |
| 11 | filter i16 |
| 12 | flags u16 |
| 13 | fflags u32 |
| 14 | data int |
| 15 | udata voidptr |
| 16 | } |
| 17 | |
| 18 | fn C.kqueue() i32 |
| 19 | fn C.__kevent__(i32, voidptr, i32, voidptr, i32, voidptr) i32 |
| 20 | fn C.EV_SET(voidptr, u32, i16, u16, u32, i32, voidptr) |
| 21 | |
| 22 | // KqueueNotifier provides methods that implement FdNotifier using the |
| 23 | // kqueue I/O event notification facility (macos, freeBSD, xxxBSD...unix only) |
| 24 | struct KqueueNotifier { |
| 25 | kqueue_fd int |
| 26 | } |
| 27 | |
| 28 | // KqueueEvent describes an event that occurred for a file descriptor in |
| 29 | // the watch list |
| 30 | struct KqueueEvent { |
| 31 | pub: |
| 32 | fd int |
| 33 | kind FdEventType |
| 34 | } |
| 35 | |
| 36 | // new creates a new KqueueNotifier. |
| 37 | // The FdNotifier interface is returned to allow OS specific |
| 38 | // implementations without exposing the concrete type |
| 39 | pub fn new() !FdNotifier { |
| 40 | fd := C.kqueue() |
| 41 | if fd == -1 { |
| 42 | return error(os.posix_get_error_msg(C.errno)) |
| 43 | } |
| 44 | // Needed to circumvent V limitations |
| 45 | x := &KqueueNotifier{ |
| 46 | kqueue_fd: fd |
| 47 | } |
| 48 | return x |
| 49 | } |
| 50 | |
| 51 | // filter types |
| 52 | const kqueue_read = i16(C.EVFILT_READ) |
| 53 | const kqueue_write = i16(C.EVFILT_WRITE) |
| 54 | const kqueue_aio = i16(C.EVFILT_AIO) |
| 55 | const kqueue_vnode = i16(C.EVFILT_VNODE) |
| 56 | const kqueue_proc = i16(C.EVFILT_PROC) |
| 57 | const kqueue_signal = i16(C.EVFILT_SIGNAL) |
| 58 | const kqueue_timer = i16(C.EVFILT_TIMER) |
| 59 | const kqueue_machport = i16(C.EVFILT_MACHPORT) |
| 60 | const kqueue_fs = i16(C.EVFILT_FS) |
| 61 | const kqueue_user = i16(C.EVFILT_USER) |
| 62 | const kqueue_vm = i16(C.EVFILT_VM) |
| 63 | const kqueue_exception = i16(C.EVFILT_EXCEPT) |
| 64 | const kqueue_syscount = i16(C.EVFILT_SYSCOUNT) |
| 65 | |
| 66 | // actions |
| 67 | const kqueue_add = u16(C.EV_ADD) |
| 68 | const kqueue_delete = u16(C.EV_DELETE) |
| 69 | const kqueue_enable = u16(C.EV_ENABLE) |
| 70 | const kqueue_disable = u16(C.EV_DISABLE) |
| 71 | |
| 72 | // flags |
| 73 | const kqueue_oneshot = u16(C.EV_ONESHOT) |
| 74 | const kqueue_edge_trigger = u16(C.EV_CLEAR) // kqueue_clear |
| 75 | |
| 76 | const kqueue_receipt = u16(C.EV_RECEIPT) |
| 77 | const kqueue_dispatch = u16(C.EV_DISPATCH) |
| 78 | const kqueue_udata_specific = u16(C.EV_UDATA_SPECIFIC) |
| 79 | const kqueue_dispatch2 = u16(C.EV_DISPATCH | C.EV_UDATA_SPECIFIC) |
| 80 | const kqueue_vanished = u16(C.EV_VANISHED) |
| 81 | const kqueue_sysflags = u16(C.EV_SYSFLAGS) |
| 82 | const kqueue_flag0 = u16(C.EV_FLAG0) |
| 83 | const kqueue_flag1 = u16(C.EV_FLAG1) |
| 84 | |
| 85 | // returned values |
| 86 | const kqueue_eof = u16(C.EV_EOF) |
| 87 | const kqueue_error = u16(C.EV_ERROR) |
| 88 | |
| 89 | // ctl is a helper method for add, modify, and remove |
| 90 | fn (mut kn KqueueNotifier) ctl(fd int, filter i16, flags u16) ! { |
| 91 | event := [1]C.kevent{} |
| 92 | C.EV_SET(&event[0], fd, filter, flags, 0, 0, unsafe { nil }) |
| 93 | if C.__kevent__(kn.kqueue_fd, &event[0], 1, unsafe { nil }, 0, unsafe { nil }) == -1 { |
| 94 | return error(os.posix_get_error_msg(C.errno)) |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | // add adds a file descriptor to the watch list |
| 99 | fn (mut kn KqueueNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ! { |
| 100 | filter := filter_to_mask(events) |
| 101 | flags := flags_to_mask(...conf) |
| 102 | kn.ctl(fd, filter, flags)! |
| 103 | } |
| 104 | |
| 105 | // modify sets an existing entry in the watch list to the provided events and configuration |
| 106 | fn (mut kn KqueueNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ! { |
| 107 | kn.add(fd, events, ...conf)! |
| 108 | } |
| 109 | |
| 110 | // remove removes a file descriptor from the watch list |
| 111 | fn (mut kn KqueueNotifier) remove(fd int) ! { |
| 112 | filter := kqueue_read | kqueue_write | kqueue_exception |
| 113 | flags := kqueue_delete |
| 114 | kn.ctl(fd, filter, flags)! |
| 115 | } |
| 116 | |
| 117 | // wait waits to be notified of events on the watch list, |
| 118 | // returns at most 512 events |
| 119 | fn (mut kn KqueueNotifier) wait(timeout time.Duration) []FdEvent { |
| 120 | // arbitrary 512 limit; events will round robin on successive |
| 121 | // waits if the number exceeds this |
| 122 | // NOTE: we use a fixed size array here for stack allocation; this has |
| 123 | // the added bonus of making KqueueNotifier thread safe |
| 124 | events := [512]C.kevent{} |
| 125 | // populate events with the new events |
| 126 | to := &C.timespec{0, timeout.nanoseconds()} |
| 127 | count := C.__kevent__(kn.kqueue_fd, unsafe { nil }, 0, &events[0], events.len, to) |
| 128 | |
| 129 | if count > 0 { |
| 130 | mut arr := []FdEvent{cap: count} |
| 131 | for i := 0; i < count; i++ { |
| 132 | fd := int(events[i].ident) |
| 133 | kind := event_mask_to_flag(events[i].filter, events[i].flags) |
| 134 | if kind.is_empty() { |
| 135 | // NOTE: tcc only reports the first event for some |
| 136 | // reason, leaving subsequent structs in the array as 0 |
| 137 | // (or possibly garbage) |
| 138 | panic('encountered an empty event kind; this is most likely due to using tcc') |
| 139 | } |
| 140 | arr << &KqueueEvent{ |
| 141 | fd: fd |
| 142 | kind: kind |
| 143 | } |
| 144 | } |
| 145 | return arr |
| 146 | } |
| 147 | return [] |
| 148 | } |
| 149 | |
| 150 | // close closes the KqueueNotifier, |
| 151 | // any successive calls to add, modify, remove, and wait should fail |
| 152 | fn (mut kn KqueueNotifier) close() ! { |
| 153 | if C.close(kn.kqueue_fd) == -1 { |
| 154 | return error(os.posix_get_error_msg(C.errno)) |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | // event_mask_to_flag is a helper function that converts a bitmask |
| 159 | // returned by kevent() wait to FdEventType |
| 160 | fn event_mask_to_flag(filter i16, flags u16) FdEventType { |
| 161 | mut res := unsafe { FdEventType(0) } |
| 162 | |
| 163 | if filter & kqueue_read != 0 { |
| 164 | res.set(.read) |
| 165 | } |
| 166 | if filter & kqueue_write != 0 { |
| 167 | res.set(.write) |
| 168 | } |
| 169 | if filter & kqueue_exception != 0 { |
| 170 | res.set(.exception) |
| 171 | } |
| 172 | |
| 173 | if flags & kqueue_eof != 0 { |
| 174 | res.set(.hangup) |
| 175 | } |
| 176 | if flags & kqueue_error != 0 { |
| 177 | res.set(.error) |
| 178 | } |
| 179 | |
| 180 | return res |
| 181 | } |
| 182 | |
| 183 | // filter_to_mask is a helper function that converts FdEventType |
| 184 | // to a bitmask used by the C functions |
| 185 | fn filter_to_mask(events FdEventType) i16 { |
| 186 | mut mask := i16(0) |
| 187 | if events.has(.read) { |
| 188 | mask |= kqueue_read |
| 189 | } |
| 190 | if events.has(.write) { |
| 191 | mask |= kqueue_write |
| 192 | } |
| 193 | if events.has(.exception) { |
| 194 | mask |= kqueue_exception |
| 195 | } |
| 196 | if events.has(.peer_hangup) { |
| 197 | panic("Kqueue does not support 'peer_hangup' event type.") |
| 198 | } |
| 199 | if events.has(.error) { |
| 200 | panic("Kqueue does not support 'error' event type.") |
| 201 | } |
| 202 | if events.has(.hangup) { |
| 203 | panic("Kqueue does not support 'hangup' event type.") |
| 204 | } |
| 205 | return mask |
| 206 | } |
| 207 | |
| 208 | // flags_to_mask is a helper function that converts FdConfigFlags |
| 209 | // to a bitmask used by the C functions |
| 210 | fn flags_to_mask(confs ...FdConfigFlags) u16 { |
| 211 | mut mask := kqueue_add | kqueue_enable |
| 212 | for conf in confs { |
| 213 | if conf.has(.edge_trigger) { |
| 214 | mask |= kqueue_edge_trigger |
| 215 | } |
| 216 | if conf.has(.one_shot) { |
| 217 | mask |= kqueue_oneshot |
| 218 | } |
| 219 | if conf.has(.wake_up) { |
| 220 | panic("Kqueue does not support 'wake_up' flag.") |
| 221 | } |
| 222 | if conf.has(.exclusive) { |
| 223 | panic("Kqueue does not support 'exclusive' flag.") |
| 224 | } |
| 225 | } |
| 226 | return mask |
| 227 | } |
| 228 | |