static void wait_callback (gint fd, gint events, gpointer user_data) { if (mono_runtime_is_shutting_down ()) return; if (fd == threadpool_io->wakeup_pipes [0]) { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: wke"); selector_thread_wakeup_drain_pipes (); } else { MonoGHashTable *states; MonoMList *list = NULL; gpointer k; gboolean remove_fd = FALSE; gint operations; g_assert (user_data); states = user_data; mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: cal fd %3d, events = %2s | %2s | %3s", fd, (events & EVENT_IN) ? "RD" : "..", (events & EVENT_OUT) ? "WR" : "..", (events & EVENT_ERR) ? "ERR" : "..."); if (!mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) g_error ("wait_callback: fd %d not found in states table", fd); if (list && (events & EVENT_IN) != 0) { MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_IN); if (job) mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job); } if (list && (events & EVENT_OUT) != 0) { MonoIOSelectorJob *job = get_job_for_event (&list, EVENT_OUT); if (job) mono_threadpool_ms_enqueue_work_item (((MonoObject*) job)->vtable->domain, (MonoObject*) job); } remove_fd = (events & EVENT_ERR) == EVENT_ERR; if (!remove_fd) { mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); operations = get_operations_for_jobs (list); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: res fd %3d, events = %2s | %2s | %2s", fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : ".."); threadpool_io->backend.register_fd (fd, operations, FALSE); } else { mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: err fd %d", fd); mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); threadpool_io->backend.remove_fd (fd); } } }
static void filter_jobs_for_domain (gpointer key, gpointer value, gpointer user_data) { FilterSockaresForDomainData *data; MonoMList *list = value, *element; MonoDomain *domain; MonoGHashTable *states; g_assert (user_data); data = user_data; domain = data->domain; states = data->states; for (element = list; element; element = mono_mlist_next (element)) { MonoIOSelectorJob *job = (MonoIOSelectorJob*) mono_mlist_get_data (element); if (mono_object_domain (job) == domain) mono_mlist_set_data (element, NULL); } /* we skip all the first elements which are NULL */ for (; list; list = mono_mlist_next (list)) { if (mono_mlist_get_data (list)) break; } if (list) { g_assert (mono_mlist_get_data (list)); /* we delete all the NULL elements after the first one */ for (element = list; element;) { MonoMList *next; if (!(next = mono_mlist_next (element))) break; if (mono_mlist_get_data (next)) element = next; else mono_mlist_set_next (element, mono_mlist_next (next)); } } mono_g_hash_table_replace (states, key, list); }
static void socket_io_add (MonoAsyncResult *ares, MonoSocketAsyncResult *state) { MonoMList *list; SocketIOData *data = &socket_io_data; int fd; gboolean is_new; int ievt; socket_io_init (&socket_io_data); if (mono_runtime_is_shutting_down () || data->inited == 3 || data->sock_to_state == NULL) return; if (async_tp.pool_status == 2) return; MONO_OBJECT_SETREF (state, ares, ares); fd = GPOINTER_TO_INT (state->handle); EnterCriticalSection (&data->io_lock); if (data->sock_to_state == NULL) { LeaveCriticalSection (&data->io_lock); return; } list = mono_g_hash_table_lookup (data->sock_to_state, GINT_TO_POINTER (fd)); if (list == NULL) { list = mono_mlist_alloc ((MonoObject*)state); is_new = TRUE; } else { list = mono_mlist_append (list, (MonoObject*)state); is_new = FALSE; } mono_g_hash_table_replace (data->sock_to_state, state->handle, list); ievt = get_events_from_list (list); data->modify (data->event_data, fd, state->operation, ievt, is_new); LeaveCriticalSection (&data->io_lock); }
static void selector_thread (gpointer data) { MonoGHashTable *states; io_selector_running = TRUE; if (mono_runtime_is_shutting_down ()) { io_selector_running = FALSE; return; } states = mono_g_hash_table_new_type (g_direct_hash, g_direct_equal, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_THREAD_POOL, "i/o thread pool states table"); for (;;) { gint i, j; gint res; mono_mutex_lock (&threadpool_io->updates_lock); for (i = 0; i < threadpool_io->updates_size; ++i) { ThreadPoolIOUpdate *update = &threadpool_io->updates [i]; switch (update->type) { case UPDATE_EMPTY: break; case UPDATE_ADD: { gint fd; gint operations; gpointer k; gboolean exists; MonoMList *list = NULL; MonoIOSelectorJob *job; fd = update->data.add.fd; g_assert (fd >= 0); job = update->data.add.job; g_assert (job); exists = mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list); list = mono_mlist_append (list, (MonoObject*) job); mono_g_hash_table_replace (states, GINT_TO_POINTER (fd), list); operations = get_operations_for_jobs (list); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: %3s fd %3d, operations = %2s | %2s | %2s", exists ? "mod" : "add", fd, (operations & EVENT_IN) ? "RD" : "..", (operations & EVENT_OUT) ? "WR" : ".."); threadpool_io->backend.register_fd (fd, operations, !exists); break; } case UPDATE_REMOVE_SOCKET: { gint fd; gpointer k; MonoMList *list = NULL; fd = update->data.remove_socket.fd; g_assert (fd >= 0); if (mono_g_hash_table_lookup_extended (states, GINT_TO_POINTER (fd), &k, (gpointer*) &list)) { mono_g_hash_table_remove (states, GINT_TO_POINTER (fd)); for (j = i + 1; j < threadpool_io->updates_size; ++j) { ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; if (update->type == UPDATE_ADD && update->data.add.fd == fd) memset (update, 0, sizeof (ThreadPoolIOUpdate)); } for (; list; list = mono_mlist_remove_item (list, list)) mono_threadpool_ms_enqueue_work_item (mono_object_domain (mono_mlist_get_data (list)), mono_mlist_get_data (list)); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: del fd %3d", fd); threadpool_io->backend.remove_fd (fd); } break; } case UPDATE_REMOVE_DOMAIN: { MonoDomain *domain; domain = update->data.remove_domain.domain; g_assert (domain); FilterSockaresForDomainData user_data = { .domain = domain, .states = states }; mono_g_hash_table_foreach (states, filter_jobs_for_domain, &user_data); for (j = i + 1; j < threadpool_io->updates_size; ++j) { ThreadPoolIOUpdate *update = &threadpool_io->updates [j]; if (update->type == UPDATE_ADD && mono_object_domain (update->data.add.job) == domain) memset (update, 0, sizeof (ThreadPoolIOUpdate)); } break; } default: g_assert_not_reached (); } } mono_cond_broadcast (&threadpool_io->updates_cond); if (threadpool_io->updates_size > 0) { threadpool_io->updates_size = 0; memset (&threadpool_io->updates, 0, UPDATES_CAPACITY * sizeof (ThreadPoolIOUpdate)); } mono_mutex_unlock (&threadpool_io->updates_lock); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_THREADPOOL, "io threadpool: wai"); res = threadpool_io->backend.event_wait (wait_callback, states); if (res == -1 || mono_runtime_is_shutting_down ()) break; } mono_g_hash_table_destroy (states); io_selector_running = FALSE; }
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; ); } }