BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file BReactor_badvpn.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29  
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stddef.h>
34  
35 #ifdef BADVPN_USE_WINAPI
36 #include <windows.h>
37 #else
38 #include <limits.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <unistd.h>
42 #endif
43  
44 #include <misc/debug.h>
45 #include <misc/offset.h>
46 #include <misc/balloc.h>
47 #include <misc/compare.h>
48 #include <base/BLog.h>
49  
50 #include <system/BReactor.h>
51  
52 #include <generated/blog_channel_BReactor.h>
53  
54 #define KEVENT_TAG_FD 1
55 #define KEVENT_TAG_KEVENT 2
56  
57 #define TIMER_STATE_INACTIVE 1
58 #define TIMER_STATE_RUNNING 2
59 #define TIMER_STATE_EXPIRED 3
60  
61 static int compare_timers (BSmallTimer *t1, BSmallTimer *t2)
62 {
63 int cmp = B_COMPARE(t1->absTime, t2->absTime);
64 if (cmp) {
65 return cmp;
66 }
67  
68 return B_COMPARE((uintptr_t)t1, (uintptr_t)t2);
69 }
70  
71 #include "BReactor_badvpn_timerstree.h"
72 #include <structure/CAvl_impl.h>
73  
74 static void assert_timer (BSmallTimer *bt)
75 {
76 ASSERT(bt->state == TIMER_STATE_INACTIVE || bt->state == TIMER_STATE_RUNNING ||
77 bt->state == TIMER_STATE_EXPIRED)
78 }
79  
80 static int move_expired_timers (BReactor *bsys, btime_t now)
81 {
82 int moved = 0;
83  
84 // move timed out timers to the expired list
85 BReactor__TimersTreeRef ref;
86 BSmallTimer *timer;
87 while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) {
88 ASSERT(timer->state == TIMER_STATE_RUNNING)
89  
90 // if it's in the future, stop
91 if (timer->absTime > now) {
92 break;
93 }
94 moved = 1;
95  
96 // remove from running timers tree
97 BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref);
98  
99 // add to expired timers list
100 LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node);
101  
102 // set expired
103 timer->state = TIMER_STATE_EXPIRED;
104 }
105  
106 return moved;
107 }
108  
109 static void move_first_timers (BReactor *bsys)
110 {
111 BReactor__TimersTreeRef ref;
112  
113 // get the time of the first timer
114 BSmallTimer *first_timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link;
115 ASSERT(first_timer)
116 ASSERT(first_timer->state == TIMER_STATE_RUNNING)
117 btime_t first_time = first_timer->absTime;
118  
119 // remove from running timers tree
120 BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref);
121  
122 // add to expired timers list
123 LinkedList1_Append(&bsys->timers_expired_list, &first_timer->u.list_node);
124  
125 // set expired
126 first_timer->state = TIMER_STATE_EXPIRED;
127  
128 // also move other timers with the same timeout
129 BSmallTimer *timer;
130 while (timer = (ref = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0)).link) {
131 ASSERT(timer->state == TIMER_STATE_RUNNING)
132 ASSERT(timer->absTime >= first_time)
133  
134 // if it's in the future, stop
135 if (timer->absTime > first_time) {
136 break;
137 }
138  
139 // remove from running timers tree
140 BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref);
141  
142 // add to expired timers list
143 LinkedList1_Append(&bsys->timers_expired_list, &timer->u.list_node);
144  
145 // set expired
146 timer->state = TIMER_STATE_EXPIRED;
147 }
148 }
149  
150 #ifdef BADVPN_USE_WINAPI
151  
152 static void set_iocp_ready (BReactorIOCPOverlapped *olap, int succeeded, DWORD bytes)
153 {
154 BReactor *reactor = olap->reactor;
155 ASSERT(!olap->is_ready)
156  
157 // set parameters
158 olap->ready_succeeded = succeeded;
159 olap->ready_bytes = bytes;
160  
161 // insert to IOCP ready list
162 LinkedList1_Append(&reactor->iocp_ready_list, &olap->ready_list_node);
163  
164 // set ready
165 olap->is_ready = 1;
166 }
167  
168 #endif
169  
170 #ifdef BADVPN_USE_EPOLL
171  
172 static void set_epoll_fd_pointers (BReactor *bsys)
173 {
174 // Write pointers to our entry pointers into file descriptors.
175 // If a handler function frees some other file descriptor, the
176 // free routine will set our pointer to NULL so we don't dispatch it.
177 for (int i = 0; i < bsys->epoll_results_num; i++) {
178 struct epoll_event *event = &bsys->epoll_results[i];
179 ASSERT(event->data.ptr)
180 BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr;
181 ASSERT(bfd->active)
182 ASSERT(!bfd->epoll_returned_ptr)
183 bfd->epoll_returned_ptr = (BFileDescriptor **)&event->data.ptr;
184 }
185 }
186  
187 #endif
188  
189 #ifdef BADVPN_USE_KEVENT
190  
191 static void set_kevent_fd_pointers (BReactor *bsys)
192 {
193 for (int i = 0; i < bsys->kevent_results_num; i++) {
194 struct kevent *event = &bsys->kevent_results[i];
195 ASSERT(event->udata)
196  
197 int *tag = event->udata;
198 switch (*tag) {
199 case KEVENT_TAG_FD: {
200 BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag);
201 ASSERT(bfd->active)
202 bsys->kevent_prev_event[i] = bfd->kevent_last_event;
203 bfd->kevent_last_event = i;
204 } break;
205  
206 case KEVENT_TAG_KEVENT: {
207 BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag);
208 ASSERT(kev->reactor == bsys)
209 bsys->kevent_prev_event[i] = kev->kevent_last_event;
210 kev->kevent_last_event = i;
211 } break;
212  
213 default:
214 ASSERT(0);
215 }
216 }
217 }
218  
219 static void update_kevent_fd_events (BReactor *bsys, BFileDescriptor *bs, int events)
220 {
221 struct kevent event;
222  
223 if (!(bs->waitEvents & BREACTOR_READ) && (events & BREACTOR_READ)) {
224 memset(&event, 0, sizeof(event));
225 event.ident = bs->fd;
226 event.filter = EVFILT_READ;
227 event.flags = EV_ADD;
228 event.udata = &bs->kevent_tag;
229 ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
230 }
231 else if ((bs->waitEvents & BREACTOR_READ) && !(events & BREACTOR_READ)) {
232 memset(&event, 0, sizeof(event));
233 event.ident = bs->fd;
234 event.filter = EVFILT_READ;
235 event.flags = EV_DELETE;
236 ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
237 }
238  
239 if (!(bs->waitEvents & BREACTOR_WRITE) && (events & BREACTOR_WRITE)) {
240 memset(&event, 0, sizeof(event));
241 event.ident = bs->fd;
242 event.filter = EVFILT_WRITE;
243 event.flags = EV_ADD;
244 event.udata = &bs->kevent_tag;
245 ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
246 }
247 else if ((bs->waitEvents & BREACTOR_WRITE) && !(events & BREACTOR_WRITE)) {
248 memset(&event, 0, sizeof(event));
249 event.ident = bs->fd;
250 event.filter = EVFILT_WRITE;
251 event.flags = EV_DELETE;
252 ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
253 }
254 }
255  
256 #endif
257  
258 #ifdef BADVPN_USE_POLL
259  
260 static void set_poll_fd_pointers (BReactor *bsys)
261 {
262 for (int i = 0; i < bsys->poll_results_num; i++) {
263 BFileDescriptor *bfd = bsys->poll_results_bfds[i];
264 ASSERT(bfd)
265 ASSERT(bfd->active)
266 ASSERT(bfd->poll_returned_index == -1)
267 bfd->poll_returned_index = i;
268 }
269 }
270  
271 #endif
272  
273 static void wait_for_events (BReactor *bsys)
274 {
275 // must have processed all pending events
276 ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
277 ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
278 #ifdef BADVPN_USE_WINAPI
279 ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list))
280 #endif
281 #ifdef BADVPN_USE_EPOLL
282 ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num)
283 #endif
284 #ifdef BADVPN_USE_KEVENT
285 ASSERT(bsys->kevent_results_pos == bsys->kevent_results_num)
286 #endif
287 #ifdef BADVPN_USE_POLL
288 ASSERT(bsys->poll_results_pos == bsys->poll_results_num)
289 #endif
290  
291 // clean up epoll results
292 #ifdef BADVPN_USE_EPOLL
293 bsys->epoll_results_num = 0;
294 bsys->epoll_results_pos = 0;
295 #endif
296  
297 // clean up kevent results
298 #ifdef BADVPN_USE_KEVENT
299 bsys->kevent_results_num = 0;
300 bsys->kevent_results_pos = 0;
301 #endif
302  
303 // clean up poll results
304 #ifdef BADVPN_USE_POLL
305 bsys->poll_results_num = 0;
306 bsys->poll_results_pos = 0;
307 #endif
308  
309 // timeout vars
310 int have_timeout = 0;
311 btime_t timeout_abs;
312 btime_t now = 0; // to remove warning
313  
314 // compute timeout
315 BSmallTimer *first_timer = BReactor__TimersTree_GetFirst(&bsys->timers_tree, 0).link;
316 if (first_timer) {
317 ASSERT(first_timer->state == TIMER_STATE_RUNNING)
318  
319 // get current time
320 now = btime_gettime();
321  
322 // if some timers have already timed out, return them immediately
323 if (move_expired_timers(bsys, now)) {
324 BLog(BLOG_DEBUG, "Got already expired timers");
325 return;
326 }
327  
328 // timeout is first timer, remember absolute time
329 have_timeout = 1;
330 timeout_abs = first_timer->absTime;
331 }
332  
333 // wait until the timeout is reached or the file descriptor / handle in ready
334 while (1) {
335 // compute timeout
336 btime_t timeout_rel = 0; // to remove warning
337 btime_t timeout_rel_trunc = 0; // to remove warning
338 if (have_timeout) {
339 timeout_rel = timeout_abs - now;
340 timeout_rel_trunc = timeout_rel;
341 }
342  
343 // perform wait
344  
345 #ifdef BADVPN_USE_WINAPI
346  
347 if (have_timeout) {
348 if (timeout_rel_trunc > INFINITE - 1) {
349 timeout_rel_trunc = INFINITE - 1;
350 }
351 }
352  
353 DWORD bytes = 0;
354 ULONG_PTR key;
355 BReactorIOCPOverlapped *olap = NULL;
356 BOOL res = GetQueuedCompletionStatus(bsys->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, (have_timeout ? timeout_rel_trunc : INFINITE));
357  
358 ASSERT_FORCE(olap || have_timeout)
359  
360 if (olap || timeout_rel_trunc == timeout_rel) {
361 if (olap) {
362 BLog(BLOG_DEBUG, "GetQueuedCompletionStatus returned event");
363  
364 DebugObject_Access(&olap->d_obj);
365 ASSERT(olap->reactor == bsys)
366 ASSERT(!olap->is_ready)
367  
368 set_iocp_ready(olap, (res == TRUE), bytes);
369 } else {
370 BLog(BLOG_DEBUG, "GetQueuedCompletionStatus timed out");
371 move_first_timers(bsys);
372 }
373 break;
374 }
375  
376 #endif
377  
378 #ifdef BADVPN_USE_EPOLL
379  
380 if (have_timeout) {
381 if (timeout_rel_trunc > INT_MAX) {
382 timeout_rel_trunc = INT_MAX;
383 }
384 }
385  
386 BLog(BLOG_DEBUG, "Calling epoll_wait");
387  
388 int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_rel_trunc : -1));
389 if (waitres < 0) {
390 int error = errno;
391 if (error == EINTR) {
392 BLog(BLOG_DEBUG, "epoll_wait interrupted");
393 goto try_again;
394 }
395 perror("epoll_wait");
396 ASSERT_FORCE(0)
397 }
398  
399 ASSERT_FORCE(!(waitres == 0) || have_timeout)
400 ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS)
401  
402 if (waitres != 0 || timeout_rel_trunc == timeout_rel) {
403 if (waitres != 0) {
404 BLog(BLOG_DEBUG, "epoll_wait returned %d file descriptors", waitres);
405 bsys->epoll_results_num = waitres;
406 set_epoll_fd_pointers(bsys);
407 } else {
408 BLog(BLOG_DEBUG, "epoll_wait timed out");
409 move_first_timers(bsys);
410 }
411 break;
412 }
413  
414 #endif
415  
416 #ifdef BADVPN_USE_KEVENT
417  
418 struct timespec ts;
419 if (have_timeout) {
420 if (timeout_rel_trunc > 86400000) {
421 timeout_rel_trunc = 86400000;
422 }
423 ts.tv_sec = timeout_rel_trunc / 1000;
424 ts.tv_nsec = (timeout_rel_trunc % 1000) * 1000000;
425 }
426  
427 BLog(BLOG_DEBUG, "Calling kevent");
428  
429 int waitres = kevent(bsys->kqueue_fd, NULL, 0, bsys->kevent_results, BSYSTEM_MAX_RESULTS, (have_timeout ? &ts : NULL));
430 if (waitres < 0) {
431 int error = errno;
432 if (error == EINTR) {
433 BLog(BLOG_DEBUG, "kevent interrupted");
434 goto try_again;
435 }
436 perror("kevent");
437 ASSERT_FORCE(0)
438 }
439  
440 ASSERT_FORCE(!(waitres == 0) || have_timeout)
441 ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS)
442  
443 if (waitres != 0 || timeout_rel_trunc == timeout_rel) {
444 if (waitres != 0) {
445 BLog(BLOG_DEBUG, "kevent returned %d events", waitres);
446 bsys->kevent_results_num = waitres;
447 set_kevent_fd_pointers(bsys);
448 } else {
449 BLog(BLOG_DEBUG, "kevent timed out");
450 move_first_timers(bsys);
451 }
452 break;
453 }
454  
455 #endif
456  
457 #ifdef BADVPN_USE_POLL
458  
459 if (have_timeout) {
460 if (timeout_rel_trunc > INT_MAX) {
461 timeout_rel_trunc = INT_MAX;
462 }
463 }
464  
465 ASSERT(bsys->poll_num_enabled_fds >= 0)
466 ASSERT(bsys->poll_num_enabled_fds <= BSYSTEM_MAX_POLL_FDS)
467 int num_fds = 0;
468  
469 LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->poll_enabled_fds_list);
470 while (list_node) {
471 BFileDescriptor *bfd = UPPER_OBJECT(list_node, BFileDescriptor, poll_enabled_fds_list_node);
472 ASSERT(bfd->active)
473 ASSERT(bfd->poll_returned_index == -1)
474  
475 // calculate poll events
476 int pevents = 0;
477 if ((bfd->waitEvents & BREACTOR_READ)) {
478 pevents |= POLLIN;
479 }
480 if ((bfd->waitEvents & BREACTOR_WRITE)) {
481 pevents |= POLLOUT;
482 }
483  
484 // write pollfd entry
485 struct pollfd *pfd = &bsys->poll_results_pollfds[num_fds];
486 pfd->fd = bfd->fd;
487 pfd->events = pevents;
488 pfd->revents = 0;
489  
490 // write BFileDescriptor reference entry
491 bsys->poll_results_bfds[num_fds] = bfd;
492  
493 // increment number of fds in array
494 num_fds++;
495  
496 list_node = LinkedList1Node_Next(list_node);
497 }
498  
499 BLog(BLOG_DEBUG, "Calling poll");
500  
501 int waitres = poll(bsys->poll_results_pollfds, num_fds, (have_timeout ? timeout_rel_trunc : -1));
502 if (waitres < 0) {
503 int error = errno;
504 if (error == EINTR) {
505 BLog(BLOG_DEBUG, "poll interrupted");
506 goto try_again;
507 }
508 perror("poll");
509 ASSERT_FORCE(0)
510 }
511  
512 ASSERT_FORCE(!(waitres == 0) || have_timeout)
513  
514 if (waitres != 0 || timeout_rel_trunc == timeout_rel) {
515 if (waitres != 0) {
516 BLog(BLOG_DEBUG, "poll returned %d file descriptors", waitres);
517 bsys->poll_results_num = num_fds;
518 bsys->poll_results_pos = 0;
519 set_poll_fd_pointers(bsys);
520 } else {
521 BLog(BLOG_DEBUG, "poll timed out");
522 move_first_timers(bsys);
523 }
524 break;
525 }
526  
527 #endif
528  
529 try_again:
530 if (have_timeout) {
531 // get current time
532 now = btime_gettime();
533 // check if we already reached the time we're waiting for
534 if (now >= timeout_abs) {
535 BLog(BLOG_DEBUG, "already timed out while trying again");
536 move_first_timers(bsys);
537 break;
538 }
539 }
540 }
541  
542 // reset limit objects
543 LinkedList1Node *list_node;
544 while (list_node = LinkedList1_GetFirst(&bsys->active_limits_list)) {
545 BReactorLimit *limit = UPPER_OBJECT(list_node, BReactorLimit, active_limits_list_node);
546 ASSERT(limit->count > 0)
547 limit->count = 0;
548 LinkedList1_Remove(&bsys->active_limits_list, &limit->active_limits_list_node);
549 }
550 }
551  
552 #ifndef BADVPN_USE_WINAPI
553  
554 void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler handler, void *user)
555 {
556 bs->fd = fd;
557 bs->handler = handler;
558 bs->user = user;
559 bs->active = 0;
560 }
561  
562 #endif
563  
564 void BSmallTimer_Init (BSmallTimer *bt, BSmallTimer_handler handler)
565 {
566 bt->handler.smalll = handler;
567 bt->state = TIMER_STATE_INACTIVE;
568 bt->is_small = 1;
569 }
570  
571 int BSmallTimer_IsRunning (BSmallTimer *bt)
572 {
573 assert_timer(bt);
574  
575 return (bt->state != TIMER_STATE_INACTIVE);
576 }
577  
578 void BTimer_Init (BTimer *bt, btime_t msTime, BTimer_handler handler, void *user)
579 {
580 bt->base.handler.heavy = handler;
581 bt->base.state = TIMER_STATE_INACTIVE;
582 bt->base.is_small = 0;
583 bt->user = user;
584 bt->msTime = msTime;
585 }
586  
587 int BTimer_IsRunning (BTimer *bt)
588 {
589 return BSmallTimer_IsRunning(&bt->base);
590 }
591  
592 int BReactor_Init (BReactor *bsys)
593 {
594 BLog(BLOG_DEBUG, "Reactor initializing");
595  
596 // set not exiting
597 bsys->exiting = 0;
598  
599 // init jobs
600 BPendingGroup_Init(&bsys->pending_jobs);
601  
602 // init timers
603 BReactor__TimersTree_Init(&bsys->timers_tree);
604 LinkedList1_Init(&bsys->timers_expired_list);
605  
606 // init limits
607 LinkedList1_Init(&bsys->active_limits_list);
608  
609 #ifdef BADVPN_USE_WINAPI
610  
611 // init IOCP list
612 LinkedList1_Init(&bsys->iocp_list);
613  
614 // init IOCP handle
615 if (!(bsys->iocp_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1))) {
616 BLog(BLOG_ERROR, "CreateIoCompletionPort failed");
617 goto fail0;
618 }
619  
620 // init IOCP ready list
621 LinkedList1_Init(&bsys->iocp_ready_list);
622  
623 #endif
624  
625 #ifdef BADVPN_USE_EPOLL
626  
627 // create epoll fd
628 if ((bsys->efd = epoll_create(10)) < 0) {
629 BLog(BLOG_ERROR, "epoll_create failed");
630 goto fail0;
631 }
632  
633 // init results array
634 bsys->epoll_results_num = 0;
635 bsys->epoll_results_pos = 0;
636  
637 #endif
638  
639 #ifdef BADVPN_USE_KEVENT
640  
641 // create kqueue fd
642 if ((bsys->kqueue_fd = kqueue()) < 0) {
643 BLog(BLOG_ERROR, "kqueue failed");
644 goto fail0;
645 }
646  
647 // init results array
648 bsys->kevent_results_num = 0;
649 bsys->kevent_results_pos = 0;
650  
651 #endif
652  
653 #ifdef BADVPN_USE_POLL
654  
655 // init enabled fds list
656 LinkedList1_Init(&bsys->poll_enabled_fds_list);
657  
658 // set zero enabled fds
659 bsys->poll_num_enabled_fds = 0;
660  
661 // allocate results arrays
662 if (!(bsys->poll_results_pollfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_pollfds[0])))) {
663 BLog(BLOG_ERROR, "BAllocArray failed");
664 goto fail0;
665 }
666 if (!(bsys->poll_results_bfds = BAllocArray(BSYSTEM_MAX_POLL_FDS, sizeof(bsys->poll_results_bfds[0])))) {
667 BLog(BLOG_ERROR, "BAllocArray failed");
668 goto fail1;
669 }
670  
671 // init results array
672 bsys->poll_results_num = 0;
673 bsys->poll_results_pos = 0;
674  
675 #endif
676  
677 DebugObject_Init(&bsys->d_obj);
678 #ifndef BADVPN_USE_WINAPI
679 DebugCounter_Init(&bsys->d_fds_counter);
680 #endif
681 #ifdef BADVPN_USE_KEVENT
682 DebugCounter_Init(&bsys->d_kevent_ctr);
683 #endif
684 DebugCounter_Init(&bsys->d_limits_ctr);
685  
686 return 1;
687  
688 #ifdef BADVPN_USE_POLL
689 fail1:
690 BFree(bsys->poll_results_pollfds);
691 #endif
692 fail0:
693 BPendingGroup_Free(&bsys->pending_jobs);
694 BLog(BLOG_ERROR, "Reactor failed to initialize");
695 return 0;
696 }
697  
698 void BReactor_Free (BReactor *bsys)
699 {
700 DebugObject_Access(&bsys->d_obj);
701  
702 #ifdef BADVPN_USE_WINAPI
703 while (!LinkedList1_IsEmpty(&bsys->iocp_list)) {
704 BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetLast(&bsys->iocp_list), BReactorIOCPOverlapped, iocp_list_node);
705 ASSERT(olap->reactor == bsys)
706 olap->handler(olap->user, BREACTOR_IOCP_EVENT_EXITING, 0);
707 }
708 #endif
709  
710 // {pending group has no BPending objects}
711 ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
712 ASSERT(BReactor__TimersTree_IsEmpty(&bsys->timers_tree))
713 ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
714 ASSERT(LinkedList1_IsEmpty(&bsys->active_limits_list))
715 DebugObject_Free(&bsys->d_obj);
716 #ifdef BADVPN_USE_WINAPI
717 ASSERT(LinkedList1_IsEmpty(&bsys->iocp_ready_list))
718 ASSERT(LinkedList1_IsEmpty(&bsys->iocp_list))
719 #endif
720 #ifndef BADVPN_USE_WINAPI
721 DebugCounter_Free(&bsys->d_fds_counter);
722 #endif
723 #ifdef BADVPN_USE_KEVENT
724 DebugCounter_Free(&bsys->d_kevent_ctr);
725 #endif
726 DebugCounter_Free(&bsys->d_limits_ctr);
727 #ifdef BADVPN_USE_POLL
728 ASSERT(bsys->poll_num_enabled_fds == 0)
729 ASSERT(LinkedList1_IsEmpty(&bsys->poll_enabled_fds_list))
730 #endif
731  
732 BLog(BLOG_DEBUG, "Reactor freeing");
733  
734 #ifdef BADVPN_USE_WINAPI
735  
736 // close IOCP handle
737 ASSERT_FORCE(CloseHandle(bsys->iocp_handle))
738  
739 #endif
740  
741 #ifdef BADVPN_USE_EPOLL
742  
743 // close epoll fd
744 ASSERT_FORCE(close(bsys->efd) == 0)
745  
746 #endif
747  
748 #ifdef BADVPN_USE_KEVENT
749  
750 // close kqueue fd
751 ASSERT_FORCE(close(bsys->kqueue_fd) == 0)
752  
753 #endif
754  
755 #ifdef BADVPN_USE_POLL
756  
757 // free results arrays
758 BFree(bsys->poll_results_bfds);
759 BFree(bsys->poll_results_pollfds);
760  
761 #endif
762  
763 // free jobs
764 BPendingGroup_Free(&bsys->pending_jobs);
765 }
766  
767 int BReactor_Exec (BReactor *bsys)
768 {
769 BLog(BLOG_DEBUG, "Entering event loop");
770  
771 while (!bsys->exiting) {
772 // dispatch job
773 if (BPendingGroup_HasJobs(&bsys->pending_jobs)) {
774 BPendingGroup_ExecuteJob(&bsys->pending_jobs);
775 continue;
776 }
777  
778 // dispatch timer
779 LinkedList1Node *list_node = LinkedList1_GetFirst(&bsys->timers_expired_list);
780 if (list_node) {
781 BSmallTimer *timer = UPPER_OBJECT(list_node, BSmallTimer, u.list_node);
782 ASSERT(timer->state == TIMER_STATE_EXPIRED)
783  
784 // remove from expired list
785 LinkedList1_Remove(&bsys->timers_expired_list, &timer->u.list_node);
786  
787 // set inactive
788 timer->state = TIMER_STATE_INACTIVE;
789  
790 // call handler
791 BLog(BLOG_DEBUG, "Dispatching timer");
792 if (timer->is_small) {
793 timer->handler.smalll(timer);
794 } else {
795 BTimer *btimer = UPPER_OBJECT(timer, BTimer, base);
796 timer->handler.heavy(btimer->user);
797 }
798 continue;
799 }
800  
801 #ifdef BADVPN_USE_WINAPI
802  
803 if (!LinkedList1_IsEmpty(&bsys->iocp_ready_list)) {
804 BReactorIOCPOverlapped *olap = UPPER_OBJECT(LinkedList1_GetFirst(&bsys->iocp_ready_list), BReactorIOCPOverlapped, ready_list_node);
805 ASSERT(olap->is_ready)
806 ASSERT(olap->handler)
807  
808 // remove from ready list
809 LinkedList1_Remove(&bsys->iocp_ready_list, &olap->ready_list_node);
810  
811 // set not ready
812 olap->is_ready = 0;
813  
814 int event = (olap->ready_succeeded ? BREACTOR_IOCP_EVENT_SUCCEEDED : BREACTOR_IOCP_EVENT_FAILED);
815  
816 // call handler
817 olap->handler(olap->user, event, olap->ready_bytes);
818 continue;
819 }
820  
821 #endif
822  
823 #ifdef BADVPN_USE_EPOLL
824  
825 // dispatch file descriptor
826 if (bsys->epoll_results_pos < bsys->epoll_results_num) {
827 // grab event
828 struct epoll_event *event = &bsys->epoll_results[bsys->epoll_results_pos];
829 bsys->epoll_results_pos++;
830  
831 // check if the BFileDescriptor was removed
832 if (!event->data.ptr) {
833 continue;
834 }
835  
836 // get BFileDescriptor
837 BFileDescriptor *bfd = (BFileDescriptor *)event->data.ptr;
838 ASSERT(bfd->active)
839 ASSERT(bfd->epoll_returned_ptr == (BFileDescriptor **)&event->data.ptr)
840  
841 // zero pointer to the epoll entry
842 bfd->epoll_returned_ptr = NULL;
843  
844 // calculate events to report
845 int events = 0;
846 if ((bfd->waitEvents&BREACTOR_READ) && (event->events&EPOLLIN)) {
847 events |= BREACTOR_READ;
848 }
849 if ((bfd->waitEvents&BREACTOR_WRITE) && (event->events&EPOLLOUT)) {
850 events |= BREACTOR_WRITE;
851 }
852 if ((event->events&EPOLLERR)) {
853 events |= BREACTOR_ERROR;
854 }
855 if ((event->events&EPOLLHUP)) {
856 events |= BREACTOR_HUP;
857 }
858  
859 if (!events) {
860 BLog(BLOG_ERROR, "no events detected?");
861 continue;
862 }
863  
864 // call handler
865 BLog(BLOG_DEBUG, "Dispatching file descriptor");
866 bfd->handler(bfd->user, events);
867 continue;
868 }
869  
870 #endif
871  
872 #ifdef BADVPN_USE_KEVENT
873  
874 // dispatch kevent
875 if (bsys->kevent_results_pos < bsys->kevent_results_num) {
876 // grab event
877 int event_index = bsys->kevent_results_pos;
878 struct kevent *event = &bsys->kevent_results[event_index];
879 bsys->kevent_results_pos++;
880  
881 // check if the event was removed
882 if (!event->udata) {
883 continue;
884 }
885  
886 // check tag
887 int *tag = event->udata;
888 switch (*tag) {
889 case KEVENT_TAG_FD: {
890 // get BFileDescriptor
891 BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag);
892 ASSERT(bfd->active)
893  
894 // when we get to the last event for this fd, reset kevent_last_event
895 if (event_index == bfd->kevent_last_event) {
896 bfd->kevent_last_event = -1;
897 }
898  
899 // calculate event to report
900 int events = 0;
901 if ((bfd->waitEvents&BREACTOR_READ) && event->filter == EVFILT_READ) {
902 events |= BREACTOR_READ;
903 }
904 if ((bfd->waitEvents&BREACTOR_WRITE) && event->filter == EVFILT_WRITE) {
905 events |= BREACTOR_WRITE;
906 }
907  
908 if (!events) {
909 BLog(BLOG_ERROR, "no events detected?");
910 continue;
911 }
912  
913 // call handler
914 BLog(BLOG_DEBUG, "Dispatching file descriptor");
915 bfd->handler(bfd->user, events);
916 continue;
917 } break;
918  
919 case KEVENT_TAG_KEVENT: {
920 // get BReactorKEvent
921 BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag);
922 ASSERT(kev->reactor == bsys)
923  
924 // when we get to the last event for this fd, reset kevent_last_event
925 if (event_index == kev->kevent_last_event) {
926 kev->kevent_last_event = -1;
927 }
928  
929 // call handler
930 BLog(BLOG_DEBUG, "Dispatching kevent");
931 kev->handler(kev->user, event->fflags, event->data);
932 continue;
933 } break;
934  
935 default:
936 ASSERT(0);
937 }
938 }
939  
940 #endif
941  
942 #ifdef BADVPN_USE_POLL
943  
944 if (bsys->poll_results_pos < bsys->poll_results_num) {
945 // grab event
946 struct pollfd *pfd = &bsys->poll_results_pollfds[bsys->poll_results_pos];
947 BFileDescriptor *bfd = bsys->poll_results_bfds[bsys->poll_results_pos];
948 bsys->poll_results_pos++;
949  
950 // skip removed entry
951 if (!bfd) {
952 continue;
953 }
954  
955 ASSERT(bfd->active)
956 ASSERT(bfd->poll_returned_index == bsys->poll_results_pos - 1)
957  
958 // remove result reference
959 bfd->poll_returned_index = -1;
960  
961 // calculate events to report
962 int events = 0;
963 if ((bfd->waitEvents & BREACTOR_READ) && (pfd->revents & POLLIN)) {
964 events |= BREACTOR_READ;
965 }
966 if ((bfd->waitEvents & BREACTOR_WRITE) && (pfd->revents & POLLOUT)) {
967 events |= BREACTOR_WRITE;
968 }
969 if ((pfd->revents & POLLERR) || (pfd->revents & POLLHUP)) {
970 events |= BREACTOR_ERROR;
971 }
972  
973 if (!events) {
974 continue;
975 }
976  
977 // call handler
978 BLog(BLOG_DEBUG, "Dispatching file descriptor");
979 bfd->handler(bfd->user, events);
980 continue;
981 }
982  
983 #endif
984  
985 wait_for_events(bsys);
986 }
987  
988 BLog(BLOG_DEBUG, "Exiting event loop, exit code %d", bsys->exit_code);
989  
990 return bsys->exit_code;
991 }
992  
993 void BReactor_Quit (BReactor *bsys, int code)
994 {
995 bsys->exiting = 1;
996 bsys->exit_code = code;
997 }
998  
999 void BReactor_SetSmallTimer (BReactor *bsys, BSmallTimer *bt, int mode, btime_t time)
1000 {
1001 assert_timer(bt);
1002 ASSERT(mode == BTIMER_SET_ABSOLUTE || mode == BTIMER_SET_RELATIVE)
1003  
1004 // unlink it if it's already in the list
1005 BReactor_RemoveSmallTimer(bsys, bt);
1006  
1007 // if mode is relative, add current time
1008 if (mode == BTIMER_SET_RELATIVE) {
1009 time = btime_add(btime_gettime(), time);
1010 }
1011  
1012 // set time
1013 bt->absTime = time;
1014  
1015 // set running
1016 bt->state = TIMER_STATE_RUNNING;
1017  
1018 // insert to running timers tree
1019 BReactor__TimersTreeRef ref = {bt, bt};
1020 int res = BReactor__TimersTree_Insert(&bsys->timers_tree, 0, ref, NULL);
1021 ASSERT_EXECUTE(res)
1022 }
1023  
1024 void BReactor_RemoveSmallTimer (BReactor *bsys, BSmallTimer *bt)
1025 {
1026 assert_timer(bt);
1027  
1028 if (bt->state == TIMER_STATE_INACTIVE) {
1029 return;
1030 }
1031  
1032 if (bt->state == TIMER_STATE_EXPIRED) {
1033 // remove from expired list
1034 LinkedList1_Remove(&bsys->timers_expired_list, &bt->u.list_node);
1035 } else {
1036 // remove from running tree
1037 BReactor__TimersTreeRef ref = {bt, bt};
1038 BReactor__TimersTree_Remove(&bsys->timers_tree, 0, ref);
1039 }
1040  
1041 // set inactive
1042 bt->state = TIMER_STATE_INACTIVE;
1043 }
1044  
1045 void BReactor_SetTimer (BReactor *bsys, BTimer *bt)
1046 {
1047 BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, bt->msTime);
1048 }
1049  
1050 void BReactor_SetTimerAfter (BReactor *bsys, BTimer *bt, btime_t after)
1051 {
1052 BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_RELATIVE, after);
1053 }
1054  
1055 void BReactor_SetTimerAbsolute (BReactor *bsys, BTimer *bt, btime_t time)
1056 {
1057 BReactor_SetSmallTimer(bsys, &bt->base, BTIMER_SET_ABSOLUTE, time);
1058 }
1059  
1060 void BReactor_RemoveTimer (BReactor *bsys, BTimer *bt)
1061 {
1062 return BReactor_RemoveSmallTimer(bsys, &bt->base);
1063 }
1064  
1065 BPendingGroup * BReactor_PendingGroup (BReactor *bsys)
1066 {
1067 return &bsys->pending_jobs;
1068 }
1069  
1070 int BReactor_Synchronize (BReactor *bsys, BSmallPending *ref)
1071 {
1072 ASSERT(ref)
1073  
1074 while (!bsys->exiting) {
1075 ASSERT(BPendingGroup_HasJobs(&bsys->pending_jobs))
1076  
1077 if (BPendingGroup_PeekJob(&bsys->pending_jobs) == ref) {
1078 return 1;
1079 }
1080  
1081 BPendingGroup_ExecuteJob(&bsys->pending_jobs);
1082 }
1083  
1084 return 0;
1085 }
1086  
1087 #ifndef BADVPN_USE_WINAPI
1088  
1089 int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
1090 {
1091 ASSERT(!bs->active)
1092  
1093 #ifdef BADVPN_USE_EPOLL
1094  
1095 // add epoll entry
1096 struct epoll_event event;
1097 memset(&event, 0, sizeof(event));
1098 event.events = 0;
1099 event.data.ptr = bs;
1100 if (epoll_ctl(bsys->efd, EPOLL_CTL_ADD, bs->fd, &event) < 0) {
1101 int error = errno;
1102 BLog(BLOG_ERROR, "epoll_ctl failed: %d", error);
1103 return 0;
1104 }
1105  
1106 // set epoll returned pointer
1107 bs->epoll_returned_ptr = NULL;
1108  
1109 #endif
1110  
1111 #ifdef BADVPN_USE_KEVENT
1112  
1113 // set kevent tag
1114 bs->kevent_tag = KEVENT_TAG_FD;
1115  
1116 // have no events
1117 bs->kevent_last_event = -1;
1118  
1119 #endif
1120  
1121 #ifdef BADVPN_USE_POLL
1122  
1123 if (bsys->poll_num_enabled_fds == BSYSTEM_MAX_POLL_FDS) {
1124 BLog(BLOG_ERROR, "too many fds");
1125 return 0;
1126 }
1127  
1128 // append to enabled fds list
1129 LinkedList1_Append(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node);
1130 bsys->poll_num_enabled_fds++;
1131  
1132 // set not returned
1133 bs->poll_returned_index = -1;
1134  
1135 #endif
1136  
1137 bs->active = 1;
1138 bs->waitEvents = 0;
1139  
1140 DebugCounter_Increment(&bsys->d_fds_counter);
1141 return 1;
1142 }
1143  
1144 void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
1145 {
1146 ASSERT(bs->active)
1147 DebugCounter_Decrement(&bsys->d_fds_counter);
1148  
1149 bs->active = 0;
1150  
1151 #ifdef BADVPN_USE_EPOLL
1152  
1153 // delete epoll entry
1154 struct epoll_event event;
1155 memset(&event, 0, sizeof(event));
1156 ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_DEL, bs->fd, &event) == 0)
1157  
1158 // write through epoll returned pointer
1159 if (bs->epoll_returned_ptr) {
1160 *bs->epoll_returned_ptr = NULL;
1161 }
1162  
1163 #endif
1164  
1165 #ifdef BADVPN_USE_KEVENT
1166  
1167 // delete kevents
1168 update_kevent_fd_events(bsys, bs, 0);
1169  
1170 // invalidate any events
1171 int event_index = bs->kevent_last_event;
1172 while (event_index != -1) {
1173 ASSERT(event_index >= 0 && event_index < bsys->kevent_results_num)
1174 struct kevent *event = &bsys->kevent_results[event_index];
1175 event->udata = NULL;
1176 event_index = bsys->kevent_prev_event[event_index];
1177 }
1178  
1179 #endif
1180  
1181 #ifdef BADVPN_USE_POLL
1182  
1183 // invalidate results entry
1184 if (bs->poll_returned_index != -1) {
1185 ASSERT(bs->poll_returned_index >= bsys->poll_results_pos)
1186 ASSERT(bs->poll_returned_index < bsys->poll_results_num)
1187 ASSERT(bsys->poll_results_bfds[bs->poll_returned_index] == bs)
1188  
1189 bsys->poll_results_bfds[bs->poll_returned_index] = NULL;
1190 }
1191  
1192 // remove from enabled fds list
1193 LinkedList1_Remove(&bsys->poll_enabled_fds_list, &bs->poll_enabled_fds_list_node);
1194 bsys->poll_num_enabled_fds--;
1195  
1196 #endif
1197 }
1198  
1199 void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events)
1200 {
1201 ASSERT(bs->active)
1202 ASSERT(!(events&~(BREACTOR_READ|BREACTOR_WRITE)))
1203  
1204 if (bs->waitEvents == events) {
1205 return;
1206 }
1207  
1208 #ifdef BADVPN_USE_EPOLL
1209  
1210 // calculate epoll events
1211 int eevents = 0;
1212 if ((events & BREACTOR_READ)) {
1213 eevents |= EPOLLIN;
1214 }
1215 if ((events & BREACTOR_WRITE)) {
1216 eevents |= EPOLLOUT;
1217 }
1218  
1219 // update epoll entry
1220 struct epoll_event event;
1221 memset(&event, 0, sizeof(event));
1222 event.events = eevents;
1223 event.data.ptr = bs;
1224 ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_MOD, bs->fd, &event) == 0)
1225  
1226 #endif
1227  
1228 #ifdef BADVPN_USE_KEVENT
1229  
1230 update_kevent_fd_events(bsys, bs, events);
1231  
1232 #endif
1233  
1234 // update events
1235 bs->waitEvents = events;
1236 }
1237  
1238 #endif
1239  
1240 void BReactorLimit_Init (BReactorLimit *o, BReactor *reactor, int limit)
1241 {
1242 DebugObject_Access(&reactor->d_obj);
1243 ASSERT(limit > 0)
1244  
1245 // init arguments
1246 o->reactor = reactor;
1247 o->limit = limit;
1248  
1249 // set count zero
1250 o->count = 0;
1251  
1252 DebugCounter_Increment(&reactor->d_limits_ctr);
1253 DebugObject_Init(&o->d_obj);
1254 }
1255  
1256 void BReactorLimit_Free (BReactorLimit *o)
1257 {
1258 BReactor *reactor = o->reactor;
1259 DebugObject_Free(&o->d_obj);
1260 DebugCounter_Decrement(&reactor->d_limits_ctr);
1261  
1262 // remove from active limits list
1263 if (o->count > 0) {
1264 LinkedList1_Remove(&reactor->active_limits_list, &o->active_limits_list_node);
1265 }
1266 }
1267  
1268 int BReactorLimit_Increment (BReactorLimit *o)
1269 {
1270 BReactor *reactor = o->reactor;
1271 DebugObject_Access(&o->d_obj);
1272  
1273 // check count against limit
1274 if (o->count >= o->limit) {
1275 return 0;
1276 }
1277  
1278 // increment count
1279 o->count++;
1280  
1281 // if limit was zero, add to active limits list
1282 if (o->count == 1) {
1283 LinkedList1_Append(&reactor->active_limits_list, &o->active_limits_list_node);
1284 }
1285  
1286 return 1;
1287 }
1288  
1289 void BReactorLimit_SetLimit (BReactorLimit *o, int limit)
1290 {
1291 DebugObject_Access(&o->d_obj);
1292 ASSERT(limit > 0)
1293  
1294 // set limit
1295 o->limit = limit;
1296 }
1297  
1298 #ifdef BADVPN_USE_KEVENT
1299  
1300 int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data)
1301 {
1302 DebugObject_Access(&reactor->d_obj);
1303  
1304 // init arguments
1305 o->reactor = reactor;
1306 o->handler = handler;
1307 o->user = user;
1308 o->ident = ident;
1309 o->filter = filter;
1310  
1311 // add kevent
1312 struct kevent event;
1313 memset(&event, 0, sizeof(event));
1314 event.ident = o->ident;
1315 event.filter = o->filter;
1316 event.flags = EV_ADD;
1317 event.fflags = fflags;
1318 event.data = data;
1319 event.udata = &o->kevent_tag;
1320 if (kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) < 0) {
1321 return 0;
1322 }
1323  
1324 // set kevent tag
1325 o->kevent_tag = KEVENT_TAG_KEVENT;
1326  
1327 // have no events
1328 o->kevent_last_event = -1;
1329  
1330 DebugObject_Init(&o->d_obj);
1331 DebugCounter_Increment(&o->reactor->d_kevent_ctr);
1332 return 1;
1333 }
1334  
1335 void BReactorKEvent_Free (BReactorKEvent *o)
1336 {
1337 BReactor *reactor = o->reactor;
1338 DebugObject_Free(&o->d_obj);
1339 DebugCounter_Decrement(&reactor->d_kevent_ctr);
1340  
1341 // invalidate any events
1342 int event_index = o->kevent_last_event;
1343 while (event_index != -1) {
1344 ASSERT(event_index >= 0 && event_index < reactor->kevent_results_num)
1345 struct kevent *event = &reactor->kevent_results[event_index];
1346 event->udata = NULL;
1347 event_index = reactor->kevent_prev_event[event_index];
1348 }
1349  
1350 // delete kevent
1351 struct kevent event;
1352 memset(&event, 0, sizeof(event));
1353 event.ident = o->ident;
1354 event.filter = o->filter;
1355 event.flags = EV_DELETE;
1356 ASSERT_FORCE(kevent(reactor->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
1357 }
1358  
1359 #endif
1360  
1361 #ifdef BADVPN_USE_WINAPI
1362  
1363 HANDLE BReactor_GetIOCPHandle (BReactor *reactor)
1364 {
1365 DebugObject_Access(&reactor->d_obj);
1366  
1367 return reactor->iocp_handle;
1368 }
1369  
1370 void BReactorIOCPOverlapped_Init (BReactorIOCPOverlapped *o, BReactor *reactor, void *user, BReactorIOCPOverlapped_handler handler)
1371 {
1372 DebugObject_Access(&reactor->d_obj);
1373  
1374 // init arguments
1375 o->reactor = reactor;
1376 o->user = user;
1377 o->handler = handler;
1378  
1379 // zero overlapped
1380 memset(&o->olap, 0, sizeof(o->olap));
1381  
1382 // append to IOCP list
1383 LinkedList1_Append(&reactor->iocp_list, &o->iocp_list_node);
1384  
1385 // set not ready
1386 o->is_ready = 0;
1387  
1388 DebugObject_Init(&o->d_obj);
1389 }
1390  
1391 void BReactorIOCPOverlapped_Free (BReactorIOCPOverlapped *o)
1392 {
1393 BReactor *reactor = o->reactor;
1394 DebugObject_Free(&o->d_obj);
1395  
1396 // remove from IOCP ready list
1397 if (o->is_ready) {
1398 LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node);
1399 }
1400  
1401 // remove from IOCP list
1402 LinkedList1_Remove(&reactor->iocp_list, &o->iocp_list_node);
1403 }
1404  
1405 void BReactorIOCPOverlapped_Wait (BReactorIOCPOverlapped *o, int *out_succeeded, DWORD *out_bytes)
1406 {
1407 BReactor *reactor = o->reactor;
1408 DebugObject_Access(&o->d_obj);
1409  
1410 // wait for IOCP events until we get an event for this olap
1411 while (!o->is_ready) {
1412 DWORD bytes = 0;
1413 ULONG_PTR key;
1414 BReactorIOCPOverlapped *olap = NULL;
1415 BOOL res = GetQueuedCompletionStatus(reactor->iocp_handle, &bytes, &key, (OVERLAPPED **)&olap, INFINITE);
1416  
1417 ASSERT_FORCE(olap)
1418 DebugObject_Access(&olap->d_obj);
1419 ASSERT(olap->reactor == reactor)
1420  
1421 // regular I/O should be done synchronously, so we shoudln't ever get a second completion before an
1422 // existing one is dispatched. If however PostQueuedCompletionStatus is being used to signal events,
1423 // just discard any excess events.
1424 if (!olap->is_ready) {
1425 set_iocp_ready(olap, (res == TRUE), bytes);
1426 }
1427 }
1428  
1429 // remove from IOCP ready list
1430 LinkedList1_Remove(&reactor->iocp_ready_list, &o->ready_list_node);
1431  
1432 // set not ready
1433 o->is_ready = 0;
1434  
1435 if (out_succeeded) {
1436 *out_succeeded = o->ready_succeeded;
1437 }
1438 if (out_bytes) {
1439 *out_bytes = o->ready_bytes;
1440 }
1441 }
1442  
1443 #endif