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
KQUEUE_INIT_FD(gint fd,gint events,gint flags)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
kqueue_init(gint wakeup_pipe_fd)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
kqueue_register_fd(gint fd,gint events,gboolean is_new)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
kqueue_remove_fd(gint fd)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
kqueue_event_wait(void (* callback)(gint fd,gint events,gpointer user_data),gpointer user_data)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
130