Skip to content

Commit

Permalink
let futures exception types override what()
Browse files Browse the repository at this point in the history
Summary:
Rather than increasing state size with `static_what_exception` and rather than inserting it into the exception base-class list, where it forms a new unintended interface, let the exception types simply override member `what` directly.

Continue to avoid constructing `std::logic_error` with the message in order to avoid the allocations and copies. In libstdc++ with libsupc++, in accordance with pre-c++11-abi `std::string`, empty exception strings in `std::logic_error` elide the heap allocation.

Reviewed By: ot

Differential Revision: D58326623

fbshipit-source-id: 27c88975eed00fdd4d79e9a4955d930d37b3fda4
  • Loading branch information
yfeldblum authored and facebook-github-bot committed Jun 9, 2024
1 parent 7cd8d52 commit 9c44b54
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 56 deletions.
43 changes: 27 additions & 16 deletions folly/futures/Future.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,16 @@

namespace folly {

class FOLLY_EXPORT FutureException
: public static_what_exception<std::logic_error> {
class FOLLY_EXPORT FutureException : public std::logic_error {
public:
using static_what_exception<std::logic_error>::static_what_exception;
using std::logic_error::logic_error;
FutureException() : std::logic_error{""} {}
};

class FOLLY_EXPORT FutureInvalid : public FutureException {
public:
FutureInvalid() : FutureException(static_lifetime{}, "Future invalid") {}
FutureInvalid() = default;
char const* what() const noexcept override { return "Future invalid"; }
};

/// At most one continuation may be attached to any given Future.
Expand All @@ -63,42 +64,52 @@ class FOLLY_EXPORT FutureInvalid : public FutureException {
/// thrown instead.
class FOLLY_EXPORT FutureAlreadyContinued : public FutureException {
public:
FutureAlreadyContinued()
: FutureException(static_lifetime{}, "Future already continued") {}
FutureAlreadyContinued() = default;
char const* what() const noexcept override {
return "Future already continued";
}
};

class FOLLY_EXPORT FutureNotReady : public FutureException {
public:
FutureNotReady() : FutureException(static_lifetime{}, "Future not ready") {}
FutureNotReady() = default;
char const* what() const noexcept override { return "Future not ready"; }
};

class FOLLY_EXPORT FutureCancellation : public FutureException {
public:
FutureCancellation()
: FutureException(static_lifetime{}, "Future was cancelled") {}
FutureCancellation() = default;
char const* what() const noexcept override { return "Future was cancelled"; }
};

class FOLLY_EXPORT FutureTimeout : public FutureException {
public:
FutureTimeout() : FutureException(static_lifetime{}, "Timed out") {}
FutureTimeout() = default;
char const* what() const noexcept override { return "Timed out"; }
};

class FOLLY_EXPORT FuturePredicateDoesNotObtain : public FutureException {
public:
FuturePredicateDoesNotObtain()
: FutureException(static_lifetime{}, "Predicate does not obtain") {}
FuturePredicateDoesNotObtain() = default;
char const* what() const noexcept override {
return "Predicate does not obtain";
}
};

class FOLLY_EXPORT FutureNoTimekeeper : public FutureException {
public:
FutureNoTimekeeper()
: FutureException(static_lifetime{}, "No timekeeper available") {}
FutureNoTimekeeper() = default;
char const* what() const noexcept override {
return "No timekeeper available";
}
};

class FOLLY_EXPORT FutureNoExecutor : public FutureException {
public:
FutureNoExecutor()
: FutureException(static_lifetime{}, "No executor provided to via") {}
FutureNoExecutor() = default;
char const* what() const noexcept override {
return "No executor provided to via";
}
};

template <class T>
Expand Down
5 changes: 3 additions & 2 deletions folly/futures/FutureSplitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ namespace folly {

class FOLLY_EXPORT FutureSplitterInvalid : public FutureException {
public:
FutureSplitterInvalid()
: FutureException(static_lifetime{}, "No Future in this FutureSplitter") {
FutureSplitterInvalid() = default;
char const* what() const noexcept override {
return "No Future in this FutureSplitter";
}
};

Expand Down
27 changes: 17 additions & 10 deletions folly/futures/Promise.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,32 @@

namespace folly {

class FOLLY_EXPORT PromiseException
: public static_what_exception<std::logic_error> {
class FOLLY_EXPORT PromiseException : public std::logic_error {
public:
using static_what_exception<std::logic_error>::static_what_exception;
using std::logic_error::logic_error;
PromiseException() : std::logic_error{""} {}
};

class FOLLY_EXPORT PromiseInvalid : public PromiseException {
public:
PromiseInvalid() : PromiseException(static_lifetime{}, "Promise invalid") {}
PromiseInvalid() = default;
char const* what() const noexcept override { return "Promise invalid"; }
};

class FOLLY_EXPORT PromiseAlreadySatisfied : public PromiseException {
public:
PromiseAlreadySatisfied()
: PromiseException(static_lifetime{}, "Promise already satisfied") {}
PromiseAlreadySatisfied() = default;
char const* what() const noexcept override {
return "Promise already satisfied";
}
};

class FOLLY_EXPORT FutureAlreadyRetrieved : public PromiseException {
public:
FutureAlreadyRetrieved()
: PromiseException(static_lifetime{}, "Future already retrieved") {}
FutureAlreadyRetrieved() = default;
char const* what() const noexcept override {
return "Future already retrieved";
}
};

class FOLLY_EXPORT BrokenPromise : public PromiseException {
Expand All @@ -69,10 +74,12 @@ class FOLLY_EXPORT BrokenPromise : public PromiseException {
template <typename T>
static constexpr auto error_message = make_error_message<T>();

const char* const what_{};

public:
template <typename T>
explicit BrokenPromise(tag_t<T>)
: PromiseException(static_lifetime{}, error_message<T>.data) {}
explicit BrokenPromise(tag_t<T>) : what_{error_message<T>.data} {}
char const* what() const noexcept override { return what_; }
};

// forward declaration
Expand Down
28 changes: 0 additions & 28 deletions folly/lang/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -617,32 +617,4 @@ class exception_shared_string {
char const* what() const noexcept;
};

/**
* A wrapper around a given exception type T that allows to store a
* static-lifetime string as what() return value, avoiding having to copy it
* into dedicated allocated storage on construction as most standard exceptions
* do even if the message is static.
*
* The constructor from the base class can still be used, in which case what()
* is delegated to the base class as well.
*/
template <class T>
class static_what_exception : public T {
protected:
struct static_lifetime {};

public:
using T::T;

static_what_exception(static_lifetime, const char* msg)
: T(std::string{}), msg_(msg) {}

const char* what() const noexcept override {
return msg_ != nullptr ? msg_ : T::what();
}

private:
const char* msg_ = nullptr;
};

} // namespace folly

0 comments on commit 9c44b54

Please sign in to comment.