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

scheduler: Support moved tast captures / arguments #216

Closed
wants to merge 1 commit into from

Conversation

ben-clayton
Copy link
Contributor

@ben-clayton ben-clayton commented Feb 21, 2022

Replace the internal use of std::function with std::packaged_task.
std::function requires that the wrapped function is CopyConstructable, where as a std::packaged_task does not.
This allows the tasks to hold std::move'd values.

This is an API / ABI breaking change, but I believe few people would be copying marl::Tasks.

Fixes: #211

@4kangjc
Copy link
Contributor

4kangjc commented Mar 27, 2023

Hello, is there any update for this?

Replace the internal use of `std::function` with `std::packaged_task`.
`std::function` requires that the wrapped function is CopyConstructable, where as a `std::packaged_task` does not.
This allows the tasks to hold `std::move`'d values.

This is an API / ABI breaking change, but I believe few people would be copying `marl::Task`s.
@ben-clayton ben-clayton force-pushed the support-moved-captures branch from 6058c63 to a2ca890 Compare March 27, 2023 13:23
@4kangjc
Copy link
Contributor

4kangjc commented Mar 27, 2023

Hello, is there any update for this?

I have another solution.

#include <iostream>
#include <type_traits>
#include <functional>
#include <memory>
#include <utility>
#include <cassert>

template <typename Callable>
void task_proxy(void* vp) {
    auto invoker_ptr(static_cast<Callable*>(vp));
    if (invoker_ptr) {
        (*invoker_ptr)();
    }
}

class task {
public:
    template <class Fn, typename = std::enable_if_t<std::is_invocable_v<Fn&&>>>
    task(Fn&& func) {
        using Wrapper = typename std::decay_t<Fn>;
        auto __vp = new Wrapper(std::forward<Fn>(func));
        proxy = &task_proxy<Wrapper>;
        vp.reset(__vp);
    }

    void operator()() const { proxy(vp.get()); }
    explicit operator bool() { return proxy.operator bool(); }
private:
    std::function<void(void*)> proxy;
    std::shared_ptr<void> vp;
};

void print() {
    std::cout << "Hello World\n";
}

int main() {
    const char* s = "Hello World";
    auto uptr = std::make_unique<int>(2);
    // Test lambda
    task t([u = std::move(uptr), s]() {
        assert(*u = 2);
        std::cout << s << std::endl;
    });
    t();
    
    // Test function name
    task t2(print);
    t2();

    // Test function pointer
    task t3(&print);
    t3();

    // Test member method
    struct A {
        void operator()() {}
    };
    A a;
    task t4(a);
    t4();
    // Test bind
    task t5(std::bind(&A::operator(), a));
    t5();
}

@ben-clayton
Copy link
Contributor Author

