pmem / libpmemobj-cpp

C++ bindings & containers for libpmemobj
https://pmem.io
Other
108 stars 76 forks source link

Simplify throwing exception mechanism in libpmemobj-cpp #1145

Open KFilipek opened 3 years ago

KFilipek commented 3 years ago

Generally (comment taken from #1144):

class transaction_out_of_memory : public transaction_alloc_error,
                  public std::bad_alloc {

All of those: transaction_alloc_error -> transaction_error -> std:runtime_error has constructor(const std::string&), but std::runtime_error not, it is designed to not take additional memory (hey, we have OOM, so we should be careful). Second thing: throw new transaction_out_of_memory().with_pmemobj_errormsg(); will create 3 objects instead of 1 causing unnecessary object creation during OOM situation.

My point is to make this simply to use and add proper constructors like these in std::runtime_error. std::bad_alloc dosn't have it for known reason.

igchor commented 3 years ago

What exactly do you propose? I don't think there is any problems with creating multiple objects (which most likely will be optimized anway anyway). And we don't need to be so careful about the memory usage since the OOM is from PMEM and std::string comes from dram.

KFilipek commented 3 years ago

With -O3 still it's not optimized.

Simply as is:

int main() {
    try {
        throw PolymorphicException("Error: ").decorate("Bonjour");
    } catch(const std::exception &e) {
        std::cout << e.what() << std::endl;

    }
}

Can be achieved by such implementation:

class PolymorphicException : public std::runtime_error {
public:
    std::string what_message = "";
    PolymorphicException() : std::runtime_error("") {
        std::cout << "Constructor of: " << this << std::endl;
    }
    PolymorphicException(const std::string &what_arg) : std::runtime_error(what_arg), what_message(what_arg) {
        std::cout << "Constructor of: " << this << std::endl;
    }
    virtual PolymorphicException & decorate(std::string additional_string) {
        what_message += additional_string;
        return *this;
    }
    virtual ~PolymorphicException() {
        std::cout << "Destructor of: " << this << std::endl;
    }

    virtual const char* what() const noexcept {
        return what_message.c_str();
    }
};

Output:

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
Constructor of: 0x7fff4b88d888
Destructor of: 0x7fff4b88d888
Error: Bonjour
Destructor of: 0x1437f30

Notice: 1 derivate object creates whole tree, in libpmemobj-cpp for transaction_out_of_memory you will create:

                    transaction_out_of_memory
                    /                   \
 transaction_alloc_error                std::bad_alloc
                    |                    |
    transaction_error                   std::exception
                    |
    std::runtime_error
                    |
    std::exception (second copy, because we don't use virtual inheritance here)

so, you will create 7 objects, 2-3 times instead of 1.