/* * select/poll wake up when a socket is closed, but epoll just removes * the socket from its internal list without notification. */ void mono_thread_pool_remove_socket (int sock) { MonoMList *list; MonoSocketAsyncResult *state; MonoObject *ares; if (socket_io_data.inited == 0) return; EnterCriticalSection (&socket_io_data.io_lock); if (socket_io_data.sock_to_state == NULL) { LeaveCriticalSection (&socket_io_data.io_lock); return; } list = mono_g_hash_table_lookup (socket_io_data.sock_to_state, GINT_TO_POINTER (sock)); if (list) mono_g_hash_table_remove (socket_io_data.sock_to_state, GINT_TO_POINTER (sock)); LeaveCriticalSection (&socket_io_data.io_lock); while (list) { state = (MonoSocketAsyncResult *) mono_mlist_get_data (list); if (state->operation == AIO_OP_RECEIVE) state->operation = AIO_OP_RECV_JUST_CALLBACK; else if (state->operation == AIO_OP_SEND) state->operation = AIO_OP_SEND_JUST_CALLBACK; ares = get_io_event (&list, MONO_POLLIN); threadpool_append_job (&async_io_tp, ares); if (list) { ares = get_io_event (&list, MONO_POLLOUT); threadpool_append_job (&async_io_tp, ares); } } }
static void tp_poll_wait (gpointer p) { #if MONO_SMALL_CONFIG #define INITIAL_POLLFD_SIZE 128 #else #define INITIAL_POLLFD_SIZE 1024 #endif #define POLL_ERRORS (MONO_POLLERR | MONO_POLLHUP | MONO_POLLNVAL) mono_pollfd *pfds; gint maxfd = 1; gint allocated; gint i; MonoInternalThread *thread; tp_poll_data *data; SocketIOData *socket_io_data = p; gpointer *async_results; gint nresults; thread = mono_thread_internal_current (); data = socket_io_data->event_data; allocated = INITIAL_POLLFD_SIZE; pfds = g_new0 (mono_pollfd, allocated); async_results = g_new0 (gpointer, allocated * 2); INIT_POLLFD (pfds, data->pipe [0], MONO_POLLIN); for (i = 1; i < allocated; i++) INIT_POLLFD (&pfds [i], -1, 0); while (1) { int nsock = 0; mono_pollfd *pfd; char one [1]; MonoMList *list; MonoObject *ares; do { if (nsock == -1) { if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } nsock = mono_poll (pfds, maxfd, -1); } while (nsock == -1 && errno == EINTR); /* * Apart from EINTR, we only check EBADF, for the rest: * EINVAL: mono_poll() 'protects' us from descriptor * numbers above the limit if using select() by marking * then as MONO_POLLERR. If a system poll() is being * used, the number of descriptor we're passing will not * be over sysconf(_SC_OPEN_MAX), as the error would have * happened when opening. * * EFAULT: we own the memory pointed by pfds. * ENOMEM: we're doomed anyway * */ if (nsock == -1 && errno == EBADF) { pfds->revents = 0; /* Just in case... */ nsock = mark_bad_fds (pfds, maxfd); } if ((pfds->revents & POLL_ERRORS) != 0) { /* We're supposed to die now, as the pipe has been closed */ g_free (pfds); g_free (async_results); socket_io_cleanup (socket_io_data); return; } /* Got a new socket */ if ((pfds->revents & MONO_POLLIN) != 0) { int nread; for (i = 1; i < allocated; i++) { pfd = &pfds [i]; if (pfd->fd == -1 || pfd->fd == data->newpfd.fd) break; } if (i == allocated) { mono_pollfd *oldfd; oldfd = pfds; i = allocated; allocated = allocated * 2; pfds = g_renew (mono_pollfd, oldfd, allocated); g_free (oldfd); for (; i < allocated; i++) INIT_POLLFD (&pfds [i], -1, 0); async_results = g_renew (gpointer, async_results, allocated * 2); } #ifndef HOST_WIN32 nread = read (data->pipe [0], one, 1); #else nread = recv ((SOCKET) data->pipe [0], one, 1, 0); #endif if (nread <= 0) { g_free (pfds); g_free (async_results); return; /* we're closed */ } INIT_POLLFD (&pfds [i], data->newpfd.fd, data->newpfd.events); memset (&data->newpfd, 0, sizeof (mono_pollfd)); MONO_SEM_POST (&data->new_sem); if (i >= maxfd) maxfd = i + 1; nsock--; } if (nsock == 0) continue; EnterCriticalSection (&socket_io_data->io_lock); if (socket_io_data->inited == 3) { g_free (pfds); g_free (async_results); LeaveCriticalSection (&socket_io_data->io_lock); return; /* cleanup called */ } nresults = 0; for (i = 1; i < maxfd && nsock > 0; i++) { pfd = &pfds [i]; if (pfd->fd == -1 || pfd->revents == 0) continue; nsock--; list = mono_g_hash_table_lookup (socket_io_data->sock_to_state, GINT_TO_POINTER (pfd->fd)); if (list != NULL && (pfd->revents & (MONO_POLLIN | POLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLIN); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL && (pfd->revents & (MONO_POLLOUT | POLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLOUT); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL) { mono_g_hash_table_replace (socket_io_data->sock_to_state, GINT_TO_POINTER (pfd->fd), list); pfd->events = get_events_from_list (list); } else { mono_g_hash_table_remove (socket_io_data->sock_to_state, GINT_TO_POINTER (pfd->fd)); pfd->fd = -1; if (i == maxfd - 1) maxfd--; } } LeaveCriticalSection (&socket_io_data->io_lock); threadpool_append_jobs (&async_io_tp, (MonoObject **) async_results, nresults); memset (async_results, 0, sizeof (gpointer) * nresults); } }
static void tp_kqueue_wait (gpointer p) { SocketIOData *socket_io_data; int kfd; MonoInternalThread *thread; struct kevent *events, *evt; int ready = 0, i; gpointer async_results [KQUEUE_NEVENTS * 2]; // * 2 because each loop can add up to 2 results here gint nresults; tp_kqueue_data *data; socket_io_data = p; data = socket_io_data->event_data; kfd = data->fd; thread = mono_thread_internal_current (); events = g_new0 (struct kevent, KQUEUE_NEVENTS); while (1) { mono_gc_set_skip_thread (TRUE); do { if (ready == -1) { if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } ready = kevent (kfd, NULL, 0, events, KQUEUE_NEVENTS, NULL); } while (ready == -1 && errno == EINTR); mono_gc_set_skip_thread (FALSE); if (ready == -1) { int err = errno; g_free (events); if (err != EBADF) g_warning ("kevent wait: %d %s", err, g_strerror (err)); return; } EnterCriticalSection (&socket_io_data->io_lock); if (socket_io_data->inited == 3) { g_free (events); LeaveCriticalSection (&socket_io_data->io_lock); return; /* cleanup called */ } nresults = 0; for (i = 0; i < ready; i++) { int fd; MonoMList *list; MonoObject *ares; evt = &events [i]; fd = evt->ident; list = mono_g_hash_table_lookup (socket_io_data->sock_to_state, GINT_TO_POINTER (fd)); if (list != NULL && (evt->filter == EVFILT_READ || (evt->flags & EV_ERROR) != 0)) { ares = get_io_event (&list, MONO_POLLIN); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL && (evt->filter == EVFILT_WRITE || (evt->flags & EV_ERROR) != 0)) { ares = get_io_event (&list, MONO_POLLOUT); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL) { int p; mono_g_hash_table_replace (socket_io_data->sock_to_state, GINT_TO_POINTER (fd), list); p = get_events_from_list (list); if (evt->filter == EVFILT_READ && (p & MONO_POLLIN) != 0) { EV_SET (evt, fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0); kevent_change (kfd, evt, "READD read"); } if (evt->filter == EVFILT_WRITE && (p & MONO_POLLOUT) != 0) { EV_SET (evt, fd, EVFILT_WRITE, EV_ADD | EV_ENABLE | EV_ONESHOT, 0, 0, 0); kevent_change (kfd, evt, "READD write"); } } else { mono_g_hash_table_remove (socket_io_data->sock_to_state, GINT_TO_POINTER (fd)); } } LeaveCriticalSection (&socket_io_data->io_lock); threadpool_append_jobs (&async_io_tp, (MonoObject **) async_results, nresults); mono_gc_bzero (async_results, sizeof (gpointer) * nresults); } }
static void tp_epoll_wait (gpointer p) { SocketIOData *socket_io_data; int epollfd; struct epoll_event *events, *evt; int ready = 0, i; gpointer async_results [EPOLL_NEVENTS * 2]; // * 2 because each loop can add up to 2 results here gint nresults; tp_epoll_data *data; socket_io_data = p; data = socket_io_data->event_data; epollfd = data->epollfd; events = g_new0 (struct epoll_event, EPOLL_NEVENTS); while (1) { mono_gc_set_skip_thread (TRUE); do { if (ready == -1) { check_for_interruption_critical (); } ready = epoll_wait (epollfd, events, EPOLL_NEVENTS, -1); } while (ready == -1 && errno == EINTR); mono_gc_set_skip_thread (FALSE); if (ready == -1) { int err = errno; g_free (events); if (err != EBADF) g_warning ("epoll_wait: %d %s", err, g_strerror (err)); return; } EnterCriticalSection (&socket_io_data->io_lock); if (socket_io_data->inited == 3) { g_free (events); LeaveCriticalSection (&socket_io_data->io_lock); return; /* cleanup called */ } nresults = 0; for (i = 0; i < ready; i++) { int fd; MonoMList *list; MonoObject *ares; evt = &events [i]; fd = evt->data.fd; list = mono_g_hash_table_lookup (socket_io_data->sock_to_state, GINT_TO_POINTER (fd)); if (list != NULL && (evt->events & (EPOLLIN | EPOLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLIN); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL && (evt->events & (EPOLLOUT | EPOLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLOUT); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL) { int p; mono_g_hash_table_replace (socket_io_data->sock_to_state, GINT_TO_POINTER (fd), list); p = get_events_from_list (list); evt->events = (p & MONO_POLLOUT) ? EPOLLOUT : 0; evt->events |= (p & MONO_POLLIN) ? EPOLLIN : 0; if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, evt) == -1) { if (epoll_ctl (epollfd, EPOLL_CTL_ADD, fd, evt) == -1) { int err = errno; g_message ("epoll(ADD): %d %s", err, g_strerror (err)); } } } else { mono_g_hash_table_remove (socket_io_data->sock_to_state, GINT_TO_POINTER (fd)); epoll_ctl (epollfd, EPOLL_CTL_DEL, fd, evt); } } LeaveCriticalSection (&socket_io_data->io_lock); threadpool_append_jobs (&async_io_tp, (MonoObject **) async_results, nresults); mono_gc_bzero (async_results, sizeof (gpointer) * nresults); } }
static void socket_io_epoll_main (gpointer p) { SocketIOData *data; int epollfd; MonoInternalThread *thread; struct epoll_event *events, *evt; int ready = 0, i; gpointer async_results [EPOLL_NEVENTS * 2]; // * 2 because each loop can add up to 2 results here gint nresults; data = p; epollfd = data->epollfd; thread = mono_thread_internal_current (); events = g_new0 (struct epoll_event, EPOLL_NEVENTS); while (1) { do { if (ready == -1) { if (THREAD_WANTS_A_BREAK (thread)) mono_thread_interruption_checkpoint (); } EPOLL_DEBUG ("epoll_wait init"); ready = epoll_wait (epollfd, events, EPOLL_NEVENTS, -1); EPOLL_DEBUG_STMT( int err = errno; EPOLL_DEBUG ("epoll_wait end with %d ready sockets (%d %s).", ready, err, (err) ? g_strerror (err) : ""); errno = err; ); } while (ready == -1 && errno == EINTR); if (ready == -1) { int err = errno; g_free (events); if (err != EBADF) g_warning ("epoll_wait: %d %s", err, g_strerror (err)); close (epollfd); return; } EnterCriticalSection (&data->io_lock); if (data->inited == 0) { EPOLL_DEBUG ("data->inited == 0"); g_free (events); close (epollfd); return; /* cleanup called */ } nresults = 0; for (i = 0; i < ready; i++) { int fd; MonoMList *list; MonoObject *ares; evt = &events [i]; fd = evt->data.fd; list = mono_g_hash_table_lookup (data->sock_to_state, GINT_TO_POINTER (fd)); EPOLL_DEBUG ("Event %d on %d list length: %d", evt->events, fd, mono_mlist_length (list)); if (list != NULL && (evt->events & (EPOLLIN | EPOLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLIN); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL && (evt->events & (EPOLLOUT | EPOLL_ERRORS)) != 0) { ares = get_io_event (&list, MONO_POLLOUT); if (ares != NULL) async_results [nresults++] = ares; } if (list != NULL) { mono_g_hash_table_replace (data->sock_to_state, GINT_TO_POINTER (fd), list); evt->events = get_events_from_list (list); EPOLL_DEBUG ("MOD %d to %d", fd, evt->events); if (epoll_ctl (epollfd, EPOLL_CTL_MOD, fd, evt)) { if (epoll_ctl (epollfd, EPOLL_CTL_ADD, fd, evt) == -1) { EPOLL_DEBUG_STMT ( int err = errno; EPOLL_DEBUG ("epoll_ctl(MOD): %d %s fd: %d events: %d", err, g_strerror (err), fd, evt->events); errno = err; ); } }