X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fscope-exit.hpp;fp=src%2Fcommon%2Fscope-exit.hpp;h=46479bc572439762ecded83cad8d4b682927cac0;hp=0000000000000000000000000000000000000000;hb=b6bbb1d666531bf061f29884da1b0d7c10f59aa0;hpb=88277a52069ed0135254ce29da617ebb6ecddbb8 diff --git a/src/common/scope-exit.hpp b/src/common/scope-exit.hpp new file mode 100644 index 000000000..46479bc57 --- /dev/null +++ b/src/common/scope-exit.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2022 Jérémie Galarneau + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_SCOPE_EXIT_H +#define LTTNG_SCOPE_EXIT_H + +#include + +namespace lttng { + +namespace details { +/* Is operator() of InvocableType is marked as noexcept? */ +template +struct is_invocation_noexcept + : std::integral_constant())())> { +}; +} /* namespace details. */ + +/* + * Generic utility to run a lambda (or any other invocable object) when leaving + * a scope. + * + * Notably, this makes it easy to specify an action (e.g. restore a context) + * that must occur at the end of a function or roll-back operations in an + * exception-safe way. + */ +template +class scope_exit { +public: + /* + * Since ScopeExitInvocableType will be invoked in the destructor, it + * must be `noexcept` lest we anger the undefined behaviour gods. + */ + static_assert(details::is_invocation_noexcept::value, + "scope_exit requires a noexcept invocable type"); + + explicit scope_exit(ScopeExitInvocableType&& scope_exit_callable) : + _on_scope_exit{std::forward(scope_exit_callable)} + { + } + + scope_exit(scope_exit&& rhs) : + _on_scope_exit{std::move(rhs._on_scope_exit)}, _armed{rhs._armed} + { + /* Don't invoke ScopeExitInvocableType for the moved-from copy. */ + rhs.disarm(); + } + + /* + * The copy constructor is disabled to prevent the action from being + * executed twice should a copy be performed accidentaly. + * + * The move-constructor is present to enable make_scope_exit() but to + * also propagate the scope_exit to another scope, should it be needed. + */ + scope_exit(const scope_exit&) = delete; + scope_exit() = delete; + + void disarm() noexcept + { + _armed = false; + } + + ~scope_exit() + { + if (_armed) { + _on_scope_exit(); + } + } + +private: + ScopeExitInvocableType _on_scope_exit; + bool _armed = true; +}; + +template +scope_exit make_scope_exit(ScopeExitInvocableType&& scope_exit_callable) +{ + return scope_exit( + std::forward(scope_exit_callable)); +} + +} /* namespace lttng */ + +#endif /* LTTNG_SCOPE_EXIT_H */