diff --git a/src/lib/swim/swim.c b/src/lib/swim/swim.c index f7a885b764a0c030b4ee1666cc04e26a3ddb3ae2..46b76731d0673ab801e47f8c3368368c8af57be3 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 49c8c273bc3903ace21c1c96d36f3786306a73f3..82668d41ddd196d4415d7c2b5fd0eae11e61ea12 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 1bd81306f39530f8e72364c6b2db9e3eac0d2110..37e743d459768c6da3d0b1cf663b01897fe6ea59 100644 --- a/src/lib/swim/swim_ev.h +++ b/src/lib/swim/swim_ev.h @@ -52,6 +52,24 @@ 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); +/** + * 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. This + * function is supposed to be an alias for 'loop()' in the + * Tarantool core, but be an isolated object in tests. + */ +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 c55c276cb7b931577c7eecdc3c87319d3dacb01d..e7ff321d4270a59e186f42f6dc53f5e9135b843b 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 a4ffa2fc89277ce23e25cdf10de3983e6bf404c9..23d909b0556593aa225bcf61bbcae245eca24c82 100644 --- a/test/unit/swim_test_ev.c +++ b/test/unit/swim_test_ev.c @@ -62,6 +62,19 @@ struct swim_event; typedef void (*swim_event_process_f)(struct swim_event *, struct ev_loop *); typedef void (*swim_event_delete_f)(struct swim_event *); +/** + * An isolated event loop not visible to the fiber scheduler, + * where it is safe to use fake file descriptors, manually invoke + * callbacks etc. + */ +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 +343,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 +353,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 ffd42cbd0b1b45d059488783373c58e935022451..f72fa2450bac2a11a25213c252a8bc0e0888ab8f 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