82e0bfe8a353c4834440cc37c7916aaa60c732fb
[mono.git] / mono / metadata / tpool-epoll.c
1 /*
2  * tpool-epoll.c: epoll related stuff
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Gonzalo Paniagua Javier (gonzalo@ximian.com)
7  *
8  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
9  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
10  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
11  */
12
13 struct _tp_epoll_data {
14         int epollfd;
15 };
16
17 typedef struct _tp_epoll_data tp_epoll_data;
18 static void tp_epoll_modify (gpointer p, int fd, int operation, int events, gboolean is_new);
19 static void tp_epoll_shutdown (gpointer event_data);
20 static void tp_epoll_wait (gpointer event_data);
21
22 static gpointer
23 tp_epoll_init (SocketIOData *data)
24 {
25         tp_epoll_data *result;
26
27         result = g_new0 (tp_epoll_data, 1);
28 #ifdef EPOLL_CLOEXEC
29         result->epollfd = epoll_create1 (EPOLL_CLOEXEC);
30 #else
31         result->epollfd = epoll_create (256); /* The number does not really matter */
32         fcntl (result->epollfd, F_SETFD, FD_CLOEXEC);
33 #endif
34         if (result->epollfd == -1) {
35                 int err = errno;
36                 if (g_getenv ("MONO_DEBUG")) {
37 #ifdef EPOLL_CLOEXEC
38                         g_message ("epoll_create1(EPOLL_CLOEXEC) failed: %d %s", err, g_strerror (err));
39 #else
40                         g_message ("epoll_create(256) failed: %d %s", err, g_strerror (err));
41 #endif
42                 }
43
44                 return NULL;
45         }
46
47         data->shutdown = tp_epoll_shutdown;
48         data->modify = tp_epoll_modify;
49         data->wait = tp_epoll_wait;
50         return result;
51 }
52
53 static void
54 tp_epoll_modify (gpointer p, int fd, int operation, int events, gboolean is_new)
55 {
56         SocketIOData *socket_io_data;
57         socket_io_data = p;
58         tp_epoll_data *data = socket_io_data->event_data;
59         struct epoll_event evt;
60         int epoll_op;
61
62         memset (&evt, 0, sizeof (evt));
63         evt.data.fd = fd;
64         if ((events & MONO_POLLIN) != 0)
65                 evt.events |= EPOLLIN;
66         if ((events & MONO_POLLOUT) != 0)
67                 evt.events |= EPOLLOUT;
68
69         epoll_op = (is_new) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
70         if (epoll_ctl (data->epollfd, epoll_op, fd, &evt) == -1) {
71                 int err = errno;
72                 if (epoll_op == EPOLL_CTL_ADD && err == EEXIST) {
73                         epoll_op = EPOLL_CTL_MOD;
74                         if (epoll_ctl (data->epollfd, epoll_op, fd, &evt) == -1) {
75                                 g_message ("epoll_ctl(MOD): %d %s", err, g_strerror (err));
76                         }
77                 }
78         }
79         LeaveCriticalSection (&socket_io_data->io_lock);
80 }
81
82 static void
83 tp_epoll_shutdown (gpointer event_data)
84 {
85         tp_epoll_data *data = event_data;
86
87         close (data->epollfd);
88         g_free (data);
89 }
90
91 #define EPOLL_ERRORS (EPOLLERR | EPOLLHUP)
92 #define EPOLL_NEVENTS   128
93 static void
94 tp_epoll_wait (gpointer p)
95 {
96         SocketIOData *socket_io_data;
97         int epollfd;
98         MonoInternalThread *thread;
99         struct epoll_event *events, *evt;
100         int ready = 0, i;
101         gpointer async_results [EPOLL_NEVENTS * 2]; // * 2 because each loop can add up to 2 results here
102         gint nresults;
103         tp_epoll_data *data;
104
105         socket_io_data = p;
106         data = socket_io_data->event_data;
107         epollfd = data->epollfd;
108         thread = mono_thread_internal_current ();
109         events = g_new0 (struct epoll_event, EPOLL_NEVENTS);
110
111         while (1) {
112                 mono_gc_set_skip_thread (TRUE);
113
114                 do {
115                         if (ready == -1) {
116                                 if (THREAD_WANTS_A_BREAK (thread))
117                                         mono_thread_interruption_checkpoint ();
118                         }
119                         ready = epoll_wait (epollfd, events, EPOLL_NEVENTS, -1);
120                 } while (ready == -1 && errno == EINTR);
121
122                 mono_gc_set_skip_thread (FALSE);
123
124                 if (ready == -1) {
125                         int err = errno;
126                         g_free (events);
127                         if (err != EBADF)
128                                 g_warning ("epoll_wait: %d %s", err, g_strerror (err));
129
130                         return;
131                 }
132
133                 EnterCriticalSection (&socket_io_data->io_lock);
134                 if (socket_io_data->inited == 3) {
135                         g_free (events);
136                         LeaveCriticalSection (&socket_io_data->io_lock);
137                         return; /* cleanup called */
138                 }
139
140                 nresults = 0;
141                 for (i = 0; i < ready; i++) {
142                         int fd;
143                         MonoMList *list;
144                         MonoObject *ares;
145
146                         evt = &events [i];
147                         fd = evt->data.fd;
148                         list = mono_g_hash_table_lookup (socket_io_data->sock_to_state, GINT_TO_POINTER (fd));
149                         if (list != NULL && (evt->events & (EPOLLIN | EPOLL_ERRORS)) != 0) {
150                                 ares = get_io_event (&list, MONO_POLLIN);
151                                 if (ares != NULL)
152                                         async_results [nresults++] = ares;
153                         }
154
155                         if (list != NULL && (evt->events & (EPOLLOUT | EPOLL_ERRORS)) != 0) {
156                                 ares = get_io_event (&list, MONO_POLLOUT);
157                                 if (ares != NULL)
158                                         async_results [nresults++] = ares;
159                         }
160
161                         if (list != NULL) {
162                                 int p;
163
164                                 mono_g_hash_table_replace (socket_io_data->sock_to_state, GINT_TO_POINTER (fd), list);
165                                 p = get_events_from_list (list);
166                                 evt->events = (p & MONO_POLLOUT) ? EPOLLOUT : 0;
167                                 evt->events |= (p & MONO_POLLIN) ? EPOLLIN : 0;
168                                 if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, evt) == -1) {
169                                         if (epoll_ctl (epollfd, EPOLL_CTL_ADD, fd, evt) == -1) {
170                                                 int err = errno;
171                                                 g_message ("epoll(ADD): %d %s", err, g_strerror (err));
172                                         }
173                                 }
174                         } else {
175                                 mono_g_hash_table_remove (socket_io_data->sock_to_state, GINT_TO_POINTER (fd));
176                                 epoll_ctl (epollfd, EPOLL_CTL_DEL, fd, evt);
177                         }
178                 }
179                 LeaveCriticalSection (&socket_io_data->io_lock);
180                 threadpool_append_jobs (&async_io_tp, (MonoObject **) async_results, nresults);
181                 mono_gc_bzero (async_results, sizeof (gpointer) * nresults);
182         }
183 }
184 #undef EPOLL_NEVENTS
185 #undef EPOLL_ERRORS