[Tarantool-patches] [PATCH 1/2] fiber: make wakeup in Lua and C nop on self

Serge Petrenko sergepetrenko at tarantool.org
Sun Apr 25 12:21:35 MSK 2021



24.04.2021 02:15, Vladislav Shpilevoy пишет:
> fiber.wakeup() in Lua and fiber_wakeup() in C could lead to a
> crash or undefined behaviour when called on the currently running
> fiber.
>
> In particular, if after wakeup was started a new fiber in a
> blocking way (fiber.create() and fiber_start()) it would crash in
> debug build, and lead to unknown results in release.
>
> If after wakeup was made a sleep with non-zero timeout or an
> infinite yield (fiber_yield()), the fiber woke up in the same
> event loop iteration regardless of any timeout or other wakeups.
> It was a spurious wakeup, which is not expected in most of the
> places internally.
>
> The patch makes the wakeup nop on the current fiber making it safe
> to use anywhere.
>
> Closes #5292
> Closes #6043
>
> @TarantoolBot document
> Title: fiber.wakeup() in Lua and fiber_wakeup() in C are nop on self
> In Lua `fiber.wakeup()` being called on the current fiber does not
> do anything, safe to use. The same for `fiber_wakeup()` in C.
> ---

Thanks for the patch!
LGTM, generally, with one test-related comment.


...

> diff --git a/test/app-tap/gh-6043-fiber-wakeup-self.test.lua b/test/app-tap/gh-6043-fiber-wakeup-self.test.lua
> new file mode 100755
> index 000000000..082c1228d
> --- /dev/null
> +++ b/test/app-tap/gh-6043-fiber-wakeup-self.test.lua
> @@ -0,0 +1,50 @@
> +#!/usr/bin/env tarantool
> +
> +local tap = require('tap')
> +local fiber = require('fiber')
> +
> +--
> +-- gh-6043: fiber.wakeup() on self could lead to a crash if followed by
> +-- fiber.create(). Also it could lead to spurious wakeup if followed by a sleep
> +-- with non-zero timeout, or by, for example, an infinite yield such as WAL
> +-- write.
> +--
> +
> +local function test_wakeup_self_and_call(test)
> +    test:plan(3)
> +
> +    local self = fiber.self()
> +    fiber.wakeup(self)
> +    local count = 0
> +    local f = fiber.create(function()
> +        count = count + 1
> +        fiber.yield()
> +        count = count + 1
> +    end)
> +    test:is(count, 1, 'created a fiber')
> +    fiber.yield()
> +    test:is(count, 2, 'it yielded')
> +    test:is(f:status(), 'dead', 'and ended')
> +end
> +
> +local function test_wakeup_self_and_wal_write(test)
> +    test:plan(1)
> +
> +    local s = box.schema.create_space('test')
> +    s:create_index('pk')
> +
> +    fiber.wakeup(fiber.self())
> +    local lsn = box.info.lsn
> +    s:replace{1}
> +    test:is(box.info.lsn, lsn + 1, 'written to WAL')
> +    s:drop()
> +end

Again, shouldn't everything related to space creation, WAL writes and so on
reside in box-tap ? I may be wrong, just asking.

> +
> +box.cfg{}
> +
> +local test = tap.test('gh-6043-fiber-wakeup-self')
> +test:plan(2)
> +test:test('wakeup self + call', test_wakeup_self_and_call)
> +test:test('wakeup self + wal write', test_wakeup_self_and_wal_write)
> +
> +os.exit(test:check() and 0 or 1)
> diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc
> index 9c1a23bdd..a1e3ded87 100644
> --- a/test/unit/fiber.cc
> +++ b/test/unit/fiber.cc
> @@ -138,8 +138,7 @@ fiber_join_test()
>   	fiber_set_joinable(fiber, true);
>   	fiber_wakeup(fiber);
>   	/** Let the fiber schedule */
> -	fiber_wakeup(fiber());
> -	fiber_yield();
> +	fiber_reschedule();
>   	note("by this time the fiber should be dead already");
>   	fiber_cancel(fiber);
>   	fiber_join(fiber);
> @@ -201,12 +200,42 @@ fiber_name_test()
>   	footer();
>   }
>   
> +static void
> +fiber_wakeup_self_test()
> +{
> +	header();
> +
> +	struct fiber *f = fiber();
> +
> +	fiber_wakeup(f);
> +	double duration = 0.001;
> +	uint64_t t1 = fiber_clock64();
> +	fiber_sleep(duration);
> +	uint64_t t2 = fiber_clock64();
> +	/*
> +	 * It was a real sleep, not 0 duration. Wakeup is nop on the running
> +	 * fiber.
> +	 */
> +	assert(t2 - t1 >= duration);
> +
> +	/*
> +	 * Wakeup + start of a new fiber. This is different from yield but
> +	 * works without crashes too.
> +	 */
> +	struct fiber *newf = fiber_new_xc("nop", noop_f);
> +	fiber_wakeup(f);
> +	fiber_start(newf);
> +
> +	footer();
> +}
> +
>   static int
>   main_f(va_list ap)
>   {
>   	fiber_name_test();
>   	fiber_join_test();
>   	fiber_stack_test();
> +	fiber_wakeup_self_test();
>   	ev_break(loop(), EVBREAK_ALL);
>   	return 0;
>   }
> diff --git a/test/unit/fiber.result b/test/unit/fiber.result
> index a61e0a2b8..36bf2a599 100644
> --- a/test/unit/fiber.result
> +++ b/test/unit/fiber.result
> @@ -17,3 +17,5 @@ SystemError Failed to allocate 42 bytes in allocator for exception: Cannot alloc
>   # normal-stack fiber not crashed
>   # big-stack fiber not crashed
>   	*** fiber_stack_test: done ***
> +	*** fiber_wakeup_self_test ***
> +	*** fiber_wakeup_self_test: done ***
> diff --git a/test/unit/fiber_stress.cc b/test/unit/fiber_stress.cc
> index 44ab12487..aa892d0a0 100644
> --- a/test/unit/fiber_stress.cc
> +++ b/test/unit/fiber_stress.cc
> @@ -9,10 +9,8 @@ enum {
>   static int
>   yield_f(va_list ap)
>   {
> -	for (int i = 0; i < ITERATIONS; i++) {
> -		fiber_wakeup(fiber());
> -		fiber_yield();
> -	}
> +	for (int i = 0; i < ITERATIONS; i++)
> +		fiber_reschedule();
>   	return 0;
>   }
>   

-- 
Serge Petrenko



More information about the Tarantool-patches mailing list