1 module dast.async.selector.epoll;
2 
3 version (linux)  : import dast.async.core,
4 core.time,
5 core.stdc.string,
6 core.stdc.errno,
7 core.sys.linux.epoll,
8 core.sys.linux.sys.eventfd,
9 core.sys.posix.netinet.tcp,
10 core.sys.posix.netinet.in_,
11 core.sys.posix.unistd;
12 
13 version (HaveTimer) import dast.async.timer;
14 
15 alias Selector = Epoll;
16 
17 template Epoll() {
18 	this() {
19 		_eventHandle = epoll_create1(0);
20 		flags |= WF.Read;
21 		handle = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
22 		register(this);
23 	}
24 
25 	~this() {
26 		close();
27 		dispose();
28 	}
29 
30 	void dispose() {
31 		if (!_eventHandle)
32 			return;
33 		unregister(this);
34 		close(_eventHandle);
35 		_eventHandle = 0;
36 	}
37 
38 	bool register(SocketChannel watcher)
39 	in (watcher) {
40 		// debug (Log) info("register, watcher fd=", watcher.handle);
41 		const fd = watcher.handle;
42 		assert(fd >= 0, "The watcher.handle is not initilized");
43 
44 		// if (fd < 0) return false;
45 		epoll_event ev = buildEvent(watcher);
46 		if (epoll_ctl(_eventHandle, EPOLL_CTL_ADD, fd, &ev) != 0) {
47 			if (errno != EEXIST)
48 				return false;
49 		}
50 		return true;
51 	}
52 	/+
53 	bool reregister(SocketChannel watcher)
54 	in (watcher) {
55 		const fd = watcher.handle;
56 		if (fd < 0)
57 			return false;
58 		auto ev = buildEvent(watcher);
59 		return epoll_ctl(_eventHandle, EPOLL_CTL_MOD, fd, &ev) == 0;
60 	}
61 +/
62 
63 	bool unregister(SocketChannel watcher)
64 	in (watcher) {
65 		debug (Log)
66 			info("unregister watcher fd=", watcher.handle);
67 
68 		const fd = watcher.handle;
69 		if (fd < 0)
70 			return false;
71 
72 		if (epoll_ctl(_eventHandle, EPOLL_CTL_DEL, fd, null)) {
73 			error("unregister failed, watcher.handle=", watcher.handle);
74 			return false;
75 		}
76 		// TODO: check this
77 		// watcher.clear();
78 		return true;
79 	}
80 
81 	void onRead() @system {
82 		ulong value = void;
83 		read(handle, &value, value.sizeof);
84 		_rBuf.data = value;
85 	}
86 
87 	void onWeakUp() @system {
88 		epoll_event[64] events;
89 		const len = epoll_wait(_eventHandle, events.ptr, events.length, 10);
90 		foreach (i; 0 .. len) {
91 			auto watch = cast(SocketChannel)events[i].data.ptr;
92 			assert(watch);
93 
94 			if (events[i].events & (EPOLLHUP | EPOLLERR | EPOLLRDHUP)) {
95 				debug (Log)
96 					warning("close event: ", watch.handle);
97 				watch.close();
98 				continue;
99 			}
100 
101 			if (watch.isRegistered && (events[i].events & EPOLLIN)) {
102 				watch.onRead();
103 			}
104 
105 			if (watch.isRegistered && (events[i].events & EPOLLOUT)) {
106 				assert(cast(SocketChannel)watch);
107 				// watch.onWrite();
108 			}
109 		}
110 	}
111 
112 	private int _eventHandle;
113 }
114 
115 epoll_event buildEvent(SocketChannel watch) {
116 	const flags = watch.flags;
117 	uint events = EPOLLRDHUP | EPOLLERR | EPOLLHUP | (flags & WF.ReadWrite);
118 	events |= (flags & WF.OneShotET) << 26;
119 	return epoll_event(events, epoll_data_t(watch));
120 }