Exception safety

Last updated

Exception safety is the state of code working correctly when exceptions are thrown. [1] To aid in ensuring exception safety, C++ standard library developers have devised a set of exception safety levels, contractual guarantees of the behavior of a data structure's operations with regards to exceptions. Library implementers and clients can use these guarantees when reasoning about exception handling correctness. The exception safety levels apply equally to other languages and error-handling mechanisms. [2]

Contents

History

As David Abrahams writes, "nobody ever spoke of 'error-safety' before C++ had exceptions." [3] The term appeared as the topic of publications in JTC1/SC22/WG21, the C++ standard committee, as early as 1994. [4] Exception safety for the C++ standard library was first formalized for STLport by Abrahams, establishing the basic safety/strong safety distinction. [5] This was extended to the modern basic/strong/nothrow guarantees in a later proposal. [6]

Background

Exceptions provide a form of non-local control flow, in that an exception may "bubble up" from a called function. This bubbling can cause an exception safety bug by breaking invariants of a mutable data structure, as follows: [7]

  1. A step of an operation on a mutable data structure modifies the data and breaks an invariant.
  2. An exception is thrown and control "bubbles up", skipping the rest of the operation's code that would restore the invariant
  3. The exception is caught and recovered from, or a finally block is entered
  4. The data structure with broken invariant is used by code that assumes the invariant, resulting in a bug

Code with a bug such as the above can be said to be "exception unsafe". [7]

Classification

The C++ standard library provides several levels of exception safety (in decreasing order of safety): [8]

  1. No-throw guarantee, also known as failure transparency: Operations are guaranteed to succeed and satisfy all requirements even in exceptional situations. If an exception occurs, it will be handled internally and not observed by clients.
  2. Strong exception safety, also known as commit or rollback semantics: Operations can fail, but failed operations are guaranteed to have no side effects, leaving the original values intact. [9]
  3. Basic exception safety: Partial execution of failed operations can result in side effects, but all invariants are preserved. Any stored data will contain valid values which may differ from the original values. Resource leaks (including memory leaks) are commonly ruled out by an invariant stating that all resources are accounted for and managed.
  4. No exception safety: No guarantees are made.

Usually, at least basic exception safety is required to write robust code. Higher levels of safety can sometimes be difficult to achieve, and might incur an overhead due to extra copying. A key mechanism for exception safety is a finally clause, or similar exception handling syntax, which ensure that certain code is always run when a block is exited, including by exceptions. Several languages have constructs that simplify this, notably using the dispose pattern, named as using, with, or try-with-resources.

Example

Consider a smart vector type, such as C++'s std::vector or Java's ArrayList. When an item x is added to a vector v, the vector must actually add x to the internal list of objects and update a count field that says how many objects are in v. It may also need to allocate new memory if the existing capacity isn't sufficient.

Exception safety alternatives:

No-throw guarantee
Implemented by ensuring that memory allocation never fails, or by defining the insert function's behavior on allocation failure (for example, by having the function return a boolean result indicating whether the insertion took place).
Strong exception safety
Implemented by doing any necessary allocation first, and then swapping buffers if no errors are encountered (the copy-and-swap idiom). In this case, either the insertion of x into v succeeds, or v remains unchanged despite the allocation failure.
Basic exception safety
Implemented by ensuring that the count field is guaranteed to reflect the final size of v. For example, if an error is encountered, the insert function might completely deallocate v and reset its count field to zero. On failure, no resources are leaked, but v's old value is not preserved.
No exception safety
An insertion failure might lead to corrupted content in v, an incorrect value in the count field, or a resource leak.

Related Research Articles

C is a general-purpose programming language. It was created in the 1970s by Dennis Ritchie and remains very widely used and influential. By design, C's features cleanly reflect the capabilities of the targeted CPUs. It has found lasting use in operating systems code, device drivers, and protocol stacks, but its use in application software has been decreasing. C is commonly used on computer architectures that range from the largest supercomputers to the smallest microcontrollers and embedded systems.

In computing, a segmentation fault or access violation is a fault, or failure condition, raised by hardware with memory protection, notifying an operating system (OS) the software has attempted to access a restricted area of memory. On standard x86 computers, this is a form of general protection fault. The operating system kernel will, in response, usually perform some corrective action, generally passing the fault on to the offending process by sending the process a signal. Processes can in some cases install a custom signal handler, allowing them to recover on their own, but otherwise the OS default signal handler is used, generally causing abnormal termination of the process, and sometimes a core dump.

<span class="mw-page-title-main">Design by contract</span> Approach for designing software

Design by contract (DbC), also known as contract programming, programming by contract and design-by-contract programming, is an approach for designing software.

Java and C++ are two prominent object-oriented programming languages. By many language popularity metrics, the two languages have dominated object-oriented and high-performance software development for much of the 21st century, and are often directly compared and contrasted. Java's syntax was based on C/C++.

In multi-threaded computer programming, a function is thread-safe when it can be invoked or accessed concurrently by multiple threads without causing unexpected behavior, race conditions, or data corruption. As in the multi-threaded context where a program executes several threads simultaneously in a shared address space and each of those threads has access to every other thread's memory, thread-safe functions need to ensure that all those threads behave properly and fulfill their design specifications without unintended interaction.

<span class="mw-page-title-main">C++</span> General-purpose programming language

C++ is a high-level, general-purpose programming language created by Danish computer scientist Bjarne Stroustrup. First released in 1985 as an extension of the C programming language, it has since expanded significantly over time; as of 1997, C++ has object-oriented, generic, and functional features, in addition to facilities for low-level memory manipulation for systems like microcomputers or to make operating systems like Linux or Windows. It is usually implemented as a compiled language, and many vendors provide C++ compilers, including the Free Software Foundation, LLVM, Microsoft, Intel, Embarcadero, Oracle, and IBM.

