|
|
|
@ -237,6 +237,19 @@ struct grpc_pollset_set { |
|
|
|
|
grpc_fd **fds; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* Common helpers |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
static void append_error(grpc_error **composite, grpc_error *error, |
|
|
|
|
const char *desc) { |
|
|
|
|
if (error == GRPC_ERROR_NONE) return; |
|
|
|
|
if (*composite == GRPC_ERROR_NONE) { |
|
|
|
|
*composite = GRPC_ERROR_CREATE(desc); |
|
|
|
|
} |
|
|
|
|
*composite = grpc_error_add_child(*composite, error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*******************************************************************************
|
|
|
|
|
* Polling island Definitions |
|
|
|
|
*/ |
|
|
|
@ -316,10 +329,13 @@ long pi_unref(polling_island *pi, int ref_cnt) { |
|
|
|
|
|
|
|
|
|
/* The caller is expected to hold pi->mu lock before calling this function */ |
|
|
|
|
static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, |
|
|
|
|
size_t fd_count, bool add_fd_refs) { |
|
|
|
|
size_t fd_count, bool add_fd_refs, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
int err; |
|
|
|
|
size_t i; |
|
|
|
|
struct epoll_event ev; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "polling_island_add_fds"; |
|
|
|
|
|
|
|
|
|
#ifdef GRPC_TSAN |
|
|
|
|
/* See the definition of g_epoll_sync for more context */ |
|
|
|
@ -333,10 +349,12 @@ static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, |
|
|
|
|
|
|
|
|
|
if (err < 0) { |
|
|
|
|
if (errno != EEXIST) { |
|
|
|
|
/* TODO: sreek - We need a better way to bubble up this error instead of
|
|
|
|
|
just logging a message */ |
|
|
|
|
gpr_log(GPR_ERROR, "epoll_ctl add for fd: %d failed with error: %s", |
|
|
|
|
fds[i]->fd, strerror(errno)); |
|
|
|
|
gpr_asprintf( |
|
|
|
|
&err_msg, |
|
|
|
|
"epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)", |
|
|
|
|
pi->epoll_fd, fds[i]->fd, errno, strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
gpr_free(err_msg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
continue; |
|
|
|
@ -356,37 +374,47 @@ static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, |
|
|
|
|
|
|
|
|
|
/* The caller is expected to hold pi->mu before calling this */ |
|
|
|
|
static void polling_island_add_wakeup_fd_locked(polling_island *pi, |
|
|
|
|
grpc_wakeup_fd *wakeup_fd) { |
|
|
|
|
grpc_wakeup_fd *wakeup_fd, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
struct epoll_event ev; |
|
|
|
|
int err; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "polling_island_add_wakeup_fd"; |
|
|
|
|
|
|
|
|
|
ev.events = (uint32_t)(EPOLLIN | EPOLLET); |
|
|
|
|
ev.data.ptr = wakeup_fd; |
|
|
|
|
err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, |
|
|
|
|
GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev); |
|
|
|
|
if (err < 0) { |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"Failed to add grpc_wake_up_fd (%d) to the epoll set (epoll_fd: %d)" |
|
|
|
|
". Error: %s", |
|
|
|
|
GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), pi->epoll_fd, |
|
|
|
|
strerror(errno)); |
|
|
|
|
if (err < 0 && errno != EEXIST) { |
|
|
|
|
gpr_asprintf(&err_msg, |
|
|
|
|
"epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " |
|
|
|
|
"error: %d (%s)", |
|
|
|
|
pi->epoll_fd, |
|
|
|
|
GRPC_WAKEUP_FD_GET_READ_FD(&grpc_global_wakeup_fd), errno, |
|
|
|
|
strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
gpr_free(err_msg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* The caller is expected to hold pi->mu lock before calling this function */ |
|
|
|
|
static void polling_island_remove_all_fds_locked(polling_island *pi, |
|
|
|
|
bool remove_fd_refs) { |
|
|
|
|
bool remove_fd_refs, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
int err; |
|
|
|
|
size_t i; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "polling_island_remove_fds"; |
|
|
|
|
|
|
|
|
|
for (i = 0; i < pi->fd_cnt; i++) { |
|
|
|
|
err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL); |
|
|
|
|
if (err < 0 && errno != ENOENT) { |
|
|
|
|
/* TODO: sreek - We need a better way to bubble up this error instead of
|
|
|
|
|
* just logging a message */ |
|
|
|
|
gpr_log(GPR_ERROR, |
|
|
|
|
"epoll_ctl deleting fds[%zu]: %d failed with error: %s", i, |
|
|
|
|
pi->fds[i]->fd, strerror(errno)); |
|
|
|
|
gpr_asprintf(&err_msg, |
|
|
|
|
"epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with " |
|
|
|
|
"error: %d (%s)", |
|
|
|
|
pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
gpr_free(err_msg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (remove_fd_refs) { |
|
|
|
@ -399,17 +427,24 @@ static void polling_island_remove_all_fds_locked(polling_island *pi, |
|
|
|
|
|
|
|
|
|
/* The caller is expected to hold pi->mu lock before calling this function */ |
|
|
|
|
static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd, |
|
|
|
|
bool is_fd_closed) { |
|
|
|
|
bool is_fd_closed, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
int err; |
|
|
|
|
size_t i; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "polling_island_remove_fd"; |
|
|
|
|
|
|
|
|
|
/* If fd is already closed, then it would have been automatically been removed
|
|
|
|
|
from the epoll set */ |
|
|
|
|
if (!is_fd_closed) { |
|
|
|
|
err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); |
|
|
|
|
if (err < 0 && errno != ENOENT) { |
|
|
|
|
gpr_log(GPR_ERROR, "epoll_ctl deleting fd: %d failed with error; %s", |
|
|
|
|
fd->fd, strerror(errno)); |
|
|
|
|
gpr_asprintf( |
|
|
|
|
&err_msg, |
|
|
|
|
"epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)", |
|
|
|
|
pi->epoll_fd, fd->fd, errno, strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
gpr_free(err_msg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -422,8 +457,12 @@ static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static polling_island *polling_island_create(grpc_fd *initial_fd) { |
|
|
|
|
/* Might return NULL in case of an error */ |
|
|
|
|
static polling_island *polling_island_create(grpc_fd *initial_fd, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
polling_island *pi = NULL; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "polling_island_create"; |
|
|
|
|
|
|
|
|
|
/* Try to get one from the polling island freelist */ |
|
|
|
|
gpr_mu_lock(&g_pi_freelist_mu); |
|
|
|
@ -449,22 +488,22 @@ static polling_island *polling_island_create(grpc_fd *initial_fd) { |
|
|
|
|
pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC); |
|
|
|
|
|
|
|
|
|
if (pi->epoll_fd < 0) { |
|
|
|
|
gpr_log(GPR_ERROR, "epoll_create1() failed with error: %s", |
|
|
|
|
strerror(errno)); |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(pi->epoll_fd >= 0); |
|
|
|
|
|
|
|
|
|
polling_island_add_wakeup_fd_locked(pi, &grpc_global_wakeup_fd); |
|
|
|
|
|
|
|
|
|
pi->next_free = NULL; |
|
|
|
|
gpr_asprintf(&err_msg, "epoll_create1 failed with error %d (%s)", errno, |
|
|
|
|
strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
gpr_free(err_msg); |
|
|
|
|
} else { |
|
|
|
|
polling_island_add_wakeup_fd_locked(pi, &grpc_global_wakeup_fd, error); |
|
|
|
|
pi->next_free = NULL; |
|
|
|
|
|
|
|
|
|
if (initial_fd != NULL) { |
|
|
|
|
/* Lock the polling island here just in case we got this structure from the
|
|
|
|
|
freelist and the polling island lock was not released yet (by the code |
|
|
|
|
that adds the polling island to the freelist) */ |
|
|
|
|
gpr_mu_lock(&pi->mu); |
|
|
|
|
polling_island_add_fds_locked(pi, &initial_fd, 1, true); |
|
|
|
|
gpr_mu_unlock(&pi->mu); |
|
|
|
|
if (initial_fd != NULL) { |
|
|
|
|
/* Lock the polling island here just in case we got this structure from
|
|
|
|
|
the freelist and the polling island lock was not released yet (by the |
|
|
|
|
code that adds the polling island to the freelist) */ |
|
|
|
|
gpr_mu_lock(&pi->mu); |
|
|
|
|
polling_island_add_fds_locked(pi, &initial_fd, 1, true, error); |
|
|
|
|
gpr_mu_unlock(&pi->mu); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return pi; |
|
|
|
@ -534,7 +573,9 @@ static polling_island *polling_island_lock(polling_island *pi) { |
|
|
|
|
return pi; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Gets the lock on the *latest* polling islands pointed by *p and *q.
|
|
|
|
|
/* Gets the lock on the *latest* polling islands in the linked lists pointed by
|
|
|
|
|
*p and *q (and also updates *p and *q to point to the latest polling islands) |
|
|
|
|
|
|
|
|
|
This function is needed because calling the following block of code to obtain |
|
|
|
|
locks on polling islands (*p and *q) is prone to deadlocks. |
|
|
|
|
{ |
|
|
|
@ -550,18 +591,8 @@ static polling_island *polling_island_lock(polling_island *pi) { |
|
|
|
|
.. |
|
|
|
|
.. Critical section with both p1 and p2 locked |
|
|
|
|
.. |
|
|
|
|
// Release locks
|
|
|
|
|
// **IMPORTANT**: Make sure you check p1 == p2 AFTER the function
|
|
|
|
|
// polling_island_lock_pair() was called and if so, release the lock only
|
|
|
|
|
// once. Note: Even if p1 != p2 beforec calling polling_island_lock_pair(),
|
|
|
|
|
// they might be after the function returns:
|
|
|
|
|
if (p1 == p2) { |
|
|
|
|
gpr_mu_unlock(&p1->mu) |
|
|
|
|
} else { |
|
|
|
|
gpr_mu_unlock(&p1->mu); |
|
|
|
|
gpr_mu_unlock(&p2->mu); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Release locks: Always call polling_island_unlock_pair() to release locks
|
|
|
|
|
polling_island_unlock_pair(p1, p2); |
|
|
|
|
*/ |
|
|
|
|
static void polling_island_lock_pair(polling_island **p, polling_island **q) { |
|
|
|
|
polling_island *pi_1 = *p; |
|
|
|
@ -623,39 +654,46 @@ static void polling_island_lock_pair(polling_island **p, polling_island **q) { |
|
|
|
|
*q = pi_2; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static polling_island *polling_island_merge(polling_island *p, |
|
|
|
|
polling_island *q) { |
|
|
|
|
/* Get locks on both the polling islands */ |
|
|
|
|
polling_island_lock_pair(&p, &q); |
|
|
|
|
|
|
|
|
|
static void polling_island_unlock_pair(polling_island *p, polling_island *q) { |
|
|
|
|
if (p == q) { |
|
|
|
|
/* Nothing needs to be done here */ |
|
|
|
|
gpr_mu_unlock(&p->mu); |
|
|
|
|
return p; |
|
|
|
|
} else { |
|
|
|
|
gpr_mu_unlock(&p->mu); |
|
|
|
|
gpr_mu_unlock(&q->mu); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Make sure that p points to the polling island with fewer fds than q */ |
|
|
|
|
if (p->fd_cnt > q->fd_cnt) { |
|
|
|
|
GPR_SWAP(polling_island *, p, q); |
|
|
|
|
} |
|
|
|
|
static polling_island *polling_island_merge(polling_island *p, |
|
|
|
|
polling_island *q, |
|
|
|
|
grpc_error **error) { |
|
|
|
|
/* Get locks on both the polling islands */ |
|
|
|
|
polling_island_lock_pair(&p, &q); |
|
|
|
|
|
|
|
|
|
/* "Merge" p with q i.e move all the fds from p (The one with fewer fds) to q
|
|
|
|
|
Note that the refcounts on the fds being moved will not change here. This |
|
|
|
|
is why the last parameter in the following two functions is 'false') */ |
|
|
|
|
polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false); |
|
|
|
|
polling_island_remove_all_fds_locked(p, false); |
|
|
|
|
if (p != q) { |
|
|
|
|
/* Make sure that p points to the polling island with fewer fds than q */ |
|
|
|
|
if (p->fd_cnt > q->fd_cnt) { |
|
|
|
|
GPR_SWAP(polling_island *, p, q); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Merge p with q i.e move all the fds from p (The one with fewer fds) to q
|
|
|
|
|
Note that the refcounts on the fds being moved will not change here. |
|
|
|
|
This is why the last param in the following two functions is 'false') */ |
|
|
|
|
polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error); |
|
|
|
|
polling_island_remove_all_fds_locked(p, false, error); |
|
|
|
|
|
|
|
|
|
/* Wakeup all the pollers (if any) on p so that they can pickup this change */ |
|
|
|
|
polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd); |
|
|
|
|
/* Wakeup all the pollers (if any) on p so that they pickup this change */ |
|
|
|
|
polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error); |
|
|
|
|
|
|
|
|
|
/* Add the 'merged_to' link from p --> q */ |
|
|
|
|
gpr_atm_rel_store(&p->merged_to, (gpr_atm)q); |
|
|
|
|
PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */ |
|
|
|
|
/* Add the 'merged_to' link from p --> q */ |
|
|
|
|
gpr_atm_rel_store(&p->merged_to, (gpr_atm)q); |
|
|
|
|
PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */ |
|
|
|
|
} |
|
|
|
|
/* else if p == q, nothing needs to be done */ |
|
|
|
|
|
|
|
|
|
gpr_mu_unlock(&p->mu); |
|
|
|
|
gpr_mu_unlock(&q->mu); |
|
|
|
|
polling_island_unlock_pair(p, q); |
|
|
|
|
|
|
|
|
|
/* Return the merged polling island */ |
|
|
|
|
/* Return the merged polling island (Note that no merge would have happened
|
|
|
|
|
if p == q which is ok) */ |
|
|
|
|
return q; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -853,6 +891,8 @@ static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, |
|
|
|
|
grpc_closure *on_done, int *release_fd, |
|
|
|
|
const char *reason) { |
|
|
|
|
bool is_fd_closed = false; |
|
|
|
|
grpc_error *error = GRPC_ERROR_NONE; |
|
|
|
|
|
|
|
|
|
gpr_mu_lock(&fd->mu); |
|
|
|
|
fd->on_done_closure = on_done; |
|
|
|
|
|
|
|
|
@ -882,7 +922,7 @@ static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, |
|
|
|
|
gpr_mu_lock(&fd->pi_mu); |
|
|
|
|
if (fd->polling_island != NULL) { |
|
|
|
|
polling_island *pi_latest = polling_island_lock(fd->polling_island); |
|
|
|
|
polling_island_remove_fd_locked(pi_latest, fd, is_fd_closed); |
|
|
|
|
polling_island_remove_fd_locked(pi_latest, fd, is_fd_closed, &error); |
|
|
|
|
gpr_mu_unlock(&pi_latest->mu); |
|
|
|
|
|
|
|
|
|
PI_UNREF(fd->polling_island, "fd_orphan"); |
|
|
|
@ -890,10 +930,11 @@ static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, |
|
|
|
|
} |
|
|
|
|
gpr_mu_unlock(&fd->pi_mu); |
|
|
|
|
|
|
|
|
|
grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE, NULL); |
|
|
|
|
grpc_exec_ctx_sched(exec_ctx, fd->on_done_closure, error, NULL); |
|
|
|
|
|
|
|
|
|
gpr_mu_unlock(&fd->mu); |
|
|
|
|
UNREF_BY(fd, 2, reason); /* Drop the reference */ |
|
|
|
|
GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static grpc_error *fd_shutdown_error(bool shutdown) { |
|
|
|
@ -1062,19 +1103,12 @@ static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { |
|
|
|
|
worker->prev->next = worker->next->prev = worker; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void kick_append_error(grpc_error **composite, grpc_error *error) { |
|
|
|
|
if (error == GRPC_ERROR_NONE) return; |
|
|
|
|
if (*composite == GRPC_ERROR_NONE) { |
|
|
|
|
*composite = GRPC_ERROR_CREATE("Kick Failure"); |
|
|
|
|
} |
|
|
|
|
*composite = grpc_error_add_child(*composite, error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* p->mu must be held before calling this function */ |
|
|
|
|
static grpc_error *pollset_kick(grpc_pollset *p, |
|
|
|
|
grpc_pollset_worker *specific_worker) { |
|
|
|
|
GPR_TIMER_BEGIN("pollset_kick", 0); |
|
|
|
|
grpc_error *error = GRPC_ERROR_NONE; |
|
|
|
|
const char *err_desc = "Kick Failure"; |
|
|
|
|
|
|
|
|
|
grpc_pollset_worker *worker = specific_worker; |
|
|
|
|
if (worker != NULL) { |
|
|
|
@ -1084,7 +1118,7 @@ static grpc_error *pollset_kick(grpc_pollset *p, |
|
|
|
|
for (worker = p->root_worker.next; worker != &p->root_worker; |
|
|
|
|
worker = worker->next) { |
|
|
|
|
if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { |
|
|
|
|
kick_append_error(&error, pollset_worker_kick(worker)); |
|
|
|
|
append_error(&error, pollset_worker_kick(worker), err_desc); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
@ -1094,7 +1128,7 @@ static grpc_error *pollset_kick(grpc_pollset *p, |
|
|
|
|
} else { |
|
|
|
|
GPR_TIMER_MARK("kicked_specifically", 0); |
|
|
|
|
if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { |
|
|
|
|
kick_append_error(&error, pollset_worker_kick(worker)); |
|
|
|
|
append_error(&error, pollset_worker_kick(worker), err_desc); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) { |
|
|
|
@ -1110,7 +1144,7 @@ static grpc_error *pollset_kick(grpc_pollset *p, |
|
|
|
|
if (worker != NULL) { |
|
|
|
|
GPR_TIMER_MARK("finally_kick", 0); |
|
|
|
|
push_back_worker(p, worker); |
|
|
|
|
kick_append_error(&error, pollset_worker_kick(worker)); |
|
|
|
|
append_error(&error, pollset_worker_kick(worker), err_desc); |
|
|
|
|
} else { |
|
|
|
|
GPR_TIMER_MARK("kicked_no_pollers", 0); |
|
|
|
|
p->kicked_without_pollers = true; |
|
|
|
@ -1238,23 +1272,17 @@ static void pollset_reset(grpc_pollset *pollset) { |
|
|
|
|
pollset_release_polling_island(pollset, "ps_reset"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void work_combine_error(grpc_error **composite, grpc_error *error) { |
|
|
|
|
if (error == GRPC_ERROR_NONE) return; |
|
|
|
|
if (*composite == GRPC_ERROR_NONE) { |
|
|
|
|
*composite = GRPC_ERROR_CREATE("pollset_work"); |
|
|
|
|
} |
|
|
|
|
*composite = grpc_error_add_child(*composite, error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#define GRPC_EPOLL_MAX_EVENTS 1000 |
|
|
|
|
static grpc_error *pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_pollset *pollset, |
|
|
|
|
int timeout_ms, sigset_t *sig_mask) { |
|
|
|
|
/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */ |
|
|
|
|
static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
grpc_pollset *pollset, int timeout_ms, |
|
|
|
|
sigset_t *sig_mask, grpc_error **error) { |
|
|
|
|
struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; |
|
|
|
|
int epoll_fd = -1; |
|
|
|
|
int ep_rv; |
|
|
|
|
polling_island *pi = NULL; |
|
|
|
|
grpc_error *error = GRPC_ERROR_NONE; |
|
|
|
|
char *err_msg; |
|
|
|
|
const char *err_desc = "pollset_work_and_unlock"; |
|
|
|
|
GPR_TIMER_BEGIN("pollset_work_and_unlock", 0); |
|
|
|
|
|
|
|
|
|
/* We need to get the epoll_fd to wait on. The epoll_fd is in inside the
|
|
|
|
@ -1265,11 +1293,15 @@ static grpc_error *pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
which we got the epoll_fd) got merged with another island while we are |
|
|
|
|
in this function. This is still okay because in such a case, we will wakeup |
|
|
|
|
right-away from epoll_wait() and pick up the latest polling_island the next |
|
|
|
|
this function (i.e pollset_work_and_unlock()) is called. |
|
|
|
|
*/ |
|
|
|
|
this function (i.e pollset_work_and_unlock()) is called */ |
|
|
|
|
|
|
|
|
|
if (pollset->polling_island == NULL) { |
|
|
|
|
pollset->polling_island = polling_island_create(NULL); |
|
|
|
|
pollset->polling_island = polling_island_create(NULL, error); |
|
|
|
|
if (pollset->polling_island == NULL) { |
|
|
|
|
GPR_TIMER_END("pollset_work_and_unlock", 0); |
|
|
|
|
return; /* Fatal error. We cannot continue */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PI_ADD_REF(pollset->polling_island, "ps"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1297,8 +1329,10 @@ static grpc_error *pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
sig_mask); |
|
|
|
|
if (ep_rv < 0) { |
|
|
|
|
if (errno != EINTR) { |
|
|
|
|
gpr_log(GPR_ERROR, "epoll_pwait() failed: %s", strerror(errno)); |
|
|
|
|
work_combine_error(&error, GRPC_OS_ERROR(errno, "epoll_pwait")); |
|
|
|
|
gpr_asprintf(&err_msg, |
|
|
|
|
"epoll_wait() epoll fd: %d failed with error: %d (%s)", |
|
|
|
|
epoll_fd, errno, strerror(errno)); |
|
|
|
|
append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); |
|
|
|
|
} else { |
|
|
|
|
/* We were interrupted. Save an interation by doing a zero timeout
|
|
|
|
|
epoll_wait to see if there are any other events of interest */ |
|
|
|
@ -1314,8 +1348,9 @@ static grpc_error *pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
for (int i = 0; i < ep_rv; ++i) { |
|
|
|
|
void *data_ptr = ep_ev[i].data.ptr; |
|
|
|
|
if (data_ptr == &grpc_global_wakeup_fd) { |
|
|
|
|
work_combine_error( |
|
|
|
|
&error, grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd)); |
|
|
|
|
append_error(error, |
|
|
|
|
grpc_wakeup_fd_consume_wakeup(&grpc_global_wakeup_fd), |
|
|
|
|
err_desc); |
|
|
|
|
} else if (data_ptr == &polling_island_wakeup_fd) { |
|
|
|
|
/* This means that our polling island is merged with a different
|
|
|
|
|
island. We do not have to do anything here since the subsequent call |
|
|
|
@ -1346,7 +1381,6 @@ static grpc_error *pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, |
|
|
|
|
PI_UNREF(pi, "ps_work"); |
|
|
|
|
|
|
|
|
|
GPR_TIMER_END("pollset_work_and_unlock", 0); |
|
|
|
|
return error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* pollset->mu lock must be held by the caller before calling this.
|
|
|
|
@ -1368,6 +1402,7 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, |
|
|
|
|
worker.pt_id = pthread_self(); |
|
|
|
|
|
|
|
|
|
*worker_hdl = &worker; |
|
|
|
|
|
|
|
|
|
gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); |
|
|
|
|
gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); |
|
|
|
|
|
|
|
|
@ -1379,14 +1414,37 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, |
|
|
|
|
GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); |
|
|
|
|
pollset->kicked_without_pollers = 0; |
|
|
|
|
} else if (!pollset->shutting_down) { |
|
|
|
|
/* We use the posix-signal with number 'grpc_wakeup_signal' for waking up
|
|
|
|
|
(i.e 'kicking') a worker in the pollset. |
|
|
|
|
A 'kick' is a way to inform that worker that there is some pending work |
|
|
|
|
that needs immediate attention (like an event on the completion queue, |
|
|
|
|
or a polling island merge that results in a new epoll-fd to wait on) and |
|
|
|
|
that the worker should not spend time waiting in epoll_pwait(). |
|
|
|
|
|
|
|
|
|
A kick can come at anytime (i.e before/during or after the worker calls |
|
|
|
|
epoll_pwait()) but in all cases we have to make sure that when a worker |
|
|
|
|
gets a kick, it does not spend time in epoll_pwait(). In other words, one |
|
|
|
|
kick should result in skipping/exiting of one epoll_pwait(); |
|
|
|
|
|
|
|
|
|
To accomplish this, we mask 'grpc_wakeup_signal' on this worker at all |
|
|
|
|
times *except* when it is in epoll_pwait(). This way, the worker never |
|
|
|
|
misses acting on a kick */ |
|
|
|
|
|
|
|
|
|
sigemptyset(&new_mask); |
|
|
|
|
sigaddset(&new_mask, grpc_wakeup_signal); |
|
|
|
|
pthread_sigmask(SIG_BLOCK, &new_mask, &orig_mask); |
|
|
|
|
sigdelset(&orig_mask, grpc_wakeup_signal); |
|
|
|
|
/* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'. This is
|
|
|
|
|
the mask used at all times *except during epoll_wait()*" |
|
|
|
|
orig_mask: The thread mask which allows 'grpc_wakeup_signal' and this is |
|
|
|
|
the mask to use *during epoll_wait()* |
|
|
|
|
|
|
|
|
|
The new_mask is set on the worker before it is added to the pollset (i.e |
|
|
|
|
before it can be kicked) */ |
|
|
|
|
|
|
|
|
|
push_front_worker(pollset, &worker); |
|
|
|
|
push_front_worker(pollset, &worker); /* Add worker to pollset */ |
|
|
|
|
|
|
|
|
|
error = pollset_work_and_unlock(exec_ctx, pollset, timeout_ms, &orig_mask); |
|
|
|
|
pollset_work_and_unlock(exec_ctx, pollset, timeout_ms, &orig_mask, &error); |
|
|
|
|
grpc_exec_ctx_flush(exec_ctx); |
|
|
|
|
|
|
|
|
|
gpr_mu_lock(&pollset->mu); |
|
|
|
@ -1412,15 +1470,20 @@ static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
*worker_hdl = NULL; |
|
|
|
|
|
|
|
|
|
gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); |
|
|
|
|
gpr_tls_set(&g_current_thread_worker, (intptr_t)0); |
|
|
|
|
|
|
|
|
|
GPR_TIMER_END("pollset_work", 0); |
|
|
|
|
|
|
|
|
|
GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); |
|
|
|
|
return error; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, |
|
|
|
|
grpc_fd *fd) { |
|
|
|
|
grpc_error *error = GRPC_ERROR_NONE; |
|
|
|
|
|
|
|
|
|
gpr_mu_lock(&pollset->mu); |
|
|
|
|
gpr_mu_lock(&fd->pi_mu); |
|
|
|
|
|
|
|
|
@ -1443,19 +1506,23 @@ static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, |
|
|
|
|
if (fd->polling_island == pollset->polling_island) { |
|
|
|
|
pi_new = fd->polling_island; |
|
|
|
|
if (pi_new == NULL) { |
|
|
|
|
pi_new = polling_island_create(fd); |
|
|
|
|
pi_new = polling_island_create(fd, &error); |
|
|
|
|
} |
|
|
|
|
} else if (fd->polling_island == NULL) { |
|
|
|
|
pi_new = polling_island_lock(pollset->polling_island); |
|
|
|
|
polling_island_add_fds_locked(pi_new, &fd, 1, true); |
|
|
|
|
polling_island_add_fds_locked(pi_new, &fd, 1, true, &error); |
|
|
|
|
gpr_mu_unlock(&pi_new->mu); |
|
|
|
|
} else if (pollset->polling_island == NULL) { |
|
|
|
|
pi_new = polling_island_lock(fd->polling_island); |
|
|
|
|
gpr_mu_unlock(&pi_new->mu); |
|
|
|
|
} else { |
|
|
|
|
pi_new = polling_island_merge(fd->polling_island, pollset->polling_island); |
|
|
|
|
pi_new = polling_island_merge(fd->polling_island, pollset->polling_island, |
|
|
|
|
&error); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* At this point, pi_new is the polling island that both fd->polling_island
|
|
|
|
|
and pollset->polling_island must be pointing to */ |
|
|
|
|
|
|
|
|
|
if (fd->polling_island != pi_new) { |
|
|
|
|
PI_ADD_REF(pi_new, "fd"); |
|
|
|
|
if (fd->polling_island != NULL) { |
|
|
|
@ -1645,13 +1712,10 @@ bool grpc_are_polling_islands_equal(void *p, void *q) { |
|
|
|
|
polling_island *p1 = p; |
|
|
|
|
polling_island *p2 = q; |
|
|
|
|
|
|
|
|
|
/* Note: polling_island_lock_pair() may change p1 and p2 to point to the
|
|
|
|
|
latest polling islands in their respective linked lists */ |
|
|
|
|
polling_island_lock_pair(&p1, &p2); |
|
|
|
|
if (p1 == p2) { |
|
|
|
|
gpr_mu_unlock(&p1->mu); |
|
|
|
|
} else { |
|
|
|
|
gpr_mu_unlock(&p1->mu); |
|
|
|
|
gpr_mu_unlock(&p2->mu); |
|
|
|
|
} |
|
|
|
|
polling_island_unlock_pair(p1, p2); |
|
|
|
|
|
|
|
|
|
return p1 == p2; |
|
|
|
|
} |
|
|
|
|