1 /**
2  * \file
3  */
4 
5 #if defined(HAVE_EPOLL)
6 
7 #include <sys/epoll.h>
8 
9 #if defined(HOST_WIN32)
10 /* We assume that epoll is not available on windows */
11 #error
12 #endif
13 
14 #define EPOLL_NEVENTS 128
15 
16 static gint epoll_fd;
17 static struct epoll_event *epoll_events;
18 
19 static gboolean
epoll_init(gint wakeup_pipe_fd)20 epoll_init (gint wakeup_pipe_fd)
21 {
22 	struct epoll_event event;
23 
24 #ifdef EPOOL_CLOEXEC
25 	epoll_fd = epoll_create1 (EPOLL_CLOEXEC);
26 #else
27 	epoll_fd = epoll_create (256);
28 	fcntl (epoll_fd, F_SETFD, FD_CLOEXEC);
29 #endif
30 
31 	if (epoll_fd == -1) {
32 #ifdef EPOOL_CLOEXEC
33 		g_error ("epoll_init: epoll (EPOLL_CLOEXEC) failed, error (%d) %s\n", errno, g_strerror (errno));
34 #else
35 		g_error ("epoll_init: epoll (256) failed, error (%d) %s\n", errno, g_strerror (errno));
36 #endif
37 		return FALSE;
38 	}
39 
40 	event.events = EPOLLIN;
41 	event.data.fd = wakeup_pipe_fd;
42 	if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, event.data.fd, &event) == -1) {
43 		g_error ("epoll_init: epoll_ctl () failed, error (%d) %s", errno, g_strerror (errno));
44 		close (epoll_fd);
45 		return FALSE;
46 	}
47 
48 	epoll_events = g_new0 (struct epoll_event, EPOLL_NEVENTS);
49 
50 	return TRUE;
51 }
52 
53 static void
epoll_register_fd(gint fd,gint events,gboolean is_new)54 epoll_register_fd (gint fd, gint events, gboolean is_new)
55 {
56 	struct epoll_event event;
57 
58 #ifndef EPOLLONESHOT
59 /* it was only defined on android in May 2013 */
60 #define EPOLLONESHOT 0x40000000
61 #endif
62 
63 	event.data.fd = fd;
64 	event.events = EPOLLONESHOT;
65 	if ((events & EVENT_IN) != 0)
66 		event.events |= EPOLLIN;
67 	if ((events & EVENT_OUT) != 0)
68 		event.events |= EPOLLOUT;
69 
70 	if (epoll_ctl (epoll_fd, is_new ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, event.data.fd, &event) == -1)
71 		g_error ("epoll_register_fd: epoll_ctl(%s) failed, error (%d) %s", is_new ? "EPOLL_CTL_ADD" : "EPOLL_CTL_MOD", errno, g_strerror (errno));
72 }
73 
74 static void
epoll_remove_fd(gint fd)75 epoll_remove_fd (gint fd)
76 {
77 	if (epoll_ctl (epoll_fd, EPOLL_CTL_DEL, fd, NULL) == -1)
78 			g_error ("epoll_remove_fd: epoll_ctl (EPOLL_CTL_DEL) failed, error (%d) %s", errno, g_strerror (errno));
79 }
80 
81 static gint
epoll_event_wait(void (* callback)(gint fd,gint events,gpointer user_data),gpointer user_data)82 epoll_event_wait (void (*callback) (gint fd, gint events, gpointer user_data), gpointer user_data)
83 {
84 	gint i, ready;
85 
86 	memset (epoll_events, 0, sizeof (struct epoll_event) * EPOLL_NEVENTS);
87 
88 	mono_gc_set_skip_thread (TRUE);
89 
90 	MONO_ENTER_GC_SAFE;
91 	ready = epoll_wait (epoll_fd, epoll_events, EPOLL_NEVENTS, -1);
92 	MONO_EXIT_GC_SAFE;
93 
94 	mono_gc_set_skip_thread (FALSE);
95 
96 	if (ready == -1) {
97 		switch (errno) {
98 		case EINTR:
99 			ready = 0;
100 			break;
101 		default:
102 			g_error ("epoll_event_wait: epoll_wait () 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 = epoll_events [i].data.fd;
114 		if (epoll_events [i].events & (EPOLLIN | EPOLLERR | EPOLLHUP))
115 			events |= EVENT_IN;
116 		if (epoll_events [i].events & (EPOLLOUT | EPOLLERR | EPOLLHUP))
117 			events |= EVENT_OUT;
118 
119 		callback (fd, events, user_data);
120 	}
121 
122 	return 0;
123 }
124 
125 static ThreadPoolIOBackend backend_epoll = {
126 	.init = epoll_init,
127 	.register_fd = epoll_register_fd,
128 	.remove_fd = epoll_remove_fd,
129 	.event_wait = epoll_event_wait,
130 };
131 
132 #endif
133