Naios / continuable

C++14 asynchronous allocation aware futures (supporting then, exception handling, coroutines and connections)
https://naios.github.io/continuable/
MIT License
815 stars 44 forks source link

make_continuable over executor #64

Closed elazarl closed 9 months ago

elazarl commented 1 year ago

@Naios

continuable has a way to execute then on an executor.

But Is it possible to create a new continuable over an executor?

Of course you can always do something like

make_ready_continuable<void>().then([](){ this_thread.sleep_for(1s); fmt::print("yo\n"); }, executor);

But the documentation discourages you from making thy ready continuable in vain.

Naios commented 1 year ago

executor is specified to be an object that just accepts a callable which itself is invokable such as cti::work.

struct my_executor {
  template<typename T>
  void operator()(T&& callable) {
    std::forward<T>(callable)(); // Can dispatach the callable from another thread.
  }
};

The answer to your question is not directly related to continuable itself. You can always dispatch the start of your continuation chain from any thread e.g. by passing a lambda to the executor directly.

The only issue here is that for instance the cti::work object callable needs to be callable as r-value reference which usually is not the case for an ordinary lambda. A wrapper class can help out circumventing this, in case the executor accepts cti::work directly:

template <typename Callable>
struct work_wrap_t {
  template<typename... T>
  auto operator()(T&&... args) &&
    -> decltype(std::move(callable_)(std::forward<T>(args)...)) {
    return std::move(callable_)(std::forward<T>(args)...);
  }

  Callable callable_;
};
executor(work_wrap_t([] {
  this_thread.sleep_for(1s);
  fmt::print("finished\n");
}));

Overall the answer to this question highly depends on the actual implementation of your executor and which parameters it accepts for initiating the dispatch.

elazarl commented 1 year ago

I'm not entirely sure I understand.

How do I get a continuable from executor().

On the other hand, as you wrote, you can always use an executor directly in then expressions.

continuable<int> c;
c.then([](){foo();}, executor);
// equivalent to:
c.then([](){executor([](){foo();}});

So I'm still not sure, why is executor useful for then, but not for make_continuable?

I might be missing something fundamental here, so feel free to explain the very basics I got incorrectly.

Naios commented 1 year ago

Did you take a look into async_on, maybe this is the solution to your question?

cti::continuable<> c1 = cti::async_on([] {
  this_thread.sleep_for(1s);
  fmt::print("finished\n");
}, executor)
elazarl commented 1 year ago

Thanks. The problem I've had with that, is that the Callable my executable accepts seems to be of the terse type

cti::detail::base::work_proxy<cti::detail::base::decoration::invoker<(lambda at /home/elazarl/.conan/data/continuable/4.2.1/_/_/package/5ab84d6acfe1f23c4fae0ab88f26e3a396351ac9/include/continu
able/detail/core/base.hpp:339:7), cti::detail::identity<tl::expected<ns::elrond::mem_mgr::migration_result, std::error_condition>>>, (lambda at ../elrond/libs/mem_mgr/src
/mem_allocator.hpp:858:13), cti::detail::base::callbacks::final_callback<tl::expected<ns::elrond::mem_mgr::migration_result, std::error_condition>>>'

Which is not type-erasable to fu2::unique_function<void()>. When I do that by hand, I'm passing a lambda there eventually, which is type erased.

Naios commented 9 months ago

It seems like this issue was resolved. If you have any further questions or issue reports open a new ticket please.