Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / threadpool-io-kqueue.c
1 /**
2  * \file
3  */
4
5 #if defined(HAVE_KQUEUE)
6
7 #include <sys/types.h>
8 #include <sys/event.h>
9 #include <sys/time.h>
10
11 #if defined(HOST_WIN32)
12 /* We assume that kqueue is not available on windows */
13 #error
14 #endif
15
16 #define KQUEUE_NEVENTS 128
17
18 static gint kqueue_fd;
19 static struct kevent *kqueue_events;
20
21 static gint
22 KQUEUE_INIT_FD (gint fd, gint events, gint flags)
23 {
24         struct kevent event;
25         EV_SET (&event, fd, events, flags, 0, 0, 0);
26         return kevent (kqueue_fd, &event, 1, NULL, 0, NULL);
27 }
28
29 static gboolean
30 kqueue_init (gint wakeup_pipe_fd)
31 {
32         kqueue_fd = kqueue ();
33         if (kqueue_fd == -1) {
34                 g_error ("kqueue_init: kqueue () failed, error (%d) %s", errno, g_strerror (errno));
35                 return FALSE;
36         }
37
38         if (KQUEUE_INIT_FD (wakeup_pipe_fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1) {
39                 g_error ("kqueue_init: kevent () failed, error (%d) %s", errno, g_strerror (errno));
40                 close (kqueue_fd);
41                 return FALSE;
42         }
43
44         kqueue_events = g_new0 (struct kevent, KQUEUE_NEVENTS);
45
46         return TRUE;
47 }
48
49 static void
50 kqueue_register_fd (gint fd, gint events, gboolean is_new)
51 {
52         if (events & EVENT_IN) {
53                 if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1)
54                         g_error ("kqueue_register_fd: kevent(read,enable) failed, error (%d) %s", errno, g_strerror (errno));
55         } else {
56                 if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_DISABLE) == -1)
57                         g_error ("kqueue_register_fd: kevent(read,disable) failed, error (%d) %s", errno, g_strerror (errno));
58         }
59         if (events & EVENT_OUT) {
60                 if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_ENABLE) == -1)
61                         g_error ("kqueue_register_fd: kevent(write,enable) failed, error (%d) %s", errno, g_strerror (errno));
62         } else {
63                 if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_DISABLE) == -1)
64                         g_error ("kqueue_register_fd: kevent(write,disable) failed, error (%d) %s", errno, g_strerror (errno));
65         }
66 }
67
68 static void
69 kqueue_remove_fd (gint fd)
70 {
71         /* FIXME: a race between closing and adding operation in the Socket managed code trigger a ENOENT error */
72         if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_DELETE) == -1)
73                 g_error ("kqueue_register_fd: kevent(read,delete) failed, error (%d) %s", errno, g_strerror (errno));
74         if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_DELETE) == -1)
75                 g_error ("kqueue_register_fd: kevent(write,delete) failed, error (%d) %s", errno, g_strerror (errno));
76 }
77
78 static gint
79 kqueue_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
80 {
81         gint i, ready;
82
83         memset (kqueue_events, 0, sizeof (struct kevent) * KQUEUE_NEVENTS);
84
85         mono_gc_set_skip_thread (TRUE);
86
87         MONO_ENTER_GC_SAFE;
88         ready = kevent (kqueue_fd, NULL, 0, kqueue_events, KQUEUE_NEVENTS, NULL);
89         MONO_EXIT_GC_SAFE;
90
91         mono_gc_set_skip_thread (FALSE);
92
93         if (ready == -1) {
94                 switch (errno) {
95                 case EINTR:
96                         ready = 0;
97                         break;
98                 default:
99                         g_error ("kqueue_event_wait: kevent () failed, error (%d) %s", errno, g_strerror (errno));
100                         break;
101                 }
102         }
103
104         if (ready == -1)
105                 return -1;
106
107         for (i = 0; i < ready; ++i) {
108                 gint fd, events = 0;
109
110                 fd = kqueue_events [i].ident;
111                 if (kqueue_events [i].filter == EVFILT_READ || (kqueue_events [i].flags & EV_ERROR) != 0)
112                         events |= EVENT_IN;
113                 if (kqueue_events [i].filter == EVFILT_WRITE || (kqueue_events [i].flags & EV_ERROR) != 0)
114                         events |= EVENT_OUT;
115
116                 callback (fd, events, user_data);
117         }
118
119         return 0;
120 }
121
122 static ThreadPoolIOBackend backend_kqueue = {
123         .init = kqueue_init,
124         .register_fd = kqueue_register_fd,
125         .remove_fd = kqueue_remove_fd,
126         .event_wait = kqueue_event_wait,
127 };
128
129 #endif