Merge pull request #1668 from alexanderkyte/bug1856
[mono.git] / mono / metadata / threadpool-ms-io-kqueue.c
1
2 #if defined(HAVE_KQUEUE)
3
4 #include <sys/types.h>
5 #include <sys/event.h>
6 #include <sys/time.h>
7
8 #if defined(HOST_WIN32)
9 /* We assume that kqueue is not available on windows */
10 #error
11 #endif
12
13 #define KQUEUE_NEVENTS 128
14
15 static gint kqueue_fd;
16 static struct kevent *kqueue_events;
17
18 static gboolean
19 kqueue_init (gint wakeup_pipe_fd)
20 {
21         struct kevent event;
22
23         kqueue_fd = kqueue ();
24         if (kqueue_fd == -1) {
25                 g_warning ("kqueue_init: kqueue () failed, error (%d) %s", errno, g_strerror (errno));
26                 return FALSE;
27         }
28
29         EV_SET (&event, wakeup_pipe_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);
30         if (kevent (kqueue_fd, &event, 1, NULL, 0, NULL) == -1) {
31                 g_warning ("kqueue_init: kevent () failed, error (%d) %s", errno, g_strerror (errno));
32                 close (kqueue_fd);
33                 return FALSE;
34         }
35
36         kqueue_events = g_new0 (struct kevent, KQUEUE_NEVENTS);
37
38         return TRUE;
39 }
40
41 static void
42 kqueue_cleanup (void)
43 {
44         g_free (kqueue_events);
45         close (kqueue_fd);
46 }
47
48 static void
49 kqueue_update_add (ThreadPoolIOUpdate *update)
50 {
51         struct kevent event;
52
53         if ((update->events & MONO_POLLIN) != 0)
54                 EV_SET (&event, update->fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
55         if ((update->events & MONO_POLLOUT) != 0)
56                 EV_SET (&event, update->fd, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
57
58         if (kevent (kqueue_fd, &event, 1, NULL, 0, NULL) == -1)
59                 g_warning ("kqueue_update_add: kevent(update) failed, error (%d) %s", errno, g_strerror (errno));
60 }
61
62 static gint
63 kqueue_event_wait (void)
64 {
65         gint ready;
66
67         ready = kevent (kqueue_fd, NULL, 0, kqueue_events, KQUEUE_NEVENTS, NULL);
68         if (ready == -1) {
69                 switch (errno) {
70                 case EINTR:
71                         check_for_interruption_critical ();
72                         ready = 0;
73                         break;
74                 default:
75                         g_warning ("kqueue_event_wait: kevent () failed, error (%d) %s", errno, g_strerror (errno));
76                         break;
77                 }
78         }
79
80         return ready;
81 }
82
83 static inline gint
84 kqueue_event_fd_at (guint i)
85 {
86         return kqueue_events [i].ident;
87 }
88
89 static gint
90 kqueue_event_max (void)
91 {
92         return KQUEUE_NEVENTS;
93 }
94
95 static gboolean
96 kqueue_event_create_sockares_at (guint i, gint fd, MonoMList **list)
97 {
98         struct kevent *kqueue_event;
99
100         g_assert (list);
101
102         kqueue_event = &kqueue_events [i];
103         g_assert (kqueue_event);
104
105         g_assert (fd == kqueue_event->ident);
106
107         if (*list && (kqueue_event->filter == EVFILT_READ || (kqueue_event->flags & EV_ERROR) != 0)) {
108                 MonoSocketAsyncResult *io_event = get_sockares_for_event (list, MONO_POLLIN);
109                 if (io_event)
110                         mono_threadpool_ms_enqueue_work_item (((MonoObject*) io_event)->vtable->domain, (MonoObject*) io_event);
111         }
112         if (*list && (kqueue_event->filter == EVFILT_WRITE || (kqueue_event->flags & EV_ERROR) != 0)) {
113                 MonoSocketAsyncResult *io_event = get_sockares_for_event (list, MONO_POLLOUT);
114                 if (io_event)
115                         mono_threadpool_ms_enqueue_work_item (((MonoObject*) io_event)->vtable->domain, (MonoObject*) io_event);
116         }
117
118         if (*list) {
119                 gint events = get_events (*list);
120                 if (kqueue_event->filter == EVFILT_READ && (events & MONO_POLLIN) != 0) {
121                         EV_SET (kqueue_event, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
122                         if (kevent (kqueue_fd, kqueue_event, 1, NULL, 0, NULL) == -1)
123                                 g_warning ("kqueue_event_create_sockares_at: kevent (read) failed, error (%d) %s", errno, g_strerror (errno));
124                 }
125                 if (kqueue_event->filter == EVFILT_WRITE && (events & MONO_POLLOUT) != 0) {
126                         EV_SET (kqueue_event, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0);
127                         if (kevent (kqueue_fd, kqueue_event, 1, NULL, 0, NULL) == -1)
128                                 g_warning ("kqueue_event_create_sockares_at: kevent (write) failed, error (%d) %s", errno, g_strerror (errno));
129                 }
130         }
131
132         return TRUE;
133 }
134
135 static ThreadPoolIOBackend backend_kqueue = {
136         .init = kqueue_init,
137         .cleanup = kqueue_cleanup,
138         .update_add = kqueue_update_add,
139         .event_wait = kqueue_event_wait,
140         .event_max = kqueue_event_max,
141         .event_fd_at = kqueue_event_fd_at,
142         .event_create_sockares_at = kqueue_event_create_sockares_at,
143 };
144
145 #endif