1 module dast.async.selector.iocp;
2 
3 version (Windows)  : import dast.async.core,
4 dast.async.iocp,
5 dast.async.tcpclient;
6 
7 alias Selector = Iocp;
8 
9 template Iocp() {
10 	import dast.async.core,
11 	dast.async.iocp,
12 	dast.async.tcpclient,
13 	dast.async.thread,
14 	tame.env : totalCPUs;
15 
16 @safe:
17 	ThreadPool* workerPool;
18 	this(uint threads = totalCPUs - 1) @trusted {
19 		_eventHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
20 		if (!_eventHandle)
21 			throw new Exception("CreateIoCompletionPort failed");
22 		if (threads)
23 			workerPool = new ThreadPool(threads);
24 	}
25 
26 	/*~this() @nogc nothrow {
27 		CloseHandle(_eventHandle);
28 	}*/
29 
30 	bool register(SocketChannel watcher) @trusted nothrow
31 	in (watcher.type <= WT.Event) {
32 		const fd = watcher.handle;
33 		const type = watcher.type;
34 		if (type == WT.TCP || type == WT.Accept) {
35 			debug (Log)
36 				trace("register on socket: ", fd);
37 			CreateIoCompletionPort(cast(HANDLE)fd, _eventHandle, cast(ulong)cast(void*)watcher, 0);
38 		} else
39 			return false;
40 
41 		debug (Log)
42 			info("watcher(fd=", fd, ", type=", type, ')');
43 		_watchers ~= watcher;
44 		return true;
45 	}
46 
47 	void weakUp() nothrow @trusted {
48 		IocpContext ctx = {operation: IocpOperation.event}; // TODO
49 		PostQueuedCompletionStatus(_eventHandle, 0, 0, &ctx.overlapped);
50 	}
51 
52 	void onWeakUp() @trusted {
53 		enum timeout = 250, N = 32;
54 		OVERLAPPED_ENTRY[N] entries = void;
55 		uint n = void;
56 		const ret = GetQueuedCompletionStatusEx(_eventHandle, entries.ptr, entries.length, &n, timeout, 0);
57 		if (ret == 0) {
58 			debug (Log) {
59 				const err = GetLastError();
60 				if (err != WAIT_TIMEOUT) // && err != ERROR_OPERATION_ABORTED
61 					error("error occurred, code=", err);
62 			}
63 			return;
64 		}
65 		foreach (i; 0 .. n) {
66 			auto entry = entries[i];
67 			if (workerPool)
68 				workerPool.run!handleEvent(entry);
69 			else
70 				handleEvent(entry);
71 		}
72 	}
73 
74 private:
75 	HANDLE _eventHandle;
76 	SocketChannel[] _watchers;
77 }
78 
79 package(dast.async) void handleEvent(OVERLAPPED_ENTRY entry) {
80 	import dast.async.tcpserver;
81 
82 	const len = entry.dwNumberOfBytesTransferred;
83 	auto ev = cast(IocpContext*)entry.lpOverlapped;
84 	auto channel = cast(void*)entry.lpCompletionKey;
85 	switch (ev.operation) with (IocpOperation) {
86 	case accept:
87 		(cast(TCPServer)channel).onRead();
88 		break;
89 	case read:
90 		if (len && (cast(SocketChannel)channel).isRegistered)
91 			(cast(TCPClient)channel).onRead(len);
92 		break;
93 	case write:
94 		debug (Log)
95 			info("finishing writing ", len, " bytes");
96 		// Notify the client about how many bytes actually sent
97 		(cast(TCPClient)channel).onWrite(len);
98 		break;
99 		// TODO
100 		//case event:
101 		//break;
102 	default:
103 	}
104 }