1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.event.selector.Kqueue; 13 14 15 // dfmt off 16 version(HAVE_KQUEUE): 17 // dfmt on 18 import hunt.event.selector.Selector; 19 import hunt.event.timer.Kqueue; 20 import hunt.Exceptions; 21 import hunt.io.channel; 22 import hunt.logging.ConsoleLogger; 23 import hunt.util.Common; 24 25 import std.exception; 26 import std.socket; 27 import std.string; 28 29 import core.time; 30 import core.stdc.string; 31 import core.stdc.errno; 32 import core.sys.posix.sys.types; // for ssize_t, size_t 33 import core.sys.posix.signal; 34 import core.sys.posix.netinet.tcp; 35 import core.sys.posix.netinet.in_; 36 import core.sys.posix.unistd; 37 import core.sys.posix.time; 38 39 /** 40 * 41 */ 42 class AbstractSelector : Selector { 43 // kevent array size 44 enum int NUM_KEVENTS = 128; 45 private bool isDisposed = false; 46 private Kevent[NUM_KEVENTS] events; 47 private int _kqueueFD; 48 private EventChannel _eventChannel; 49 50 this(size_t number, size_t divider, size_t maxChannels = 1500) { 51 super(number, divider, maxChannels); 52 _kqueueFD = kqueue(); 53 _eventChannel = new KqueueEventChannel(this); 54 register(_eventChannel); 55 } 56 57 ~this() @nogc { 58 // dispose(); 59 } 60 61 override void dispose() { 62 if (isDisposed) 63 return; 64 65 version (HUNT_IO_DEBUG) 66 tracef("disposing selector[fd=%d]...", _kqueueFD); 67 isDisposed = true; 68 _eventChannel.close(); 69 int r = core.sys.posix.unistd.close(_kqueueFD); 70 if(r != 0) { 71 version(HUNT_DEBUG) warningf("error: %d", r); 72 } 73 74 super.dispose(); 75 } 76 77 override void onStop() { 78 version (HUNT_IO_DEBUG) 79 infof("Selector stopping. fd=%d", _kqueueFD); 80 81 if(!_eventChannel.isClosed()) { 82 _eventChannel.trigger(); 83 // _eventChannel.onWrite(); 84 } 85 } 86 87 override bool register(AbstractChannel channel) { 88 assert(channel !is null); 89 90 const int fd = channel.handle; 91 version (HUNT_IO_DEBUG) 92 tracef("register channel: fd=%d, type=%s", fd, channel.type); 93 94 int err = -1; 95 if (channel.type == ChannelType.Timer) { 96 Kevent ev; 97 AbstractTimer timerChannel = cast(AbstractTimer) channel; 98 if (timerChannel is null) 99 return false; 100 size_t time = timerChannel.time < 20 ? 20 : timerChannel.time; // in millisecond 101 EV_SET(&ev, timerChannel.handle, EVFILT_TIMER, 102 EV_ADD | EV_ENABLE | EV_CLEAR, 0, time, cast(void*) channel); 103 err = kevent(_kqueueFD, &ev, 1, null, 0, null); 104 } 105 else { 106 if (fd < 0) 107 return false; 108 Kevent[2] ev = void; 109 short read = EV_ADD | EV_ENABLE; 110 short write = EV_ADD | EV_ENABLE; 111 if (channel.hasFlag(ChannelFlag.ETMode)) { 112 read |= EV_CLEAR; 113 write |= EV_CLEAR; 114 } 115 EV_SET(&(ev[0]), fd, EVFILT_READ, read, 0, 0, cast(void*) channel); 116 EV_SET(&(ev[1]), fd, EVFILT_WRITE, write, 0, 0, cast(void*) channel); 117 if (channel.hasFlag(ChannelFlag.Read) && channel.hasFlag(ChannelFlag.Write)) 118 err = kevent(_kqueueFD, &(ev[0]), 2, null, 0, null); 119 else if (channel.hasFlag(ChannelFlag.Read)) 120 err = kevent(_kqueueFD, &(ev[0]), 1, null, 0, null); 121 else if (channel.hasFlag(ChannelFlag.Write)) 122 err = kevent(_kqueueFD, &(ev[1]), 1, null, 0, null); 123 } 124 if (err < 0) { 125 return false; 126 } 127 return true; 128 } 129 130 override bool deregister(AbstractChannel channel) { 131 assert(channel !is null); 132 const fd = channel.handle; 133 if (fd < 0) 134 return false; 135 136 int err = -1; 137 if (channel.type == ChannelType.Timer) { 138 Kevent ev; 139 AbstractTimer timerChannel = cast(AbstractTimer) channel; 140 if (timerChannel is null) 141 return false; 142 EV_SET(&ev, fd, EVFILT_TIMER, EV_DELETE, 0, 0, cast(void*) channel); 143 err = kevent(_kqueueFD, &ev, 1, null, 0, null); 144 } 145 else { 146 Kevent[2] ev = void; 147 EV_SET(&(ev[0]), fd, EVFILT_READ, EV_DELETE, 0, 0, cast(void*) channel); 148 EV_SET(&(ev[1]), fd, EVFILT_WRITE, EV_DELETE, 0, 0, cast(void*) channel); 149 if (channel.hasFlag(ChannelFlag.Read) && channel.hasFlag(ChannelFlag.Write)) 150 err = kevent(_kqueueFD, &(ev[0]), 2, null, 0, null); 151 else if (channel.hasFlag(ChannelFlag.Read)) 152 err = kevent(_kqueueFD, &(ev[0]), 1, null, 0, null); 153 else if (channel.hasFlag(ChannelFlag.Write)) 154 err = kevent(_kqueueFD, &(ev[1]), 1, null, 0, null); 155 } 156 if (err < 0) { 157 return false; 158 } 159 // channel.currtLoop = null; 160 channel.clear(); 161 return true; 162 } 163 164 protected override int doSelect(long timeout) { 165 timespec ts; 166 timespec *tsp; 167 // timeout is in milliseconds. Convert to struct timespec. 168 // timeout == -1 : wait forever : timespec timeout of NULL 169 // timeout == 0 : return immediately : timespec timeout of zero 170 if (timeout >= 0) { 171 // For some indeterminate reason kevent(2) has been found to fail with 172 // an EINVAL error for timeout values greater than or equal to 173 // 100000001000L. To avoid this problem, clamp the timeout arbitrarily 174 // to the maximum value of a 32-bit signed integer which is 175 // approximately 25 days in milliseconds. 176 const int timeoutMax = int.max; 177 if (timeout > timeoutMax) { 178 timeout = timeoutMax; 179 } 180 ts.tv_sec = timeout / 1000; 181 ts.tv_nsec = (timeout % 1000) * 1000000; //nanosec = 1 million millisec 182 tsp = &ts; 183 } else { 184 tsp = null; 185 } 186 187 // auto tspec = timespec(1, 1000 * 10); 188 int result = kevent(_kqueueFD, null, 0, events.ptr, events.length, tsp); 189 190 foreach (i; 0 .. result) { 191 AbstractChannel channel = cast(AbstractChannel)(events[i].udata); 192 ushort eventFlags = events[i].flags; 193 version (HUNT_IO_DEBUG) 194 infof("handling event: events=%d, fd=%d", eventFlags, channel.handle); 195 196 if (eventFlags & EV_ERROR) { 197 warningf("channel[fd=%d] has a error.", channel.handle); 198 channel.close(); 199 continue; 200 } 201 if (eventFlags & EV_EOF) { 202 version (HUNT_IO_DEBUG) infof("channel[fd=%d] closed", channel.handle); 203 channel.close(); 204 continue; 205 } 206 207 short filter = events[i].filter; 208 handeChannelEvent(channel, filter); 209 } 210 return result; 211 } 212 213 private void handeChannelEvent(AbstractChannel channel, uint filter) { 214 version (HUNT_IO_DEBUG) 215 infof("handling event: events=%d, fd=%d", filter, channel.handle); 216 try { 217 if(filter == EVFILT_TIMER) { 218 channel.onRead(); 219 } else if (filter == EVFILT_WRITE) { 220 channel.onWrite(); 221 } else if (filter == EVFILT_READ) { 222 channel.onRead(); 223 } else { 224 warningf("Unhandled channel fileter: %d", filter); 225 } 226 } catch(Exception e) { 227 errorf("error while handing channel: fd=%s, message=%s", 228 channel.handle, e.msg); 229 } 230 } 231 } 232 233 234 enum : short { 235 EVFILT_READ = -1, 236 EVFILT_WRITE = -2, 237 EVFILT_AIO = -3, /* attached to aio requests */ 238 EVFILT_VNODE = -4, /* attached to vnodes */ 239 EVFILT_PROC = -5, /* attached to struct proc */ 240 EVFILT_SIGNAL = -6, /* attached to struct proc */ 241 EVFILT_TIMER = -7, /* timers */ 242 EVFILT_MACHPORT = -8, /* Mach portsets */ 243 EVFILT_FS = -9, /* filesystem events */ 244 EVFILT_USER = -10, /* User events */ 245 EVFILT_VM = -12, /* virtual memory events */ 246 EVFILT_SYSCOUNT = 11 247 } 248 249 extern (D) void EV_SET(Kevent* kevp, typeof(Kevent.tupleof) args) @nogc nothrow { 250 *kevp = Kevent(args); 251 } 252 253 struct Kevent { 254 uintptr_t ident; /* identifier for this event */ 255 short filter; /* filter for event */ 256 ushort flags; 257 uint fflags; 258 intptr_t data; 259 void* udata; /* opaque user data identifier */ 260 } 261 262 enum { 263 /* actions */ 264 EV_ADD = 0x0001, /* add event to kq (implies enable) */ 265 EV_DELETE = 0x0002, /* delete event from kq */ 266 EV_ENABLE = 0x0004, /* enable event */ 267 EV_DISABLE = 0x0008, /* disable event (not reported) */ 268 269 /* flags */ 270 EV_ONESHOT = 0x0010, /* only report one occurrence */ 271 EV_CLEAR = 0x0020, /* clear event state after reporting */ 272 EV_RECEIPT = 0x0040, /* force EV_ERROR on success, data=0 */ 273 EV_DISPATCH = 0x0080, /* disable event after reporting */ 274 275 EV_SYSFLAGS = 0xF000, /* reserved by system */ 276 EV_FLAG1 = 0x2000, /* filter-specific flag */ 277 278 /* returned values */ 279 EV_EOF = 0x8000, /* EOF detected */ 280 EV_ERROR = 0x4000, /* error, data contains errno */ 281 282 } 283 284 enum { 285 /* 286 * data/hint flags/masks for EVFILT_USER, shared with userspace 287 * 288 * On input, the top two bits of fflags specifies how the lower twenty four 289 * bits should be applied to the stored value of fflags. 290 * 291 * On output, the top two bits will always be set to NOTE_FFNOP and the 292 * remaining twenty four bits will contain the stored fflags value. 293 */ 294 NOTE_FFNOP = 0x00000000, /* ignore input fflags */ 295 NOTE_FFAND = 0x40000000, /* AND fflags */ 296 NOTE_FFOR = 0x80000000, /* OR fflags */ 297 NOTE_FFCOPY = 0xc0000000, /* copy fflags */ 298 NOTE_FFCTRLMASK = 0xc0000000, /* masks for operations */ 299 NOTE_FFLAGSMASK = 0x00ffffff, 300 301 NOTE_TRIGGER = 0x01000000, /* Cause the event to be 302 triggered for output. */ 303 304 /* 305 * data/hint flags for EVFILT_{READ|WRITE}, shared with userspace 306 */ 307 NOTE_LOWAT = 0x0001, /* low water mark */ 308 309 /* 310 * data/hint flags for EVFILT_VNODE, shared with userspace 311 */ 312 NOTE_DELETE = 0x0001, /* vnode was removed */ 313 NOTE_WRITE = 0x0002, /* data contents changed */ 314 NOTE_EXTEND = 0x0004, /* size increased */ 315 NOTE_ATTRIB = 0x0008, /* attributes changed */ 316 NOTE_LINK = 0x0010, /* link count changed */ 317 NOTE_RENAME = 0x0020, /* vnode was renamed */ 318 NOTE_REVOKE = 0x0040, /* vnode access was revoked */ 319 320 /* 321 * data/hint flags for EVFILT_PROC, shared with userspace 322 */ 323 NOTE_EXIT = 0x80000000, /* process exited */ 324 NOTE_FORK = 0x40000000, /* process forked */ 325 NOTE_EXEC = 0x20000000, /* process exec'd */ 326 NOTE_PCTRLMASK = 0xf0000000, /* mask for hint bits */ 327 NOTE_PDATAMASK = 0x000fffff, /* mask for pid */ 328 329 /* additional flags for EVFILT_PROC */ 330 NOTE_TRACK = 0x00000001, /* follow across forks */ 331 NOTE_TRACKERR = 0x00000002, /* could not track child */ 332 NOTE_CHILD = 0x00000004, /* am a child process */ 333 334 } 335 336 extern (C) { 337 int kqueue() @nogc nothrow; 338 int kevent(int kq, const Kevent* changelist, int nchanges, 339 Kevent* eventlist, int nevents, const timespec* timeout) @nogc nothrow; 340 } 341 342 static if (CompilerHelper.isLessThan(2078)) { 343 enum SO_REUSEPORT = 0x0200; 344 }