Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Возможная гонка из-за порядка разрушения #28

Open
dmirys opened this issue Jun 22, 2023 · 1 comment

Comments

@dmirys
Copy link

dmirys commented Jun 22, 2023

Привет.

Столкнулся с гонкой при обработке цепочки continuations.
Упрощённый пример:

future
    .next(
        executor1,
        [resource1 = get_resource()](auto result) {
            return result;
        })
    .next(
        executor2,
        [](auto result) {
            auto resource2 = get_resource();
            return result;
        });

Вот тут выполняется первая лямбда. А вот здесь её результат будет передан второй лямбде. При этом первая ещё не разрушена, это происходит выше по стеку (при разрушении вот этой лямбды). Значит resource1 всё ещё цел. Это неочевидное поведение, которое может привести к дедлоку, если get_resource не может отдать результат, пока жив resource1, или гонке, если вторая лямбда должна исполняться на другом потоке, а get_resource отдаёт общий ресурс.

Вот такое решение в portable_concurrency/bits/utils.h исправляет поведение и делает его более ожидаемым:

@@ -14,7 +14,8 @@ void set_state_value(std::shared_ptr<shared_state<R>>& state, F&& f, A&&... a) {
   assert(state);
   bool executed = false;
   try {
-    auto&& res = ::portable_concurrency::cxx14_v1::detail::invoke(std::forward<F>(f), std::forward<A>(a)...);
+    auto stolen_f = std::move(f);
+    auto&& res = ::portable_concurrency::cxx14_v1::detail::invoke(std::move(stolen_f), std::forward<A>(a)...);
     executed = true;
     shared_state<R>::unwrap(state, std::move(res));
   } catch (...) {
@VestniK
Copy link
Owner

VestniK commented Jul 2, 2023

Фактически ты хочешь гарантии что результат который вернула функция отданая в then/next не будет использован до разрушения самой функции. Попробую накидать тестов на такое поведение вместе с реализацией.

Внутри set_state_value может не получиться обеспечить подобной гарантии, так как там f передан по perfect-forward ссылке, а владеет им кто-то снаружи. Если владелец отдаёт константный объект, то move не будет иметь эффекта. Так что правильная реализация потребует чуть большего количества правок.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants