/* returns whether there were any poll events handled */ static dbus_bool_t babysitter_iteration (DBusBabysitter *sitter, dbus_bool_t block) { DBusPollFD fds[2]; int i; dbus_bool_t descriptors_ready; descriptors_ready = FALSE; i = 0; if (sitter->error_pipe_from_child >= 0) { fds[i].fd = sitter->error_pipe_from_child; fds[i].events = _DBUS_POLLIN; fds[i].revents = 0; ++i; } if (sitter->socket_to_babysitter >= 0) { fds[i].fd = sitter->socket_to_babysitter; fds[i].events = _DBUS_POLLIN; fds[i].revents = 0; ++i; } if (i > 0) { int ret; ret = _dbus_poll (fds, i, 0); if (ret == 0 && block) ret = _dbus_poll (fds, i, -1); if (ret > 0) { descriptors_ready = TRUE; while (i > 0) { --i; if (fds[i].fd == sitter->error_pipe_from_child) handle_error_pipe (sitter, fds[i].revents); else if (fds[i].fd == sitter->socket_to_babysitter) handle_babysitter_socket (sitter, fds[i].revents); } } } return descriptors_ready; }
static void babysit (pid_t grandchild_pid, int parent_pipe) { int sigchld_pipe[2]; /* We don't exec, so we keep parent state, such as the pid that * _dbus_verbose() uses. Reset the pid here. */ _dbus_verbose_reset (); /* I thought SIGCHLD would just wake up the poll, but * that didn't seem to work, so added this pipe. * Probably the pipe is more likely to work on busted * operating systems anyhow. */ if (pipe (sigchld_pipe) < 0) { _dbus_warn ("Not enough file descriptors to create pipe in babysitter process\n"); exit (1); } babysit_sigchld_pipe = sigchld_pipe[WRITE_END]; #ifndef __SYMBIAN32__ _dbus_set_signal_handler (SIGCHLD, babysit_signal_handler); #endif write_pid (parent_pipe, grandchild_pid); check_babysit_events (grandchild_pid, parent_pipe, 0); while (TRUE) { DBusPollFD pfds[2]; pfds[0].fd = parent_pipe; pfds[0].events = _DBUS_POLLIN; pfds[0].revents = 0; pfds[1].fd = sigchld_pipe[READ_END]; pfds[1].events = _DBUS_POLLIN; pfds[1].revents = 0; _dbus_poll (pfds, _DBUS_N_ELEMENTS (pfds), -1); if (pfds[0].revents != 0) { check_babysit_events (grandchild_pid, parent_pipe, pfds[0].revents); } else if (pfds[1].revents & _DBUS_POLLIN) { char b; read (sigchld_pipe[READ_END], &b, 1); /* do waitpid check */ check_babysit_events (grandchild_pid, parent_pipe, 0); } } exit (1); }
/** This is basically Linux's epoll_wait(2) implemented in terms of poll(2); * it returns results into a caller-supplied buffer so we can be reentrant. */ static int socket_set_poll_poll (DBusSocketSet *set, DBusSocketEvent *revents, int max_events, int timeout_ms) { DBusSocketSetPoll *self = socket_set_poll_cast (set); int i; int n_events; int n_ready; _dbus_assert (max_events > 0); for (i = 0; i < self->n_fds; i++) self->fds[i].revents = 0; n_ready = _dbus_poll (self->fds, self->n_fds, timeout_ms); if (n_ready <= 0) return n_ready; n_events = 0; for (i = 0; i < self->n_fds; i++) { if (self->fds[i].revents != 0) { revents[n_events].fd = self->fds[i].fd; revents[n_events].flags = watch_flags_from_poll_revents (self->fds[i].revents); n_events += 1; /* We ignore events beyond max_events because we have nowhere to * put them. _dbus_poll is level-triggered, so we'll just be told * about them next time round the main loop anyway. */ if (n_events == max_events) return n_events; } } return n_events; }
/** * @todo We need to have a way to wake up the select sleep if * a new iteration request comes in with a flag (read/write) that * we're not currently serving. Otherwise a call that just reads * could block a write call forever (if there are no incoming * messages). */ static void socket_do_iteration (DBusTransport *transport, unsigned int flags, int timeout_milliseconds) { DBusTransportSocket *socket_transport = (DBusTransportSocket*) transport; DBusPollFD poll_fd; int poll_res; int poll_timeout; _dbus_verbose (" iteration flags = %s%s timeout = %d read_watch = %p write_watch = %p fd = %" DBUS_SOCKET_FORMAT "\n", flags & DBUS_ITERATION_DO_READING ? "read" : "", flags & DBUS_ITERATION_DO_WRITING ? "write" : "", timeout_milliseconds, socket_transport->read_watch, socket_transport->write_watch, _dbus_socket_printable (socket_transport->fd)); /* the passed in DO_READING/DO_WRITING flags indicate whether to * read/write messages, but regardless of those we may need to block * for reading/writing to do auth. But if we do reading for auth, * we don't want to read any messages yet if not given DO_READING. */ poll_fd.fd = _dbus_socket_get_pollable (socket_transport->fd); poll_fd.events = 0; if (_dbus_transport_try_to_authenticate (transport)) { /* This is kind of a hack; if we have stuff to write, then try * to avoid the poll. This is probably about a 5% speedup on an * echo client/server. * * If both reading and writing were requested, we want to avoid this * since it could have funky effects: * - both ends spinning waiting for the other one to read * data so they can finish writing * - prioritizing all writing ahead of reading */ if ((flags & DBUS_ITERATION_DO_WRITING) && !(flags & (DBUS_ITERATION_DO_READING | DBUS_ITERATION_BLOCK)) && !transport->disconnected && _dbus_connection_has_messages_to_send_unlocked (transport->connection)) { do_writing (transport); if (transport->disconnected || !_dbus_connection_has_messages_to_send_unlocked (transport->connection)) goto out; } /* If we get here, we decided to do the poll() after all */ _dbus_assert (socket_transport->read_watch); if (flags & DBUS_ITERATION_DO_READING) poll_fd.events |= _DBUS_POLLIN; _dbus_assert (socket_transport->write_watch); if (flags & DBUS_ITERATION_DO_WRITING) poll_fd.events |= _DBUS_POLLOUT; } else { DBusAuthState auth_state; auth_state = _dbus_auth_do_work (transport->auth); if (transport->receive_credentials_pending || auth_state == DBUS_AUTH_STATE_WAITING_FOR_INPUT) poll_fd.events |= _DBUS_POLLIN; if (transport->send_credentials_pending || auth_state == DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND) poll_fd.events |= _DBUS_POLLOUT; } if (poll_fd.events) { int saved_errno; if (flags & DBUS_ITERATION_BLOCK) poll_timeout = timeout_milliseconds; else poll_timeout = 0; /* For blocking selects we drop the connection lock here * to avoid blocking out connection access during a potentially * indefinite blocking call. The io path is still protected * by the io_path_cond condvar, so we won't reenter this. */ if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("unlock pre poll\n"); _dbus_connection_unlock (transport->connection); } again: poll_res = _dbus_poll (&poll_fd, 1, poll_timeout); saved_errno = _dbus_save_socket_errno (); if (poll_res < 0 && _dbus_get_is_errno_eintr (saved_errno)) goto again; if (flags & DBUS_ITERATION_BLOCK) { _dbus_verbose ("lock post poll\n"); _dbus_connection_lock (transport->connection); } if (poll_res >= 0) { if (poll_res == 0) poll_fd.revents = 0; /* some concern that posix does not guarantee this; * valgrind flags it as an error. though it probably * is guaranteed on linux at least. */ if (poll_fd.revents & _DBUS_POLLERR) do_io_error (transport); else { dbus_bool_t need_read = (poll_fd.revents & _DBUS_POLLIN) > 0; dbus_bool_t need_write = (poll_fd.revents & _DBUS_POLLOUT) > 0; dbus_bool_t authentication_completed; _dbus_verbose ("in iteration, need_read=%d need_write=%d\n", need_read, need_write); do_authentication (transport, need_read, need_write, &authentication_completed); /* See comment in socket_handle_watch. */ if (authentication_completed) goto out; if (need_read && (flags & DBUS_ITERATION_DO_READING)) do_reading (transport); if (need_write && (flags & DBUS_ITERATION_DO_WRITING)) do_writing (transport); } } else { _dbus_verbose ("Error from _dbus_poll(): %s\n", _dbus_strerror (saved_errno)); } } out: /* We need to install the write watch only if we did not * successfully write everything. Note we need to be careful that we * don't call check_write_watch *before* do_writing, since it's * inefficient to add the write watch, and we can avoid it most of * the time since we can write immediately. * * However, we MUST always call check_write_watch(); DBusConnection code * relies on the fact that running an iteration will notice that * messages are pending. */ check_write_watch (transport); _dbus_verbose (" ... leaving do_iteration()\n"); }