From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 681C43059E for ; Sat, 1 Jun 2019 20:00:27 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 2xwIZ2vjZ2YD for ; Sat, 1 Jun 2019 20:00:27 -0400 (EDT) Received: from smtp51.i.mail.ru (smtp51.i.mail.ru [94.100.177.111]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 8F73030599 for ; Sat, 1 Jun 2019 20:00:26 -0400 (EDT) From: Vladislav Shpilevoy Subject: [tarantool-patches] [PATCH 1/5] test: create isolated ev_loop for swim unit tests Date: Sun, 2 Jun 2019 02:00:17 +0200 Message-Id: <50a84d1ffdd25646894e576ef4ff19195aaf769e.1559433539.git.v.shpilevoy@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.org Cc: kostja@tarantool.org The SWIM unit tests code with the fake events and time does lots of forbidden things: it manually invokes pending watcher callbacks; manages global time without a kernel; puts not existing descriptors into the loop. These foul blows open the gates to the full control over IO events, descriptors, virtual time. Hundreds of virtual seconds pass in milliseconds in reality, it makes SWIM unit tests fast despite complex logic. All these actions does not affect the loop until yield. On yield a scheduler fiber wakes up and 1) infinitely generates EV_READ on not existing descriptors because a kernel considers them closed; 2) manual pending callbacks invocation asserts, because it is not allowed for non-scheduler fibers. To avoid these problems a new isolated loop is created, not visible for the scheduler. Here the fake events library can rack and ruin whatever it wants. Needed for #4250 --- src/lib/swim/swim.c | 21 ++++++++++++--------- src/lib/swim/swim_ev.c | 6 ++++++ src/lib/swim/swim_ev.h | 3 +++ src/lib/swim/swim_io.c | 15 ++++++++------- test/unit/swim_test_ev.c | 24 ++++++++++++++++++++++++ test/unit/swim_test_utils.c | 2 +- 6 files changed, 54 insertions(+), 17 deletions(-) diff --git a/src/lib/swim/swim.c b/src/lib/swim/swim.c index f7a885b76..46b76731d 100644 --- a/src/lib/swim/swim.c +++ b/src/lib/swim/swim.c @@ -519,7 +519,7 @@ swim_wait_ack(struct swim *swim, struct swim_member *member, timeout *= 2; member->ping_deadline = swim_time() + timeout; wait_ack_heap_insert(&swim->wait_ack_heap, member); - swim_ev_timer_again(loop(), &swim->wait_ack_tick); + swim_ev_timer_again(swim_loop(), &swim->wait_ack_tick); } } @@ -802,7 +802,7 @@ swim_new_member(struct swim *swim, const struct sockaddr_in *addr, return NULL; } if (mh_size(swim->members) > 1) - swim_ev_timer_again(loop(), &swim->round_tick); + swim_ev_timer_again(swim_loop(), &swim->round_tick); /* Dissemination component. */ swim_on_member_update(swim, member); @@ -1122,7 +1122,7 @@ swim_complete_step(struct swim_task *task, * It could be stopped by the step begin function, if the * sending was too long. */ - swim_ev_timer_again(loop(), &swim->round_tick); + swim_ev_timer_again(swim_loop(), &swim->round_tick); /* * It is possible that the original member was deleted * manually during the task execution. @@ -1813,16 +1813,17 @@ swim_cfg(struct swim *swim, const char *uri, double heartbeat_rate, addr = swim->self->addr; } struct ev_timer *t = &swim->round_tick; + struct ev_loop *l = swim_loop(); if (t->repeat != heartbeat_rate && heartbeat_rate > 0) { swim_ev_timer_set(t, 0, heartbeat_rate); if (swim_ev_is_active(t)) - swim_ev_timer_again(loop(), t); + swim_ev_timer_again(l, t); } t = &swim->wait_ack_tick; if (t->repeat != ack_timeout && ack_timeout > 0) { swim_ev_timer_set(t, 0, ack_timeout); if (swim_ev_is_active(t)) - swim_ev_timer_again(loop(), t); + swim_ev_timer_again(l, t); } if (new_self != NULL) { @@ -1953,9 +1954,10 @@ swim_size(const struct swim *swim) void swim_delete(struct swim *swim) { + struct ev_loop *l = swim_loop(); swim_scheduler_destroy(&swim->scheduler); - swim_ev_timer_stop(loop(), &swim->round_tick); - swim_ev_timer_stop(loop(), &swim->wait_ack_tick); + swim_ev_timer_stop(l, &swim->round_tick); + swim_ev_timer_stop(l, &swim->wait_ack_tick); mh_int_t node; mh_foreach(swim->members, node) { struct swim_member *m = @@ -2018,8 +2020,9 @@ void swim_quit(struct swim *swim) { assert(swim_is_configured(swim)); - swim_ev_timer_stop(loop(), &swim->round_tick); - swim_ev_timer_stop(loop(), &swim->wait_ack_tick); + struct ev_loop *l = swim_loop(); + swim_ev_timer_stop(l, &swim->round_tick); + swim_ev_timer_stop(l, &swim->wait_ack_tick); swim_scheduler_stop_input(&swim->scheduler); /* Start the last round - quiting. */ swim_new_round(swim); diff --git a/src/lib/swim/swim_ev.c b/src/lib/swim/swim_ev.c index 49c8c273b..82668d41d 100644 --- a/src/lib/swim/swim_ev.c +++ b/src/lib/swim/swim_ev.c @@ -55,3 +55,9 @@ swim_ev_timer_stop(struct ev_loop *loop, struct ev_timer *watcher) { ev_timer_stop(loop, watcher); } + +struct ev_loop * +swim_loop(void) +{ + return loop(); +} diff --git a/src/lib/swim/swim_ev.h b/src/lib/swim/swim_ev.h index 1bd81306f..900be150f 100644 --- a/src/lib/swim/swim_ev.h +++ b/src/lib/swim/swim_ev.h @@ -52,6 +52,9 @@ swim_ev_timer_again(struct ev_loop *loop, struct ev_timer *watcher); void swim_ev_timer_stop(struct ev_loop *loop, struct ev_timer *watcher); +struct ev_loop * +swim_loop(void); + #define swim_ev_is_active ev_is_active #define swim_ev_init ev_init diff --git a/src/lib/swim/swim_io.c b/src/lib/swim/swim_io.c index c55c276cb..e7ff321d4 100644 --- a/src/lib/swim/swim_io.c +++ b/src/lib/swim/swim_io.c @@ -148,7 +148,7 @@ swim_task_schedule(struct swim_task *task, struct swim_scheduler *scheduler) { assert(! swim_task_is_scheduled(task)); rlist_add_tail_entry(&scheduler->queue_output, task, in_queue_output); - swim_ev_io_start(loop(), &scheduler->output); + swim_ev_io_start(swim_loop(), &scheduler->output); } void @@ -289,16 +289,17 @@ int swim_scheduler_bind(struct swim_scheduler *scheduler, const struct sockaddr_in *addr) { - swim_ev_io_stop(loop(), &scheduler->input); - swim_ev_io_stop(loop(), &scheduler->output); + struct ev_loop *l = swim_loop(); + swim_ev_io_stop(l, &scheduler->input); + swim_ev_io_stop(l, &scheduler->output); struct swim_transport *t = &scheduler->transport; int rc = swim_transport_bind(t, (const struct sockaddr *) addr, sizeof(*addr)); if (t->fd >= 0) { swim_ev_io_set(&scheduler->output, t->fd, EV_WRITE); swim_ev_io_set(&scheduler->input, t->fd, EV_READ); - swim_ev_io_start(loop(), &scheduler->input); - swim_ev_io_start(loop(), &scheduler->output); + swim_ev_io_start(l, &scheduler->input); + swim_ev_io_start(l, &scheduler->output); } return rc; } @@ -306,7 +307,7 @@ swim_scheduler_bind(struct swim_scheduler *scheduler, void swim_scheduler_stop_input(struct swim_scheduler *scheduler) { - swim_ev_io_stop(loop(), &scheduler->input); + swim_ev_io_stop(swim_loop(), &scheduler->input); } void @@ -323,7 +324,7 @@ swim_scheduler_destroy(struct swim_scheduler *scheduler) t->cancel(t, scheduler, -1); } swim_transport_destroy(&scheduler->transport); - swim_ev_io_stop(loop(), &scheduler->output); + swim_ev_io_stop(swim_loop(), &scheduler->output); swim_scheduler_stop_input(scheduler); } diff --git a/test/unit/swim_test_ev.c b/test/unit/swim_test_ev.c index a4ffa2fc8..fb25ac9e4 100644 --- a/test/unit/swim_test_ev.c +++ b/test/unit/swim_test_ev.c @@ -62,6 +62,27 @@ struct swim_event; typedef void (*swim_event_process_f)(struct swim_event *, struct ev_loop *); typedef void (*swim_event_delete_f)(struct swim_event *); +/** + * The unit tests code with the fake events and time does lots of + * forbidden things: it manually invokes pending watcher + * callbacks; manages global time without a kernel; puts not + * existing descriptors into the loop. All these actions does not + * affect the loop until yield. On yield a scheduler fiber wakes + * up and 1) infinitely generates EV_READ on not existing + * descriptors because considers them closed; 2) manual pending + * callbacks invocation asserts, because it is not allowed for + * non-scheduler fibers. To avoid these problems a new isolated + * loop is created, not visible for the scheduler. Here the fake + * events library can rack and ruin whatever it wants. + */ +static struct ev_loop *test_loop; + +struct ev_loop * +swim_loop(void) +{ + return test_loop; +} + /** * Base event. It is stored in the event heap and virtualizes * other events. @@ -330,6 +351,8 @@ swim_test_ev_init(void) events_hash = mh_i64ptr_new(); assert(events_hash != NULL); event_heap_create(&event_heap); + test_loop = ev_loop_new(0); + assert(test_loop != NULL); } void @@ -338,4 +361,5 @@ swim_test_ev_free(void) swim_test_ev_reset(); event_heap_destroy(&event_heap); mh_i64ptr_delete(events_hash); + ev_loop_destroy(test_loop); } diff --git a/test/unit/swim_test_utils.c b/test/unit/swim_test_utils.c index ffd42cbd0..f72fa2450 100644 --- a/test/unit/swim_test_utils.c +++ b/test/unit/swim_test_utils.c @@ -607,7 +607,7 @@ swim_wait_timeout(double timeout, struct swim_cluster *cluster, { swim_ev_set_brk(timeout); double deadline = swim_time() + timeout; - struct ev_loop *loop = loop(); + struct ev_loop *loop = swim_loop(); /* * There can be pending out of bound IO events, affecting * the result. For example, 'quit' messages, which are -- 2.20.1 (Apple Git-117)