//------------------------------------------------------------------------------ // File: FutureWrapper.hh // Author: Georgios Bitzes - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2018 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #pragma once #include "common/Namespace.hh" #include EOSCOMMONNAMESPACE_BEGIN //------------------------------------------------------------------------------ //! Wrap a future and its result, allows to transparently use the object without //! worrying if it has arrived yet. If it hasn't, we block. //! //! A lot of times when dealing with std::future, we have to keep track a future //! object, and the object itself, and whether the future has arrived, so as to //! avoid caling get() twice... //! //! This class takes care of all that transparently. //! //! Requires T to have default constructor. //! If the future arrives armed with an exception, it is re-thrown __every__ time //! this wrapper is accessed for reading the object! Not just the first one. //------------------------------------------------------------------------------ template class FutureWrapper { public: //---------------------------------------------------------------------------- //! Empty constructor //---------------------------------------------------------------------------- FutureWrapper() : mFut(folly::makeFuture(T())), mArrived(true) {} //---------------------------------------------------------------------------- //! Constructor, takes an existing future object //! //! @param future object to take ownership //---------------------------------------------------------------------------- FutureWrapper(folly::Future&& future) : mFut(std::move(future)) {} //---------------------------------------------------------------------------- //! Constructor, takes a promise //! //! @param promise promise used to get a future //---------------------------------------------------------------------------- FutureWrapper(folly::Promise& promise) : mFut(promise.getFuture()) {} //---------------------------------------------------------------------------- //! Check if accessing the object might block. Exception safe - if the //! underlying future is hiding an exception, we don't throw here, but during //! first actual access. //---------------------------------------------------------------------------- bool ready() { if (mArrived) { return true; } return mFut.isReady(); } //---------------------------------------------------------------------------- //! Get a reference to the object itself. Unlike std::future::get, we only //! return a reference, not a copy of the object, and you can call this //! function as many times as you like. //! //! @note Beware of exceptions! They will be propagated from the underlying //! future //---------------------------------------------------------------------------- T& get() { wait(); if (mException) { std::rethrow_exception(mException); } return mObj; } //---------------------------------------------------------------------------- //! Convenience function, use -> to access members of the underlying object //! //! @note Beware of exceptions! They will be propagated from the underlying //! future. //---------------------------------------------------------------------------- T* operator->() { wait(); if (mException) { std::rethrow_exception(mException); } return &mObj; } const T* operator->() const { wait(); if (mException) { std::rethrow_exception(mException); } return &mObj; } //---------------------------------------------------------------------------- //! Check if future is armed with an exception - will wait to receive result //---------------------------------------------------------------------------- bool hasException() { wait(); return mException != nullptr; } //---------------------------------------------------------------------------- //! Method that waits for the underlying future to return //---------------------------------------------------------------------------- void wait() const { if (mArrived) { return; } mArrived = true; try { mObj = std::move(mFut).get(); } catch (...) { mException = std::current_exception(); } } private: mutable folly::Future mFut; mutable bool mArrived = false; mutable std::exception_ptr mException; mutable T mObj; }; EOSCOMMONNAMESPACE_END