aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/iomgr/pollset_posix.c65
1 files changed, 64 insertions, 1 deletions
diff --git a/src/core/iomgr/pollset_posix.c b/src/core/iomgr/pollset_posix.c
index 87e7aa85ee..39945ff3cb 100644
--- a/src/core/iomgr/pollset_posix.c
+++ b/src/core/iomgr/pollset_posix.c
@@ -201,11 +201,43 @@ static void become_empty_pollset(grpc_pollset *pollset) {
* via poll()
*/
-static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) {
+
+typedef struct grpc_unary_promote_args {
+ const grpc_pollset_vtable *original_vtable;
+ grpc_pollset *pollset;
+ grpc_fd *fd;
+} grpc_unary_promote_args;
+
+static void unary_poll_do_promote(void *args, int success) {
+ grpc_unary_promote_args *up_args = args;
+ const grpc_pollset_vtable *original_vtable = up_args->original_vtable;
+ grpc_pollset *pollset = up_args->pollset;
+ grpc_fd *fd = up_args->fd;
grpc_fd *fds[2];
+ gpr_free(up_args);
+
+ gpr_mu_lock(&pollset->mu);
+ /* First we need to ensure that nobody is polling concurrently */
+ while (pollset->counter != 0 && pollset->vtable == original_vtable) {
+ grpc_pollset_kick(pollset);
+ gpr_cv_wait(&pollset->cv, &pollset->mu, gpr_inf_future);
+ }
+ /* At this point the pollset may no longer be a unary poller. In that case
+ * we should just call the right add function and be done. */
+ /* TODO(klempner): If we're not careful this could cause infinite recursion.
+ * That's not a problem for now because empty_pollset has a trivial poller
+ * and we don't have any mechanism to unbecome multipoller. */
+ if (pollset->vtable != original_vtable) {
+ pollset->vtable->add_fd(pollset, fd);
+ gpr_cv_broadcast(&pollset->cv);
+ gpr_mu_unlock(&pollset->mu);
+ return;
+ }
+
if (fd == pollset->data.ptr) return;
fds[0] = pollset->data.ptr;
fds[1] = fd;
+
if (!grpc_fd_is_orphaned(fds[0])) {
grpc_platform_become_multipoller(pollset, fds, GPR_ARRAY_SIZE(fds));
grpc_fd_unref(fds[0]);
@@ -216,6 +248,37 @@ static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) {
pollset->data.ptr = fd;
grpc_fd_ref(fd);
}
+
+ gpr_cv_broadcast(&pollset->cv);
+ gpr_mu_unlock(&pollset->mu);
+
+ /* Matching ref in unary_poll_pollset_add_fd */
+ grpc_fd_unref(fd);
+}
+
+static void unary_poll_pollset_add_fd(grpc_pollset *pollset, grpc_fd *fd) {
+ grpc_unary_promote_args *up_args;
+ if (fd == pollset->data.ptr) return;
+
+ if (grpc_fd_is_orphaned(pollset->data.ptr)) {
+ /* old fd is orphaned and we haven't cleaned it up until now, so remain a
+ * unary poller */
+ grpc_fd_unref(pollset->data.ptr);
+ pollset->data.ptr = fd;
+ grpc_fd_ref(fd);
+ return;
+ }
+
+ /* Now we need to promote. This needs to happen when we're not polling. Since
+ * this may be called from poll, the wait needs to happen asynchronously. */
+ grpc_fd_ref(fd);
+ up_args = gpr_malloc(sizeof(*up_args));
+ up_args->pollset = pollset;
+ up_args->fd = fd;
+ up_args->original_vtable = pollset->vtable;
+ grpc_iomgr_add_callback(unary_poll_do_promote, up_args);
+
+ grpc_pollset_kick(pollset);
}
static void unary_poll_pollset_del_fd(grpc_pollset *pollset, grpc_fd *fd) {