|
|
|
@ -45,6 +45,7 @@ |
|
|
|
|
#include <grpc/support/log.h> |
|
|
|
|
|
|
|
|
|
#include "src/core/lib/iomgr/iomgr.h" |
|
|
|
|
#include "src/core/lib/iomgr/workqueue.h" |
|
|
|
|
#include "test/core/util/test_config.h" |
|
|
|
|
|
|
|
|
|
typedef struct test_pollset { |
|
|
|
@ -60,6 +61,22 @@ typedef struct test_fd { |
|
|
|
|
/* num_fds should be an even number */ |
|
|
|
|
static void test_fd_init(test_fd *tfds, int *fds, int num_fds) { |
|
|
|
|
int i; |
|
|
|
|
int r; |
|
|
|
|
|
|
|
|
|
/* Create some dummy file descriptors. Currently using pipe file descriptors
|
|
|
|
|
* for this test but we could use any other type of file descriptors. Also, |
|
|
|
|
* since pipe() used in this test creates two fds in each call, num_fds should |
|
|
|
|
* be an even number */ |
|
|
|
|
GPR_ASSERT((num_fds % 2) == 0); |
|
|
|
|
for (i = 0; i < num_fds; i = i + 2) { |
|
|
|
|
r = pipe(fds + i); |
|
|
|
|
if (r != 0) { |
|
|
|
|
gpr_log(GPR_ERROR, "Error in creating pipe. %d (%s)", errno, |
|
|
|
|
strerror(errno)); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < num_fds; i++) { |
|
|
|
|
tfds[i].inner_fd = fds[i]; |
|
|
|
|
tfds[i].fd = grpc_fd_create(fds[i], "test_fd"); |
|
|
|
@ -111,8 +128,80 @@ static void test_pollset_cleanup(grpc_exec_ctx *exec_ctx, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#define NUM_FDS 8 |
|
|
|
|
#define NUM_POLLSETS 4 |
|
|
|
|
static void increment(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { |
|
|
|
|
++*(int *)arg; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Validate that merging two workqueues preserves the closures in each queue. |
|
|
|
|
* This is a regression test for a bug in |
|
|
|
|
* polling_island_merge()[ev_epoll_linux.c], where the parent relationship was |
|
|
|
|
* inverted. |
|
|
|
|
*/ |
|
|
|
|
static void test_pollset_queue_merge_items() { |
|
|
|
|
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; |
|
|
|
|
const int num_fds = 2; |
|
|
|
|
const int num_pollsets = 2; |
|
|
|
|
const int num_closures = 4; |
|
|
|
|
test_fd tfds[num_fds]; |
|
|
|
|
int fds[num_fds]; |
|
|
|
|
test_pollset pollsets[num_pollsets]; |
|
|
|
|
grpc_closure closures[num_closures]; |
|
|
|
|
int i; |
|
|
|
|
int result = 0; |
|
|
|
|
|
|
|
|
|
test_fd_init(tfds, fds, num_fds); |
|
|
|
|
test_pollset_init(pollsets, num_pollsets); |
|
|
|
|
|
|
|
|
|
/* Two distinct polling islands, each with their own FD and pollset. */ |
|
|
|
|
for (i = 0; i < num_fds; i++) { |
|
|
|
|
grpc_pollset_add_fd(&exec_ctx, pollsets[i].pollset, tfds[i].fd); |
|
|
|
|
grpc_exec_ctx_flush(&exec_ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Enqeue the closures, 3 to polling island 0 and 1 to polling island 1. */ |
|
|
|
|
grpc_closure_init( |
|
|
|
|
closures, increment, &result, |
|
|
|
|
grpc_workqueue_scheduler(grpc_fd_get_polling_island(tfds[0].fd))); |
|
|
|
|
grpc_closure_init( |
|
|
|
|
closures + 1, increment, &result, |
|
|
|
|
grpc_workqueue_scheduler(grpc_fd_get_polling_island(tfds[0].fd))); |
|
|
|
|
grpc_closure_init( |
|
|
|
|
closures + 2, increment, &result, |
|
|
|
|
grpc_workqueue_scheduler(grpc_fd_get_polling_island(tfds[0].fd))); |
|
|
|
|
grpc_closure_init( |
|
|
|
|
closures + 3, increment, &result, |
|
|
|
|
grpc_workqueue_scheduler(grpc_fd_get_polling_island(tfds[1].fd))); |
|
|
|
|
for (i = 0; i < num_closures; ++i) { |
|
|
|
|
grpc_closure_sched(&exec_ctx, closures + i, GRPC_ERROR_NONE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Merge the two polling islands. */ |
|
|
|
|
grpc_pollset_add_fd(&exec_ctx, pollsets[0].pollset, tfds[1].fd); |
|
|
|
|
grpc_exec_ctx_flush(&exec_ctx); |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Execute the closures, verify we see each one execute when executing work on |
|
|
|
|
* the merged polling island. |
|
|
|
|
*/ |
|
|
|
|
grpc_pollset_worker *worker = NULL; |
|
|
|
|
for (i = 0; i < num_closures; ++i) { |
|
|
|
|
const gpr_timespec deadline = gpr_time_add( |
|
|
|
|
gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(2, GPR_TIMESPAN)); |
|
|
|
|
gpr_mu_lock(pollsets[1].mu); |
|
|
|
|
GRPC_LOG_IF_ERROR( |
|
|
|
|
"grpc_pollset_work", |
|
|
|
|
grpc_pollset_work(&exec_ctx, pollsets[1].pollset, &worker, |
|
|
|
|
gpr_now(GPR_CLOCK_MONOTONIC), deadline)); |
|
|
|
|
gpr_mu_unlock(pollsets[1].mu); |
|
|
|
|
} |
|
|
|
|
GPR_ASSERT(result == num_closures); |
|
|
|
|
|
|
|
|
|
test_fd_cleanup(&exec_ctx, tfds, num_fds); |
|
|
|
|
test_pollset_cleanup(&exec_ctx, pollsets, num_pollsets); |
|
|
|
|
grpc_exec_ctx_finish(&exec_ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Cases to test: |
|
|
|
|
* case 1) Polling islands of both fd and pollset are NULL |
|
|
|
@ -125,28 +214,16 @@ static void test_pollset_cleanup(grpc_exec_ctx *exec_ctx, |
|
|
|
|
* */ |
|
|
|
|
static void test_add_fd_to_pollset() { |
|
|
|
|
grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; |
|
|
|
|
test_fd tfds[NUM_FDS]; |
|
|
|
|
int fds[NUM_FDS]; |
|
|
|
|
test_pollset pollsets[NUM_POLLSETS]; |
|
|
|
|
const int num_fds = 8; |
|
|
|
|
const int num_pollsets = 4; |
|
|
|
|
test_fd tfds[num_fds]; |
|
|
|
|
int fds[num_fds]; |
|
|
|
|
test_pollset pollsets[num_pollsets]; |
|
|
|
|
void *expected_pi = NULL; |
|
|
|
|
int i; |
|
|
|
|
int r; |
|
|
|
|
|
|
|
|
|
/* Create some dummy file descriptors. Currently using pipe file descriptors
|
|
|
|
|
* for this test but we could use any other type of file descriptors. Also, |
|
|
|
|
* since pipe() used in this test creates two fds in each call, NUM_FDS should |
|
|
|
|
* be an even number */ |
|
|
|
|
for (i = 0; i < NUM_FDS; i = i + 2) { |
|
|
|
|
r = pipe(fds + i); |
|
|
|
|
if (r != 0) { |
|
|
|
|
gpr_log(GPR_ERROR, "Error in creating pipe. %d (%s)", errno, |
|
|
|
|
strerror(errno)); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
test_fd_init(tfds, fds, NUM_FDS); |
|
|
|
|
test_pollset_init(pollsets, NUM_POLLSETS); |
|
|
|
|
test_fd_init(tfds, fds, num_fds); |
|
|
|
|
test_pollset_init(pollsets, num_pollsets); |
|
|
|
|
|
|
|
|
|
/*Step 1.
|
|
|
|
|
* Create three polling islands (This will exercise test case 1 and 2) with |
|
|
|
@ -207,19 +284,19 @@ static void test_add_fd_to_pollset() { |
|
|
|
|
|
|
|
|
|
/* Compare Fd:0's polling island with that of all other Fds */ |
|
|
|
|
expected_pi = grpc_fd_get_polling_island(tfds[0].fd); |
|
|
|
|
for (i = 1; i < NUM_FDS; i++) { |
|
|
|
|
for (i = 1; i < num_fds; i++) { |
|
|
|
|
GPR_ASSERT(grpc_are_polling_islands_equal( |
|
|
|
|
expected_pi, grpc_fd_get_polling_island(tfds[i].fd))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Compare Fd:0's polling island with that of all other pollsets */ |
|
|
|
|
for (i = 0; i < NUM_POLLSETS; i++) { |
|
|
|
|
for (i = 0; i < num_pollsets; i++) { |
|
|
|
|
GPR_ASSERT(grpc_are_polling_islands_equal( |
|
|
|
|
expected_pi, grpc_pollset_get_polling_island(pollsets[i].pollset))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
test_fd_cleanup(&exec_ctx, tfds, NUM_FDS); |
|
|
|
|
test_pollset_cleanup(&exec_ctx, pollsets, NUM_POLLSETS); |
|
|
|
|
test_fd_cleanup(&exec_ctx, tfds, num_fds); |
|
|
|
|
test_pollset_cleanup(&exec_ctx, pollsets, num_pollsets); |
|
|
|
|
grpc_exec_ctx_finish(&exec_ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -231,6 +308,7 @@ int main(int argc, char **argv) { |
|
|
|
|
poll_strategy = grpc_get_poll_strategy_name(); |
|
|
|
|
if (poll_strategy != NULL && strcmp(poll_strategy, "epoll") == 0) { |
|
|
|
|
test_add_fd_to_pollset(); |
|
|
|
|
test_pollset_queue_merge_items(); |
|
|
|
|
} else { |
|
|
|
|
gpr_log(GPR_INFO, |
|
|
|
|
"Skipping the test. The test is only relevant for 'epoll' " |
|
|
|
|