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.IOCP;
13 
14 // dfmt off
15 version (HAVE_IOCP) : 
16 // dfmt on
17 
18 import hunt.event.selector.Selector;
19 import hunt.io.channel.Common;
20 import hunt.io.channel;
21 import hunt.event.timer;
22 import hunt.logging.ConsoleLogger;
23 import hunt.system.Error;
24 import hunt.io.channel.iocp.AbstractStream;
25 import core.sys.windows.windows;
26 import std.conv;
27 import std.container : DList;
28 /**
29 */
30 class AbstractSelector : Selector {
31 
32     this(size_t number, size_t divider, size_t maxChannels = 1500) {
33         super(number, divider, maxChannels);
34         _iocpHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, null, 0, 0);
35         if (_iocpHandle is null)
36             errorf("CreateIoCompletionPort failed: %d\n", GetLastError());
37         _timer.init();
38     }
39 
40     ~this() {
41         // import std.socket;
42         // std.socket.close(_iocpHandle);
43     }
44 
45     override bool register(AbstractChannel channel) {
46         assert(channel !is null);
47         ChannelType ct = channel.type;
48         auto fd = channel.handle;
49         version (HUNT_IO_DEBUG)
50             tracef("register, channel(fd=%d, type=%s)", fd, ct);
51 
52         if (ct == ChannelType.Timer) {
53             AbstractTimer timerChannel = cast(AbstractTimer) channel;
54             assert(timerChannel !is null);
55             if (!timerChannel.setTimerOut())
56                 return false;
57             _timer.timeWheel().addNewTimer(timerChannel.timer, timerChannel.wheelSize());
58         } else if (ct == ChannelType.TCP
59                 || ct == ChannelType.Accept || ct == ChannelType.UDP) {
60             version (HUNT_IO_DEBUG)
61                 trace("Run CreateIoCompletionPort on socket: ", fd);
62 
63             // _event.setNext(channel);
64             CreateIoCompletionPort(cast(HANDLE) fd, _iocpHandle,
65                     cast(size_t)(cast(void*) channel), 0);
66 
67             //cast(AbstractStream)channel)
68         } else {
69             warningf("Can't register a channel: %s", ct);
70         }
71 
72         (cast(AbstractStream)channel).beginRead();
73         return true;
74     }
75 
76     override bool deregister(AbstractChannel channel) {
77         // FIXME: Needing refactor or cleanup -@Administrator at 8/28/2018, 3:28:18 PM
78         // https://stackoverflow.com/questions/6573218/removing-a-handle-from-a-i-o-completion-port-and-other-questions-about-iocp
79         tracef("deregister (fd=%d)", channel.handle);
80 
81         // IocpContext _data;
82         // _data.channel = channel;
83         // _data.operation = IocpOperation.close;
84         // PostQueuedCompletionStatus(_iocpHandle, 0, 0, &_data.overlapped);
85         //(cast(AbstractStream)channel).stopAction();
86         //WaitForSingleObject
87         return true;
88     }
89 
90     // void weakUp() {
91     //     IocpContext _data;
92     //     // _data.channel = _event;
93     //     _data.operation = IocpOperation.event;
94 
95     //     // PostQueuedCompletionStatus(_iocpHandle, 0, 0, &_data.overlapped);
96     //     PostQueuedCompletionStatus(_iocpHandle, 0, 0, null);
97     // }
98 
99     void putTast(AbstractStream stream)
100     {
101         _queue.insertBack(stream);
102     }
103 
104     override void onLoop(long timeout = -1) {
105         _timer.init();
106         super.onLoop(timeout);
107     }
108 
109     protected override int doSelect(long t) {
110         //if(!_queue.empty)
111         //{
112         //    auto task = _queue.front();
113         //    if (task !is null)
114         //    {
115         //        task.beginRead();
116         //    }
117         //    _queue.removeFront();
118         //}
119         //trace("ssssss");
120         auto timeout = _timer.doWheel();
121         OVERLAPPED* overlapped;
122         ULONG_PTR key = 0;
123         DWORD bytes = 0;
124 
125         // const int ret = GetQueuedCompletionStatus(_iocpHandle, &bytes,
126         //         &key, &overlapped, INFINITE);
127         // tracef("GetQueuedCompletionStatus, ret=%d", ret);
128 
129         // trace("timeout=", timeout);
130         const int ret = GetQueuedCompletionStatus(_iocpHandle, &bytes, &key,
131                 &overlapped, INFINITE);
132 
133         IocpContext* ev = cast(IocpContext*) overlapped;
134         if (ret == 0) {
135             if (overlapped is null)
136             {
137                 return ret;
138             }
139             if (key == 0)
140             {
141                 return ret;
142             }
143             const auto erro = GetLastError();
144             //// About ERROR_OPERATION_ABORTED
145             //// https://stackoverflow.com/questions/7228703/the-i-o-operation-has-been-aborted-because-of-either-a-thread-exit-or-an-applica
146             if (erro == WAIT_TIMEOUT || erro == ERROR_OPERATION_ABORTED) //
147                 return ret;
148 
149             debug warningf("error occurred, code=%d, message: %s", erro, getErrorMessage(erro));
150             assert(ev !is null);
151             AbstractChannel channel = ev.channel;
152             if (channel !is null && !channel.isClosed())
153             {
154                 channel.close();
155             }
156 
157         }
158         //else if (ev is null || ev.channel is null)
159         //    warning("ev is null or ev.watche is null");
160         else {
161             handleChannelEvent(ev.operation, ev.channel, bytes);
162         }
163 
164         return ret;
165     }
166 
167     private void handleChannelEvent(IocpOperation op, AbstractChannel channel, DWORD bytes) {
168 
169         version (HUNT_IO_DEBUG)
170             infof("ev.operation: %s, fd=%d", op, channel.handle);
171         try {
172             switch (op) {
173                 case IocpOperation.accept:
174                // channel.onRead();
175                 warningf("accept ............................");
176                 break;
177                 case IocpOperation.connect:
178                 onSocketRead(channel, 0);
179                 warningf("connect ............................");
180                 break;
181                 case IocpOperation.read:
182                 onSocketRead(channel, bytes);
183                 warningf("read ............................");
184                 break;
185                 case IocpOperation.write:
186                 onSocketWrite(channel, bytes);
187                 warningf("write ............................");
188                 break;
189                 case IocpOperation.event:
190                 warningf("event ............................");
191                 channel.onRead();
192                 break;
193                 case IocpOperation.close:
194                 warningf("close -------------------------: %d", channel.handle);
195                 break;
196                 default:
197                 warning("unsupported operation type -------------------------: ", op);
198                 break;
199             }
200         } catch (Exception e)
201         {
202 
203         }
204 
205     }
206 
207     override void stop() {
208         super.stop();
209         // weakUp();
210         PostQueuedCompletionStatus(_iocpHandle, 0, 0, null);
211     }
212 
213     void handleTimer() {
214 
215     }
216 
217     // override void dispose() {
218 
219     // }
220 
221     private void onSocketRead(AbstractChannel channel, size_t len) {
222         debug if (channel is null) {
223             warning("channel is null");
224             return;
225         }
226 
227         if (channel is null)
228         {
229             return;
230         }
231 
232         (cast(AbstractStream)channel).setBusyWrite(false);
233 
234         if (len == 0 || channel.isClosed) {
235             //version (HUNT_DEBUG)
236             //    infof("channel [fd=%d] closed %d  %d", channel.handle, channel.isClosed,len);
237             //(cast(AbstractStream)channel).setFristRead(false);
238             //(cast(AbstractStream)channel).setBusyWrite(true);
239             //channel.close();
240             return;
241         }
242 
243         AbstractSocketChannel socketChannel = cast(AbstractSocketChannel) channel;
244         // assert(socketChannel !is null, "The type of channel is: " ~ typeid(channel).name);
245         if (socketChannel is null) {
246             warning("The channel socket is null: ");
247         } else {
248             socketChannel.setRead(len);
249             if (!(cast(AbstractStream)channel).getFristRead)
250             {
251                 (cast(AbstractStream)channel).setFristRead(true);
252             }
253             channel.onRead();
254         }
255     }
256 
257     private void onSocketWrite(AbstractChannel channel, size_t len) {
258         debug if (channel is null) {
259             warning("channel is null");
260             return;
261         }
262         AbstractStream client = cast(AbstractStream) channel;
263         // assert(client !is null, "The type of channel is: " ~ typeid(channel).name);
264         if (client is null) {
265             warning("The channel socket is null: ");
266             return;
267         }
268         warning("len ------------------------ %d",len);
269         client.onWriteDone(len); // Notify the client about how many bytes actually sent.
270     }
271 
272 private:
273     HANDLE _iocpHandle;
274     CustomTimer _timer;
275     DList!AbstractStream _queue;
276 }