Standard ML (SML) is a general-purpose, high-level, modular, functional programming language with compile-time type checking and type inference. It is popular for writing compilers, for programming language research, and for developing theorem provers.

In object-oriented (OO) and functional programming, an immutable object is an object whose state cannot be modified after it is created. This is in contrast to a mutable object, which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change, but the object's state appears unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.

In computer programming, a reference is a value that enables a program to indirectly access a particular datum, such as a variable's value or a record, in the computer's memory or in some other storage device. The reference is said to refer to the datum, and accessing the datum is called dereferencing the reference. A reference is distinct from the datum itself.

In computer science, type safety and type soundness are the extent to which a programming language discourages or prevents type errors. Type safety is sometimes alternatively considered to be a property of facilities of a computer language; that is, some facilities are type-safe and their usage will not result in type errors, while other facilities in the same language may be type-unsafe and a program using them may encounter type errors. The behaviors classified as type errors by a given programming language are usually those that result from attempts to perform operations on values that are not of the appropriate data type, e.g., adding a string to an integer when there's no definition on how to handle this case. This classification is partly based on opinion.

Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically typed programming languages to describe a particular language behavior. In RAII, holding a resource is a class invariant, and is tied to object lifetime. Resource allocation is done during object creation, by the constructor, while resource deallocation (release) is done during object destruction, by the destructor. In other words, resource acquisition must succeed for initialization to succeed. Thus the resource is guaranteed to be held between when initialization finishes and finalization starts, and to be held only when the object is alive. Thus if there are no object leaks, there are no resource leaks.

In computing, a null pointer or null reference is a value saved for indicating that the pointer or reference does not refer to a valid object. Programs routinely use null pointers to represent conditions such as the end of a list of unknown length or the failure to perform some action; this use of null pointers can be compared to nullable types and to the Nothing value in an option type.

Programming languages are used for controlling the behavior of a machine. Like natural languages, programming languages follow rules for syntax and semantics.

In the C++ programming language, new and delete are a pair of language constructs that perform dynamic memory allocation, object construction and object destruction.

In computer programming, a semipredicate problem occurs when a subroutine intended to return a useful value can fail, but the signalling of failure uses an otherwise valid return value. The problem is that the caller of the subroutine cannot tell what the result means in this case.

In object-oriented programming, the dispose pattern is a design pattern for resource management. In this pattern, a resource is held by an object, and released by calling a conventional method – usually called close, dispose, free, release depending on the language – which releases any resources the object is holding onto. Many programming languages offer language constructs to avoid having to call the dispose method explicitly in common situations.

Memory safety is the state of being protected from various software bugs and security vulnerabilities when dealing with memory access, such as buffer overflows and dangling pointers. For example, Java is said to be memory-safe because its runtime error detection checks array bounds and pointer dereferences. In contrast, C and C++ allow arbitrary pointer arithmetic with pointers implemented as direct memory addresses with no provision for bounds checking, and thus are potentially memory-unsafe.

<span class="mw-page-title-main">Rust (programming language)</span> General-purpose programming language

Rust is a general-purpose programming language emphasizing performance, type safety, and concurrency. It enforces memory safety, meaning that all references point to valid memory. It does so without a traditional garbage collector; instead, both memory safety errors and data races are prevented by the "borrow checker", which tracks the object lifetime of references at compile time.

In computer programming, resource management refers to techniques for managing resources.

<span class="mw-page-title-main">Zig (programming language)</span> A general-purpose programming language, toolchain to build Zig/C/C++ code

Zig is an imperative, general-purpose, statically typed, compiled system programming language designed by Andrew Kelley. It is intended as a successor to the language C, with the intent of being even smaller and simpler to program in, while offering more functionality. It is free and open-source software, released under an MIT License.

References

  1. Crichton, Alex (24 July 2015). "Rust RFC: Stabilize catch_panic". The Rust Programming Language. Retrieved 26 May 2022. Code is exception safe if it works correctly even when the functions it calls into throw exceptions.
  2. Lau, Ron (10 November 2020). "Exception safety in JS world". Medium.
  3. Dave Abrahams (2000). Exception-Safety in Generic Components. Generic Programming. Lecture Notes in Computer Science. Vol. 1766. Springer. pp. 69–79. doi:10.1007/3-540-39953-4_6. ISBN   978-3-540-41090-4 . Retrieved 2008-08-29.
  4. Colvin, Gregory (1994). "Exception Safe Exceptions" (PDF). C++ Standards Committee Papers. Retrieved 17 December 2021.
  5. Abrahams, David. "STLport: Exception Handling". www.stlport.org. Retrieved 17 December 2021.
  6. Abrahams, Dave; Colvin, Greg. "Making the C++ Standard Library Exception Safe" (PDF). C++ Standards Committee Papers. Retrieved 17 December 2021.
  7. 1 2 Crichton, Alex (24 July 2015). "Rust RFC: Stabilize catch_panic". The Rust Programming Language. Retrieved 26 May 2022.
  8. Bjarne Stroustrup (1997). Appendix E: Standard-Library Exception Safety in "The C++ Programming Language" (PDF) (3rd ed.). Addison-Wesley. ISBN   0-201-88954-4.
  9. Austern, Matt (30 May 1997). "Standard Library Exception Policy". C++ Standards Committee Papers. Retrieved 26 May 2022.