Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[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 gint
19 KQUEUE_INIT_FD (gint fd, gint events, gint flags)
20 {
21         struct kevent event;
22         EV_SET (&event, fd, events, flags, 0, 0, 0);
23         return kevent (kqueue_fd, &event, 1, NULL, 0, NULL);
24 }
25
26 static gboolean
27 kqueue_init (gint wakeup_pipe_fd)
28 {
29         kqueue_fd = kqueue ();
30         if (kqueue_fd == -1) {
31                 g_error ("kqueue_init: kqueue () failed, error (%d) %s", errno, g_strerror (errno));
32                 return FALSE;
33         }
34
35         if (KQUEUE_INIT_FD (wakeup_pipe_fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1) {
36                 g_error ("kqueue_init: kevent () failed, error (%d) %s", errno, g_strerror (errno));
37                 close (kqueue_fd);
38                 return FALSE;
39         }
40
41         kqueue_events = g_new0 (struct kevent, KQUEUE_NEVENTS);
42
43         return TRUE;
44 }
45
46 static void
47 kqueue_cleanup (void)
48 {
49         g_free (kqueue_events);
50         close (kqueue_fd);
51 }
52
53 static void
54 kqueue_register_fd (gint fd, gint events, gboolean is_new)
55 {
56         if (events & EVENT_IN) {
57                 if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_ENABLE) == -1)
58                         g_error ("kqueue_register_fd: kevent(read,enable) failed, error (%d) %s", errno, g_strerror (errno));
59         } else {
60                 if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_ADD | EV_DISABLE) == -1)
61                         g_error ("kqueue_register_fd: kevent(read,disable) failed, error (%d) %s", errno, g_strerror (errno));
62         }
63         if (events & EVENT_OUT) {
64                 if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_ENABLE) == -1)
65                         g_error ("kqueue_register_fd: kevent(write,enable) failed, error (%d) %s", errno, g_strerror (errno));
66         } else {
67                 if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_ADD | EV_DISABLE) == -1)
68                         g_error ("kqueue_register_fd: kevent(write,disable) failed, error (%d) %s", errno, g_strerror (errno));
69         }
70 }
71
72 static void
73 kqueue_remove_fd (gint fd)
74 {
75         /* FIXME: a race between closing and adding operation in the Socket managed code trigger a ENOENT error */
76         if (KQUEUE_INIT_FD (fd, EVFILT_READ, EV_DELETE) == -1)
77                 g_error ("kqueue_register_fd: kevent(read,delete) failed, error (%d) %s", errno, g_strerror (errno));
78         if (KQUEUE_INIT_FD (fd, EVFILT_WRITE, EV_DELETE) == -1)
79                 g_error ("kqueue_register_fd: kevent(write,delete) failed, error (%d) %s", errno, g_strerror (errno));
80 }
81
82 static gint
83 kqueue_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
84 {
85         gint i, ready;
86
87         memset (kqueue_events, 0, sizeof (struct kevent) * KQUEUE_NEVENTS);
88
89         mono_gc_set_skip_thread (TRUE);
90
91         ready = kevent (kqueue_fd, NULL, 0, kqueue_events, KQUEUE_NEVENTS, NULL);
92
93         mono_gc_set_skip_thread (FALSE);
94
95         if (ready == -1) {
96                 switch (errno) {
97                 case EINTR:
98                         mono_thread_internal_check_for_interruption_critical (mono_thread_internal_current ());
99                         ready = 0;
100                         break;
101                 default:
102                         g_error ("kqueue_event_wait: kevent () failed, error (%d) %s", errno, g_strerror (errno));
103                         break;
104                 }
105         }
106
107         if (ready == -1)
108                 return -1;
109
110         for (i = 0; i < ready; ++i) {
111                 gint fd, events = 0;
112
113                 fd = kqueue_events [i].ident;
114                 if (kqueue_events [i].filter == EVFILT_READ || (kqueue_events [i].flags & EV_ERROR) != 0)
115                         events |= EVENT_IN;
116                 if (kqueue_events [i].filter == EVFILT_WRITE || (kqueue_events [i].flags & EV_ERROR) != 0)
117                         events |= EVENT_OUT;
118
119                 callback (fd, events, user_data);
120         }
121
122         return 0;
123 }
124
125 static ThreadPoolIOBackend backend_kqueue = {
126         .init = kqueue_init,
127         .cleanup = kqueue_cleanup,
128         .register_fd = kqueue_register_fd,
129         .remove_fd = kqueue_remove_fd,
130         .event_wait = kqueue_event_wait,
131 };
132
133 #endif