MSVC doesn't like this change:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1195): error C2280: 'std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>::_Binder(const std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>> &)': attempting to reference a deleted function [T:\src\github\marl\build\marl-unittests.vcxproj]
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1916): note: compiler has generated 'std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>::_Binder' here
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1916): note: 'std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>::_Binder(const std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>> &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::_Compressed_pair<WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::tuple<std::unique_ptr<std::string,std::default_delete<_Ty>>>,false>::_Compressed_pair(const std::_Compressed_pair<WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>>,false> &)'
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]
          and
          [
              _Ty=std::string
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xutility(355): note: 'std::_Compressed_pair<WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::tuple<std::unique_ptr<std::string,std::default_delete<_Ty>>>,false>::_Compressed_pair(const std::_Compressed_pair<WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>>,false> &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::tuple<std::unique_ptr<std::string,std::default_delete<_Ty>>>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>> &)'
          with
          [
              _Ty=std::string
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\tuple(484): note: 'std::tuple<std::unique_ptr<std::string,std::default_delete<_Ty>>>::tuple(const std::tuple<std::unique_ptr<_Ty,std::default_delete<_Ty>>> &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::_Tuple_val<_This>::_Tuple_val(const std::_Tuple_val<_This> &)'
          with
          [
              _Ty=std::string
          ]
          and
          [
              _This=std::unique_ptr<std::string,std::default_delete<std::string>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\tuple(271): note: 'std::_Tuple_val<_This>::_Tuple_val(const std::_Tuple_val<_This> &)': function was implicitly deleted because a data member invokes a deleted or inaccessible function 'std::unique_ptr<std::string,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)'
          with
          [
              _This=std::unique_ptr<std::string,std::default_delete<std::string>>
          ]
          and
          [
              _Ty=std::string
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\memory(2337): note: 'std::unique_ptr<std::string,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': function was explicitly deleted
          with
          [
              _Ty=std::string
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1214): note: see reference to function template instantiation 'std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>,_Ret>::_Func_impl_no_alloc<const _Callable&,void>(_Other)' being compiled
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              _Ret=void,
              _Callable=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Other=const std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>> &
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1214): note: see reference to function template instantiation 'std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>,_Ret>::_Func_impl_no_alloc<const _Callable&,void>(_Other)' being compiled
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              _Ret=void,
              _Callable=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Other=const std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>> &
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1213): note: while compiling class template member function 'std::_Func_base<_Ret> *std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>,_Ret>::_Clone(void *,std::false_type) const'
          with
          [
              _Ret=void,
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1204): note: see reference to function template instantiation 'std::_Func_base<_Ret> *std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>,_Ret>::_Clone(void *,std::false_type) const' being compiled
          with
          [
              _Ret=void,
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1061): note: see reference to class template instantiation 'std::_Func_impl_no_alloc<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>,_Ret>' being compiled
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              _Ret=void
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1320): note: see reference to class template instantiation 'std::_Is_large<_Impl>' being compiled
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1502): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>>(_Fx &&)' being compiled
          with
          [
              _Ret=void,
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              _Fx=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\functional(1502): note: see reference to function template instantiation 'void std::_Func_class<_Ret>::_Reset<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>>(_Fx &&)' being compiled
          with
          [
              _Ret=void,
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              _Fx=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\future(674): note: see reference to function template instantiation 'std::function<void (void)>::function<_Ty,void>(_Fx)' being compiled
          with
          [
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fx=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\future(673): note: see reference to function template instantiation 'std::function<void (void)>::function<_Ty,void>(_Fx)' being compiled
          with
          [
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fx=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\future(1676): note: see reference to function template instantiation 'std::_Packaged_state<_Ret (void)>::_Packaged_state<_Ty>(_Fty2 &&)' being compiled
          with
          [
              _Ret=void,
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fty2=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\future(1675): note: see reference to function template instantiation 'std::_Packaged_state<_Ret (void)>::_Packaged_state<_Ty>(_Fty2 &&)' being compiled
          with
          [
              _Ret=void,
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fty2=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  t:\src\github\marl\include\marl\task.h(64): note: see reference to function template instantiation 'std::packaged_task<void (void)>::packaged_task<_Ty,void>(_Fty2 &&)' being compiled
          with
          [
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fty2=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  t:\src\github\marl\include\marl\task.h(64): note: see reference to function template instantiation 'std::packaged_task<void (void)>::packaged_task<_Ty,void>(_Fty2 &&)' being compiled
          with
          [
              _Ty=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>,
              _Fty2=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  T:\src\github\marl\include\marl/scheduler.h(600): note: see reference to function template instantiation 'marl::Task::Task<std::_Binder<std::_Unforced,_Ty,std::unique_ptr<std::string,std::default_delete<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>>>>(F &&,marl::Task::Flags)' being compiled
          with
          [
              _Ty=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,
              F=std::_Binder<std::_Unforced,WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<std::string>>>
          ]
  T:\src\github\marl\src\scheduler_test.cpp(137): note: see reference to function template instantiation 'void marl::schedule<WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>,std::unique_ptr<std::string,std::default_delete<_Ty>>>(Function &&,std::unique_ptr<_Ty,std::default_delete<_Ty>> &&)' being compiled
          with
          [
              _Ty=std::string,
              Function=WithBoundScheduler_ScheduleWithMovedArg_Test::TestBody::<lambda_b1b149e6d2ae47e822ff4f13e8423889>
          ]

I haven't had time to see if I can find a work around for this.

I have another solution.

Thank you for the suggestion.

The heap allocation is costly to do for all tasks. marl::aligned_storage would allow you to placement-new, but then each task would carry a std::function and an additional fixed-size payload. That would also negatively affect performance.

I guess there's nothing stopping a user from using this work-around as something they'd pass to marl::schedule()?

@4kangjc
Copy link
Contributor

4kangjc commented Mar 27, 2023

Implement a marl::Function in marl, similar to std::move_only_function of C++23?

Copy link
Member

@amaiorano amaiorano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks okay to me. Not super familiar with std::packaged_task, but reading about it, it looks like there would be some size and runtime overhead as compared to std::function because it stores a std::future of the callable's return value; however, in this case, the return type is void so this probably doesn't make much difference.

@4kangjc
Copy link
Contributor

4kangjc commented Mar 28, 2023

This is a solution referring to std::move_only_function of c++23.

struct Task {
    // template <class Fn, typename = std::enable_if_t<std::is_invocable_v<Fn>>>
    template <class Fn>
    Task(Fn&& func) {
        using Wrapper = typename std::decay<Fn>::type;
        init<Wrapper>(std::forward<Fn>(func));
        invoker = &Invoke<Wrapper>;
    }

    ~Task() {
        if (manager) {
            manager(storage, nullptr);
        }
    }

    void operator()() const {
        invoker(&storage);
    }

    struct Storage {
        void* addr() noexcept { return &bytes[0]; }
        const void* addr() const noexcept { return &bytes[0]; }

        struct Delegate { void (Storage::*pfm)(); Storage* obj; };
        union {
	        void* ptr;
	        alignas(Delegate) alignas(void(*)())
	        unsigned char bytes[sizeof(Delegate)];
        };

    };

    template<typename T>
    static constexpr bool stored_locally
	= sizeof(T) <= sizeof(Storage) && alignof(T) <= alignof(Storage);
	    // && std::is_nothrow_move_constructible_v<T>;

    template <class T>
    static void Manage(Storage& target, Storage* src) noexcept {
        if (stored_locally<T>) {
            if (src) {
                T* rval = static_cast<T*>(src->addr());
                ::new (target.addr()) T(std::move(*rval));
                rval->~T();
            } else {
                static_cast<T*>(target.addr())->~T();
            }
        } else {
            if (src) {
                target.ptr = src->ptr;
            } else {
                delete static_cast<T*>(target.ptr);
            }
        }
    }

    template <class T>
    static void Invoke(Storage* vp) {
        if (stored_locally<T>) {
            auto invoker_ptr(static_cast<T*>(vp->addr()));
            if (invoker_ptr) {
                (*invoker_ptr)();
            }
        } else {
            auto invoker_ptr(static_cast<T*>(vp->ptr));
            if (invoker_ptr) {
                (*invoker_ptr)();
            }
        }
    }

    template <class T, class... Args>
    void init(Args&&... args) {
        if (stored_locally<T>) {
            new (storage.addr()) T(std::forward<Args>(args)...);
        } else {
            storage.ptr = new T(std::forward<Args>(args)...);
        }
        manager = &Manage<T>;
    }

    using Invoker = void(*)(Storage*);
    using Manager = void(*)(Storage&, Storage*);

    Invoker invoker = nullptr;
    mutable Storage storage;
    Manager manager = nullptr;
};

@315567599
Copy link

The master branch did not see this commit yet. lambda still not support unique_ptr. Why is this request not submitted?

@ben-clayton
Copy link
Contributor Author

The master branch did not see this commit yet. lambda still not support unique_ptr. Why is this request not submitted?

Because the PR does not compile with MSVC, and suggested alternatives will add unacceptable overhead to each scheduled task.

If someone can create a PR that adds move-support with no additional overhead, then I'd happily review it.

Closing this PR.

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

Successfully merging this pull request may close these issues.

Can't capture unique_ptr in marl::schedule lambda
4 participants