// This file is part of Desktop App Toolkit, // a set of libraries for developing nice desktop applications. // // For license and copyright information please follow this link: // https://github.com/desktop-app/legal/blob/master/LEGAL // #pragma once #include #include #include #include #include #ifdef CRL_ENABLE_RPL_INTEGRATION #include #endif // CRL_ENABLE_RPL_INTEGRATION namespace crl::details { template class object_async_base : public std::enable_shared_from_this> , private Policy { public: template < typename Value, typename = std::enable_if_t>> void destroy(Value &&value) const; protected: template void async(Callable &&callable) const; template void sync(Callable &&callable) const; ~object_async_base() = default; }; template class object_async_storage { protected: Type &value(); const Type &value() const; ~object_async_storage(); private: std::aligned_storage_t _storage; }; template class object_async_data final : private object_async_storage , public object_async_base { public: using Object = Type; template void construct(Args &&...args); template void with(Method &&method); template void with(Method &&method) const; template void with_sync(Method &&method); template void with_sync(Method &&method) const; }; template class weak_async final { using data = details::object_async_data< Policy, std::remove_const_t>; using my_data = std::conditional_t< std::is_const_v, const data, data>; public: weak_async() = default; weak_async(const std::shared_ptr &strong); weak_async(const weak_async &other) = default; weak_async(weak_async &&other) = default; weak_async &operator=(const weak_async &other) = default; weak_async &operator=(weak_async &&other) = default; template void with(Method &&method) const; template void with_sync(Method &&method) const; template < typename Value, typename = std::enable_if_t>> void destroy(Value &&value) const; // Returns a lambda that runs arbitrary callable on the objects queue. // const auto r = runner(); r([] { make_some_work_on_queue(); }); auto runner() const { return [weak = *this](auto &&method) { weak.with([ method = std::forward(method) ](Type&) mutable { std::move(method)(); }); }; } #ifdef CRL_ENABLE_RPL_INTEGRATION template < typename Method, typename Invoke, typename Result = decltype( std::declval()(std::declval()))> Result producer(Method &&method, Invoke &&invoke) const; template < typename Method, typename Result = decltype( std::declval()(std::declval()))> Result producer_on_main(Method &&method) const; #endif // CRL_ENABLE_RPL_INTEGRATION private: std::weak_ptr _weak; }; template class object_async final { public: template object_async(Args &&...args); object_async(const object_async &other) = delete; object_async &operator=(const object_async &other) = delete; template void with(Method &&method); template void with(Method &&method) const; template void with_sync(Method &&method); template void with_sync(Method &&method) const; template < typename Value, typename = std::enable_if_t>> void destroy(Value &&value) const; #ifdef CRL_ENABLE_RPL_INTEGRATION template < typename Method, typename Invoke, typename Result = decltype( std::declval()(std::declval()))> Result producer(Method &&method, Invoke &&invoke) const; template < typename Method, typename Result = decltype( std::declval()(std::declval()))> Result producer_on_main(Method &&method) const; #endif // CRL_ENABLE_RPL_INTEGRATION weak_async weak(); weak_async weak() const; ~object_async(); private: using Data = details::object_async_data; std::shared_ptr _data; }; template template void object_async_base::async(Callable &&callable) const { Policy::async_plain([ that = this->shared_from_this(), what = std::forward(callable) ]() mutable { std::move(what)(); }); } template template void object_async_base::sync(Callable &&callable) const { semaphore waiter; Policy::async_plain([&] { const auto guard = details::finally([&] { waiter.release(); }); callable(); }); waiter.acquire(); } template template void object_async_base::destroy(Value &&value) const { Policy::async_plain([moved = std::move(value)]{}); } template Type &object_async_storage::value() { return *reinterpret_cast(&_storage); } template const Type &object_async_storage::value() const { return *reinterpret_cast(&_storage); } template object_async_storage::~object_async_storage() { value().~Type(); } template template void object_async_data::construct(Args &&...args) { object_async_base::async([arguments = std::make_tuple( &object_async_storage::value(), std::forward(args)... )]() mutable { const auto create = [](void *storage, Args &&...args) { new (storage) Type(std::forward(args)...); }; std::apply(create, std::move(arguments)); }); } template template void object_async_data::with(Method &&method) { object_async_base::async([ =, method = std::forward(method) ]() mutable { std::move(method)(object_async_storage::value()); }); } template template void object_async_data::with(Method &&method) const { object_async_base::async([ =, method = std::forward(method) ]() mutable { std::move(method)(object_async_storage::value()); }); } template template void object_async_data::with_sync(Method &&method) { object_async_base::sync([ =, method = std::forward(method) ]() mutable { std::move(method)(object_async_storage::value()); }); } template template void object_async_data::with_sync(Method &&method) const { object_async_base::sync([ =, method = std::forward(method) ]() mutable { std::move(method)(object_async_storage::value()); }); } template weak_async::weak_async(const std::shared_ptr &strong) : _weak(strong) { } template template void weak_async::with(Method &&method) const { if (auto strong = _weak.lock()) { const auto raw = strong.get(); raw->with(std::move(method)); raw->destroy(std::move(strong)); } } template template void weak_async::with_sync(Method &&method) const { if (auto strong = _weak.lock()) { const auto raw = strong.get(); raw->with_sync(std::move(method)); raw->destroy(std::move(strong)); } } template template void weak_async::destroy(Value &&value) const { if (auto strong = _weak.lock()) { const auto raw = strong.get(); raw->destroy(std::move(value)); raw->destroy(std::move(strong)); } else { [[maybe_unused]] const auto moved = std::move(value); } } #ifdef CRL_ENABLE_RPL_INTEGRATION template template Result weak_async::producer( Method &&method, Invoke &&invoke) const { return [ weak = *this, method = std::forward(method), invoke = std::forward(invoke) ](auto consumer) mutable { auto lifetime_on_queue = std::make_shared(); weak.with([ method = std::move(method), invoke = std::move(invoke), consumer = std::move(consumer), lifetime_on_queue ](const Type &that) mutable { method( that ) | rpl::on_next_error_done([=](auto &&value) { invoke([ consumer, value = std::forward(value) ]() mutable { consumer.put_next(std::move(value)); }); }, [=](auto &&error) { invoke([ consumer, error = std::forward(error) ]() mutable { consumer.put_error(std::move(error)); }); }, [=] { invoke([=] { consumer.put_done(); }); }, *lifetime_on_queue); }); return rpl::lifetime([ lifetime_on_queue = std::move(lifetime_on_queue), weak = std::move(weak) ]() mutable { weak.destroy(std::move(lifetime_on_queue)); }); }; } template template Result weak_async::producer_on_main(Method &&method) const { return producer(std::forward(method), [](auto &&callback) { crl::on_main(std::forward(callback)); }); } #endif // CRL_ENABLE_RPL_INTEGRATION template template object_async::object_async(Args &&...args) : _data(std::make_shared()) { constexpr auto plain_construct = std::is_constructible_v< Type, Args...>; [[maybe_unused]] constexpr auto with_weak_construct = std::is_constructible_v< Type, weak_async, Args...>; if constexpr (plain_construct) { _data->construct(std::forward(args)...); } else if constexpr (with_weak_construct) { _data->construct(weak(), std::forward(args)...); } else { static_assert(false_t(args...), "Could not find a constructor."); } } template template void object_async::with(Method &&method) { _data->with(std::forward(method)); } template template void object_async::with(Method &&method) const { const auto data = static_cast(_data.get()); data->with(std::forward(method)); } template template void object_async::with_sync(Method &&method) { _data->with_sync(std::forward(method)); } template template void object_async::with_sync(Method &&method) const { const auto data = static_cast(_data.get()); data->with_sync(std::forward(method)); } template template void object_async::destroy(Value &&value) const { _data->destroy(std::move(value)); } #ifdef CRL_ENABLE_RPL_INTEGRATION template template Result object_async::producer( Method &&method, Callback &&callback) const { return weak().producer( std::forward(method), std::forward(callback)); } template template Result object_async::producer_on_main(Method &&method) const { return weak().producer_on_main(std::forward(method)); } #endif // CRL_ENABLE_RPL_INTEGRATION template auto object_async::weak() -> weak_async { return { _data }; } template auto object_async::weak() const -> weak_async { return { _data }; } template object_async::~object_async() { _data->destroy(std::move(_data)); } } // namespace crl::details