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