diff --git a/cpp/include/kvikio/utils.hpp b/cpp/include/kvikio/utils.hpp index 41b50f7108..8194310198 100644 --- a/cpp/include/kvikio/utils.hpp +++ b/cpp/include/kvikio/utils.hpp @@ -150,10 +150,43 @@ class PushAndPopContext { std::tuple get_alloc_info(void const* devPtr, CUcontext* ctx = nullptr); +/** + * @brief Create a shared state in a future object that is immediately ready. + * + * A partial implementation of the namesake function from the concurrency TS + * (https://en.cppreference.com/w/cpp/experimental/make_ready_future). The cases of + * std::reference_wrapper and void are not implemented. + * + * @tparam T Type of the value provided. + * @param t Object provided. + * @return A future holding a decayed copy of the object provided. + */ +template +std::future> make_ready_future(T&& t) +{ + std::promise> p; + auto fut = p.get_future(); + p.set_value(std::forward(t)); + return fut; +} + +/** + * @brief Check the status of the future object. True indicates that the result is available in the + * future's shared state. False otherwise. + * + * The future shall not be created using `std::async(std::launch::deferred)`. Otherwise, this + * function always returns true. + * + * @tparam T Type of the future. + * @param future Instance of the future. + * @return Boolean answer indicating if the future is ready or not. + */ template bool is_future_done(T const& future) { - assert(future.valid()); + if (!future.valid()) { + throw std::invalid_argument("The future object does not refer to a valid shared state."); + } return future.wait_for(std::chrono::seconds(0)) != std::future_status::timeout; } diff --git a/cpp/src/file_handle.cpp b/cpp/src/file_handle.cpp index 882799e016..a1bb5781ee 100644 --- a/cpp/src/file_handle.cpp +++ b/cpp/src/file_handle.cpp @@ -26,6 +26,7 @@ #include #include #include +#include "kvikio/utils.hpp" namespace kvikio { @@ -212,10 +213,9 @@ std::future FileHandle::pread(void* buf, if (size < gds_threshold) { PushAndPopContext c(ctx); auto bytes_read = detail::posix_device_read(_fd_direct_off.fd(), buf, size, file_offset, 0); - std::promise read_promise; - auto read_future = read_promise.get_future(); - read_promise.set_value(bytes_read); - return read_future; + // Maintain API consistency while making this trivial case synchronous. + // The result in the future is immediately available after the call. + return make_ready_future(bytes_read); } // Let's synchronize once instead of in each task. @@ -263,10 +263,9 @@ std::future FileHandle::pwrite(void const* buf, if (size < gds_threshold) { PushAndPopContext c(ctx); auto bytes_write = detail::posix_device_write(_fd_direct_off.fd(), buf, size, file_offset, 0); - std::promise write_promise; - auto write_future = write_promise.get_future(); - write_promise.set_value(bytes_write); - return write_future; + // Maintain API consistency while making this trivial case synchronous. + // The result in the future is immediately available after the call. + return make_ready_future(bytes_write); } // Let's synchronize once instead of in each task.