Skip to content

gsl::finally executes throwing code under noexcept, resulting in runtime crash #1193

@apenn-msft

Description

@apenn-msft

gsl::finally in name, API, and documentation purports to allow arbitrary code to be executed at scope exit, however in practice it executes the provided function within its noexcept(true) destructor which results in a runtime crash if the provided action throws.

In name, there is an implied understanding for gsl::finally, which borrows the "finally" concept from C-based languages, to execute arbitrary, even throwing code. This behavior models what "finally" means in other C-based languages where there isn't a constraint on code in the finally block being non-throwing.

In API, gsl::finally will accept a throwing action without indication/enforcement that the action must not throw even though in practice and at runtime, this is a requirement otherwise the program terminates:

gsl::finally([] { throw 1; }); // valid code which compiles and crashes at runtime

In documentation, it documents that "something gets run", implying any arbitrary code is fine.

`final_action` allows you to ensure something gets run at the end of a scope.

In summary, programmers do want a utility to help execute code at scope exit.
First we must decide if we should provide such a utility or if not, and what alternative should exist if any to make code such as:

if (...) Update(); return;
if (...) Update(); return;
if (...) Update(); return;

which would be a reason a programmer would seek out gsl::finally as a utility to execute Update prior to returning without having to manually write it out n-many times.

Next, we must decide if we place exception constraints on gsl::finally.
We may decide to truly allow arbitrary code to execute in gsl::finally, in which case we could specify its destructor's noexcept(false) or based on a template parameter or whether the provided action throws.
If we instead decide to retain gsl::finally's noexcept execution of the action, we should decide how to solve for the problem of:

gsl::finally([] { throw 1; };

where we know at compile time such code is invalid and will terminate at runtime. Can we enforce this at compile time? Yes, i.e. by requiring std::is_nothrow_invocable on the function.

Finally, in addition to compile time enforcement, the documentation (and possibly the name) should be updated to make it clear that gsl::finally can only be used with non-throwing code and the user must ensure the supplied action must not throw (else the program will terminate)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: BlockedCannot proceed due to external factorsStatus: OpenNeeds attentionType: EnhancementSuggests an improvement or new feature

    Projects

    Status

    New

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions