exception.handling.fundamentals.and.programming
exception.handling.fundamentals.and.programming
Exception Handling
Fundamentals and
Programming
SpringerBriefs in Computer Science
SpringerBriefs present concise summaries of cutting-edge research and practical
applications across a wide spectrum of fields. Featuring compact volumes of 50 to
125 pages, the series covers a range of content from professional to academic.
Typical topics might include:
Briefs allow authors to present their ideas and readers to absorb them with
minimal time investment. Briefs will be published as part of Springer’s eBook
collection, with millions of users worldwide. In addition, Briefs will be available
for individual print and electronic purchase. Briefs are characterized by fast, global
electronic dissemination, standard publishing contracts, easy-to-use manuscript
preparation and formatting guidelines, and expedited production schedules. We
aim for publication 8–12 weeks after acceptance. Both solicited and unsolicited
manuscripts are considered for publication in this series.
**Indexing: This series is indexed in Scopus, Ei-Compendex, and zbMATH **
Pedro Mejia Alvarez • Raul E. Gonzalez Torres
Susana Ortega Cisneros
Exception Handling
Fundamentals and Programming
Pedro Mejia Alvarez Raul E. Gonzalez Torres
CINVESTAV-Guadalajara CINVESTAV-Guadalajara
Zapopan, Jalisco, Mexico Zapopan, Jalisco, Mexico
© The Editor(s) (if applicable) and The Author(s), under exclusive license to Springer Nature Switzerland
AG 2024
This work is subject to copyright. All rights are solely and exclusively licensed by the Publisher,
whether the whole or part of the material is concerned, specifically the rights of translation, reprinting,
reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way,
and transmission or information storage and retrieval, electronic adaptation, computer software, or by
similar or dissimilar methodology now known or hereafter developed.
The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication
does not imply, even in the absence of a specific statement, that such names are exempt from the relevant
protective laws and regulations and therefore free for general use.
The publisher, the authors, and the editors are safe to assume that the advice and information in this
book are believed to be true and accurate at the date of publication. Neither the publisher nor the
authors or the editors give a warranty, expressed or implied, with respect to the material contained herein
or for any errors or omissions that may have been made. The publisher remains neutral with regard to
jurisdictional claims in published maps and institutional affiliations.
This Springer imprint is published by the registered company Springer Nature Switzerland AG
The registered company address is: Gewerbestrasse 11, 6330 Cham, Switzerland
Whether you’re building a simple app or an intricate software system, things can go
wrong. These unexpected events, or "exceptions", can range from trivial matters like
a missing file to severe errors that can crash an entire system. If these exceptions
are not handled, they can lead to unreliable and unpredictable software behavior.
Exception handling is the process of responding to the occurrence of exceptions
– abnormal or exceptional conditions requiring special processing – during the
software’s execution. Exception handling is crucial in producing robust software
that can cope with unexpected situations, ensuring the software is more resilient,
maintainable, and user-friendly.
In this book, we will delve deep into the world of exception handling with examples
written in C++ and Python. Starting with its history and evolution, we will explore
the many facets of exception handling, such as its syntax, semantics, challenges, best
practices, and more. You’ll understand the nuances between syntax and semantic
errors, learn how to employ try-catch blocks effectively, grasp the importance of
logging exceptions, and even delve into advanced exception-handling techniques.
The following chapters will provide you with a comprehensive understanding of
this crucial software development concept:
Chapter 1 provides an introduction, covering the history, various definitions,
and challenges of exception handling. Chapter 2 delves into the basics, offering
insights into the foundational concepts and techniques. Chapter 3 touches upon the
best practices for exception handling, including the differences between errors and
exceptions, the use of assertions, and how to provide meaningful error messages.
Chapter 4 takes a deep dive into advanced exception-handling techniques. Here, we
explore patterns, guard clauses, hierarchical exception handling, and much more.
Finally, chapter 5 focuses on the complexities of exception handling in real-time and
embedded systems.
Whether you are a seasoned developer looking to refine your understanding of
exception handling or a newcomer eager to grasp its essentials, this book offers a clear,
thorough, and practical guide to mastering this crucial area of software development.
As you progress through the chapters, you will acquire the knowledge and skills
needed to handle exceptions effectively, ensuring that your software applications
v
vi Preface
are more robust, reliable, and resilient to unexpected disruptions. Welcome to the
enlightening journey of mastering exception handling!
vii
viii Contents
References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
Chapter 1
Introduction to Exception Handling
1950s and 1960s, assembly language and Fortran were the dominant programming
languages. They relied on fundamental error-checking mechanisms, using simple
branching instructions to handle errors. As a result, programmers have to write
extensive code to handle different types of errors and exceptions, often leading to
less maintainable and more error-prone code.
One of the first programming languages to introduce structured exception han-
dling was PL/I in the 1960s. The language featured condition handling, where specific
conditions could be associated with particular handlers. However, the system still
required manual management of error propagation, making it less flexible and more
error-prone than modern exception-handling systems. The advent of object-oriented
programming languages like C++ and Python played a crucial role in popularizing
the try-catch-finally construct. This construct allowed developers to write more struc-
tured, maintainable code when dealing with exceptions. In addition, the exception-
handling mechanism provided a clear separation of concerns, with specific blocks
of code designated for error handling and recovery.
As programming languages continue to evolve, newer languages have adopted
more advanced and flexible exception-handling constructs. For example, languages
like Python, C#, and Ruby support exception-handling constructs similar to those
found in Java, focusing on making the code more readable and maintainable. Script-
ing languages, such as JavaScript and PHP, also feature their exception-handling
mechanisms. For example, while JavaScript relies on the try-catch-finally
construct, similar to Java and C++, PHP uses try-catch with optional finally blocks.
In conclusion, exception handling has come a long way since the early days of
programming, with each programming paradigm introducing new approaches and
constructs to handle errors and exceptions effectively. Therefore, studying the history
and evolution of exception-handling mechanisms provides valuable insights into the
progress of programming languages and the software development process.
• Null pointer dereference: This situation arises when trying to access or deref-
erence a null pointer, which points to no valid memory location. It can lead to
crashes, segmentation faults, or other memory-related errors.
5 try {
6 if (! ptr)
7 throw std :: runtime_error ("Null pointer dereference
!");
8 *ptr = 10;
9 } catch (const std :: runtime_error & e) {
10 std :: cerr << " Error : " << e.what () << std :: endl;
11 }
12
13 return 0;
14 }
• Out-of-bounds array access: This case occurs when accessing an array element
using an index that exceeds the array’s size or falls outside its defined range. It
can cause unpredictable behavior, memory corruption, or program crashes.
4 1 Introduction to Exception Handling
11 return 0;
12 }
• File not found or inaccessible: This situation occurs when attempting to access
a file that does not exist or is not accessible due to permissions, network issues,
or other reasons. It results in a runtime error or exception.
• Invalid data type conversion or casting: This scenario arises when performing
an invalid data type conversion or casting operation, such as trying to reinterpret a
value of one type as another incompatible type. It can result in undefined behavior,
data corruption, or type-related errors.
These examples illustrate common scenarios where exceptions can occur in pro-
gramming. Exception-handling mechanisms allow developers to catch and handle
these exceptions gracefully, enabling better control over the program’s behavior and
providing error-recovery strategies.
1.2 Definition of Exceptions 5
7 try {
8 if (! file. is_open ())
9 throw std :: runtime_error ("File not found or
inaccessible !");
10 } catch ( const std :: runtime_error & e) {
11 std :: cerr << " Error: " << e.what () << std :: endl;
12 }
13
14 return 0;
15 }
7 try {
8 char* charPtr = reinterpret_cast <char *>( voidPtr );
9 if (! charPtr )
10 throw std :: runtime_error (" Invalid data type
conversion or casting !");
11 } catch ( const std :: runtime_error & e) {
12 std :: cerr << " Error: " << e.what () << std :: endl;
13 }
14
15 return 0;
16 }
16 return 0;
17 }
Exception objects and structures provide a way to encapsulate and convey in-
formation about exceptional conditions in a program. By using exception objects,
developers can give detailed error messages, stack traces, and other relevant infor-
mation that can aid in debugging and handling exceptional situations. The specific
implementation details and syntax may vary across programming languages, but the
concept of using exception objects remains consistent. Understanding how excep-
tions are represented and handled in different languages allows developers to write
robust, error-tolerant code.
4 void readFile () {
5 throw std :: runtime_error ("File not found");
6 }
7 int main () {
8 try {
9 readFile ();
10 } catch ( const std :: exception & e) {
11 // Handle the exception
12 }
13
14 return 0;
15 }
1 try {
2 // Code that might throw an exception
3 } catch ( ExceptionType1 &e1) {
4 // Handle exception of type ExceptionType1
5 } catch ( ExceptionType2 &e2) {
6 // Handle exception of type ExceptionType2
7 }
1 try:
2 # Code that might raise an exception
3 except ExceptionType1 as e1:
4 # Handle exception of type ExceptionType1
5 except ExceptionType2 as e2:
6 # Handle exception of type ExceptionType2
7 finally :
8 # This block is always executed
1 try:
2 x = 1 / 0
3 except ZeroDivisionError :
4 print ("Error: Division by zero.")
1 try {
2 int result1 = 10 / 0;
3 int result2 = result1 + 2;
4 } catch ( ArithmeticException e) {
5 System .out. println (" Error : Division by zero.");
6 }
1 class MyClass :
2
3 def __init__ (self , value):
4 self. value = value
5 def divide_by (self , divisor ):
6 try:
7 return self. value / divisor
8 except ZeroDivisionError :
9 print("Error in MyClass : Division by zero.")
10 return None
11
can arise from a wide range of sources, including user input, hardware failures,
network issues, and software bugs. By implementing proper exception handling,
programs can detect and handle these errors in a controlled and graceful manner,
preventing the program from crashing or terminating prematurely. Consider, for
instance, a program that reads data from a file and performs some calculations on the
data. If the file is not found or cannot be read, the program will encounter an error and
fail to execute. However, with proper exception handling, the program can detect the
error and gracefully exit, providing feedback to the user and avoiding any unexpected
behavior. Another example is a program that performs network communication. If
a network connection is lost or interrupted, the program may encounter an error
and terminate. However, with proper exception handling, the program can detect
the error and attempt to reconnect, ensuring that the communication can continue
without any significant impact on the user.
In software development, errors and exceptions are common occurrences that can
lead to unexpected program behavior, crashes, and incorrect results. Therefore,
understanding the different types of errors and exceptions is essential for developing
robust and reliable software applications that can handle unexpected events and
gracefully recover from errors.
Syntax errors occur when the program’s syntax is incorrect, leading to a compile-
time error. These errors are easy to detect and fix, as they cause the program to fail
to compile. Here’s an example of a syntax error in C programming:
In this example, the program attempts to print a message to the console but needs
to include a semicolon at the end of the line. This results in a syntax error, which
causes the program to fail to compile.
Semantic errors occur when the program’s logic is incorrect [50], leading to unex-
pected or incorrect results. Various factors, such as incorrect calculations, algorithms,
or data types, can cause these errors. Semantic errors can be challenging to detect
and fix, as they may not cause the program to crash or generate an error message.
Here’s an example of a semantic error in Python programming:
• Predictable Software Behavior: Both DbC and exception handling aim to make
software behavior more predictable. While DbC does this through clear con-
tracts, exception handling provides mechanisms to deal with unpredicted scenar-
ios gracefully.
Try-catch blocks are a programming construct that enables the handling of exceptions
or errors during the execution of a program. When an error occurs within a try block,
the program flow is directed to the corresponding catch block, where the error can
be managed gracefully, without crashing the entire program. Many programming
languages, such as Java, C++, 𝐶#, Python, and JavaScript, offer built-in support for
try-catch blocks. In this section, we will explore the concept of try-catch blocks in
various programming languages.
1. Try-Catch blocks in C++: In C++, try-catch blocks are used to manage ex-
ceptions that may occur during the program execution. The following example
demonstrates a simple try-catch block in C++:
4 int main () {
5 int a = 5, b = 0;
6 int result ;
7
8 try {
9 if (b == 0) {
10 throw std :: runtime_error (" Division by zero.");
11 }
12 result = a / b;
13 std :: cout << " Result : " << result << std :: endl;
14 } catch (const std :: runtime_error & e) {
15 std :: cout << " Error : " << e.what () << std :: endl;
16 }
17
18 return 0;
19 }
1 numbers = [1, 2, 3]
2
3 try:
4 print ( numbers [3])
5 except IndexError :
6 print ("Error: List index out of range.")
In this example, we again try to access an array element with an invalid index.
When the exception occurs, the except block is executed, and a descriptive error
message is printed to the console.
2.3 Custom Exceptions Handling 19
2. Python: In Python, the raise keyword is used instead of throw to raise an exception
explicitly. Custom exception classes can be created by inheriting from the built-in
Exception class or one of its subclasses.
The following shows raising a built-in exception.
7 try:
8 raise CustomException ("This is a custom exception .", 42)
9 except CustomException as e:
10 print (f" Caught custom exception : {e}, error code: {e.
error_code }")
2.4 Exception Propagation in Exception Handling 21
12 void functionC () {
13 throw CustomException ();
14 }
15
16 void functionB () {
17 functionC ();
18 }
19
20 void functionA () {
21 try {
22 functionB ();
23 } catch ( const CustomException & e) {
24 std :: cout << " Exception caught in functionA : " << e.what () << std
:: endl;
25 }
26 }
27
28 int main () {
29 functionA ();
30 return 0;
31 }
more efficient and robust software development. In the following subsections, we will
examine the concept of exception propagation in several programming languages.
Each subsection will present a scenario where an exception occurs, demonstrate how
the exception propagates through the call stack, and provide examples illustrating
how to handle the exception at different levels.
1. C++: In C++, exceptions propagate up the call stack similarly to Java. For example,
if an exception is thrown and not caught within a function, it propagates up the call
stack to the calling function. This process continues until the exception is caught
or reaches the main function, resulting in program termination if not handled.
The example above demonstrates how exception propagation works in C++:
• A custom exception class, CustomException, is defined by inheriting from
the std::exception class and overriding the what method.
• The main function calls functionA without any exception handling.
• functionA calls functionB and catches any CustomException that occurs,
printing "Exception caught in functionA."
• functionB calls functionC, but does not handle any exceptions, allowing
them to propagate up the call stack.
• functionC throws a new instance of CustomException, which propagates
up to functionA.
2. Python: In Python, exception propagation works similarly to Java and C++. When
an exception is raised and not caught within a function, it propagates up the call
stack to the calling function. This process continues until the exception is caught
or reaches the top level of the script, where it will result in program termination if
2.5 Nested Try-Catch Blocks 23
not handled. The example above demonstrates how exception propagation works
in Python.
• A custom exception class, CustomException, is defined by inheriting from
the Exception class.
• The main function calls function_a without any exception handling.
• function_a calls function_b and catches any CustomException that oc-
curs, printing Exception caught in function_a.
• function_b calls function_c but does not handle any exceptions, allowing
them to propagate up the call stack.
• function_c raises a new instance of CustomException, which propagates
up to function_a.
There are several benefits to using nested try-catch blocks in exception handling:
• Granularity of error handling: By using nested try-catch blocks, you can handle
errors at a more granular level. This can make it easier to identify and fix problems
in your code.
• Improved program stability: By catching and handling exceptions at a more
granular level, you can improve the stability of your program. This can help
prevent crashes and other errors that could lead to data loss or other problems.
• Simpler code: In some cases, using nested try-catch blocks can simplify your
code. By breaking down error handling into smaller, more manageable blocks,
you can make your code easier to read and understand.
• Better error reporting: By catching and handling exceptions at a more granular
level, you can provide better error reporting to your users. This can help them
understand what went wrong and how to fix the problem.
24 2 Basics of Exception Handling
While there are several benefits to using nested try-catch blocks, there are also some
challenges to be aware of:
• Increased code complexity: Using nested try-catch blocks can make your code
more complex and harder to understand. This can make it harder to maintain and
debug your code over time.
• Performance overhead: Using nested try-catch blocks can result in performance
overhead, as the exception handling code needs to be executed for each try-catch
block. This can impact the performance of your program, especially if you have
many nested try-catch blocks.
• Risk of error masking: If you are not careful, using nested try-catch blocks can
mask errors and make it harder to identify and fix problems in your code. This
is especially true if you catch exceptions too broadly or fail to provide adequate
error reporting.
1 // C++ Example
2 try {
3 // Some code here
4 try {
5 // Some more code here
6 } catch ( exception & e) {
7 // Handle exception from inner try block
8 }
9 } catch ( exception & e) {
10 // Handle exception from outer try block
11 }
2.5.3 Examples
Here are some examples of nested try-catch blocks in several programming lan-
guages.
1. C++ In C++, nested try-catch blocks can also be used to handle exceptions more
granularly. In the example above, an outer try block catches exceptions from the
code within it. An inner try block also catches exceptions from the code within it.
If an exception occurs in the inner try block, it is caught and handled by the catch
block within that block. Likewise, if an exception occurs in the outer try block, it
is caught and handled by the catch block within that block.
2. Python: In Python, nested try-catch blocks are also commonly used. In the
example below, an outer try block catches exceptions from the code within it.
There is also an inner try block that catches exceptions from the code within it. If
an exception occurs in the inner try block, it is caught and handled by the catch
block within that block. Likewise, if an exception occurs in the outer try block, it
is caught and handled by the catch block within that block.
2.6 Conditional Exception Handling 25
1 # Python example
2 try:
3 # Some code here
4 try:
5 # Some more code here
6 except Exception as e:
7 # Handle exception from inner try block
8 except Exception as e:
9 # Handle exception from outer try block
When using nested try-catch blocks, there are some best practices to keep in mind:
• Keep blocks small: It is generally a good idea to keep try-catch blocks as small
as possible. This can make it easier to identify and fix problems in your code.
• Avoid nesting too deeply: Try to avoid nesting too many try-catch blocks. This
can make your code harder to read and understand.
• Use specific catch blocks: Whenever possible, use catch blocks that are specific
to the type of exception you are handling. This can help avoid masking errors and
make it easier to identify and fix problems in your code.
• Provide good error reporting: When catching and handling exceptions, provide
good error reporting to your users. This can help them understand what went
wrong and how to fix the problem.
1. C++: In C++, exception handling is accomplished using try, catch, and throw
constructs. Conditional handling in C++ mirrors the approach in Java, where the
condition is checked within the catch block (see the example below).
In C++, you can rethrow the caught exception using a simple throw; statement
inside the catch block.
26 2 Basics of Exception Handling
2. Python: Python uses the try, except, and raise keywords for exception handling.
Conditional exception handling in Python involves inserting a conditional state-
ment inside the except block.
Python provides the flexibility of catching multiple exceptions, either by specify-
ing them in a tuple or using separate except blocks. The conditional logic inside
each block can then determine the specific handling for each exception type.
1 // Example in C++
2 try {
3 // Code that might throw an exception
4 } catch (std :: exception &e) {
5 if ( someCondition ) {
6 // Handle the exception in one way
7 } else {
8 // Handle it differently or rethrow
9 throw;
10 }
11 }
1 # Example in Python
2 try:
3 # Code that might raise an exception
4 except Exception as e:
5 if some_condition :
6 # Handle the exception in a specific way
7 else:
8 # Handle differently or re - raise
9 raise
Conditional exception handling provides a finer level of control over how excep-
tions are dealt with. By evaluating certain conditions after catching an exception,
developers can craft more nuanced and appropriate responses to exceptional situa-
tions. However, it’s essential to ensure that conditional exception handling doesn’t
complicate the codebase unnecessarily. The primary goal should always be clar-
ity and ensuring that the software behaves predictably and robustly in the face of
unexpected events.
1. C++: Event-driven programming in C++ is often achieved with the help of li-
braries. For this example, we’ll implement a rudimentary event-driven exception
handling system without any external libraries
10 void doSomething () {
11 try {
12 // Some operation that can throw an exception
13 throw "An error occurred ";
14 } catch ( const char* e) {
15 if ( onException ) {
16 onException ( ExceptionEvent ());
17 }
18 }
19 }
20 };
21
22 int main () {
23 MyClass obj;
24 obj. onException = []( ExceptionEvent e) {
25 std :: cout << " Exception handled using an event - driven
approach !" << std :: endl;
26 };
27
28 obj. doSomething ();
29 return 0;
30 }
2. Python: Python, with its dynamic and object-oriented nature, can effortlessly
implement an event-driven exception handling mechanism.
1 class ExceptionEvent :
2 pass
3
4 class MyClass :
5 def __init__ (self):
6 self. _exception_event_handlers = []
7
22 obj = MyClass ()
23 obj. add_exception_event_handler ( handle_exception_event )
24 obj. do_something ()
Errors and exceptions are two types of events that can occur in a program. They differ
in their causes, handling mechanisms, and consequences. This section will explore
the differences between errors and exceptions in more detail.
1. Causes: Both internal and external factors can cause errors. Internal factors
include bugs in the code or the program’s architecture. External factors include
environmental issues such as power outages or network failures. In contrast,
exceptions are caused by specific program logic or behavior errors. Some common
examples of exceptions include invalid input, arithmetic errors, or out-of-memory
errors [43, 79].
2. Handling mechanisms: Errors and exceptions are handled differently in a pro-
gram. When an error occurs, the program terminates, and the error message is
displayed. In contrast, when an exception occurs, the program may continue run-
ning, and the code may handle the exception. Error handling usually requires the
programmer to check for errors manually by inspecting error codes or return val-
ues. Exception handling, on the other hand, is done using try-catch blocks, which
allow the program to handle exceptions in a centralized and structured manner
[68, 75].
3. Consequences: Errors can have severe consequences for the program and its users.
Errors can result in data loss, system downtime, or other negative outcomes. In
contrast, exceptions can be handled without significantly affecting the program’s
overall operation or stability. By handling exceptions gracefully, programs can
provide a better user experience and minimize the impact of errors on the system
[68, 79].
3.2 Errors as Undesired Events 31
• Unexpected outcomes: The occurrence of the exception was not foreseen in the
normal flow of program execution and leads to outcomes that deviate from the
expected behavior.
Disruption in execution: The exception disrupts the standard operational proce-
dure, resulting in the interruption of processes, tasks, or the application itself.
• Potential damage: If left unhandled, the exception could cause damage, like data
corruption, loss of unsaved data, or potentially, in some systems, even physical
harm (e.g., exceptions in embedded systems controlling machinery).
• User experience impact: The exception negatively impacts the user experience,
causing confusion, delays, or forcing the user to restart or rerun operations.
• System resources: The exception may lead to resource leaks, such as memory
not being freed, files left open, or network connections lingering, leading to
inefficiencies or even crashes over time.
• Safety concerns: Especially relevant in mission-critical applications or embedded
systems where an exception can result in safety hazards.
• Requires intervention: An exception that requires manual intervention, either to
fix the underlying cause or to restart services, is generally considered undesired.
• Reproducibility: If the exception is not just a one-time fluke and can be consis-
tently reproduced under certain conditions, it confirms its nature as an undesired
event, requiring attention.
• Inconsistencies in data: Exceptions that lead to data inconsistency, such as
database transaction failures or interrupted data transfers, are considered unde-
sired.
• Impacts business processes: For enterprise applications, any exception that dis-
rupts a business process, delays a workflow, or impedes decision-making can be
seen as an undesired event.
It’s essential to note that almost all exceptions are inherently "undesired" as
they represent disruptions in the expected flow. However, their criticality and the
urgency to address them can vary based on the application, the domain (e.g., finance,
healthcare, entertainment), and the specific circumstances of the exception.
3. Abort: This means signaling a definitive failure. It’s the last resort when no other
attempts seem fruitful or possible. In such scenarios, logging the error for future
reference might be beneficial.
Since ignoring isn’t a viable alternative, once an undesired event is addressed,
there are only two potential outcomes: success if the issue was resolved or a definitive
failure if the resolution attempts were unsuccessful or if the choice was made to abort
directly.
Error codes and return values are traditional mechanisms for handling errors in
software development. Even before the widespread use of exceptions in modern
programming languages, developers relied on these methodologies to signal the
occurrence of errors during the execution of a program.
In the context of exception handling, understanding the role of error codes and
return values is crucial. While they are considered less sophisticated compared to
structured exception handling, they are still prominent, especially in systems where
performance is a critical factor or where full-fledged exception handling might
introduce overhead.
The advantages of using error codes and return values are the following.
• Performance: Exception handling can introduce a performance overhead, es-
pecially if exceptions are thrown frequently. Error codes, being a more direct
mechanism, generally have a predictable and often lower overhead.
• Explicit control flow: With error codes, the control flow is explicit. A function
returns an error, and the caller explicitly checks this return value.
The disadvantages of using error codes and return values are the following.
• Verbose: You have to check after every call if an error occurs. This can lead to a
lot of repetitive code, making the source code longer and harder to read.
• Mistakes: It’s easy to forget to check an error code. If this happens, the program
will continue running as if nothing went wrong, possibly leading to more issues
down the line.
C++ doesn’t have a standard way of returning error codes, but a conventional approach
is to return a status (often an integer or an enum) and use output parameters for
actual function results.
Below is an example of using integer error codes in C++.
34 3 Exception Handling Best Practices
Another approach is to use specific return values to indicate errors. This works best
when the range of valid return values is limited, so certain values can be set aside to
represent errors.
Below is an example in C++ of using return values as errors.
11 int main () {
12 auto result = divide (8, 0);
13 if ( result ) {
14 std :: cout << " Result : " << * result << std :: endl;
15 } else {
16 std :: cerr << " Error: Division by zero." << std :: endl;
17 }
18 return 0;
19 }
3.4 Assertions and Exception Handling 35
In software development, both assertions and exception handling are essential tools
for managing unexpected conditions. However, their primary roles and use cases
are quite distinct. This section delves into the nature of assertions and exception
handling, outlining their purposes, differences, and best practices. Since we already
know what is exception handling we will only describe the concept of assertion.
3.4.1 Assertions
The following are general criteria to decide when to use exceptions vs. exception
handling.
• Debugging vs. runtime errors: Use assertions for conditions that should never
be false unless there’s a bug in the program. Use exception handling for errors
that can legitimately occur during the program’s operation, even if the code is
correct.
36 3 Exception Handling Best Practices
• Severity: Assertions usually indicate severe errors that can’t be recovered from,
leading to immediate termination. Exceptions can sometimes be caught and han-
dled, allowing the program to continue running or fail gracefully.
• Control: Exception handling offers more control than assertions. With exceptions,
you can catch specific types of errors and decide how to handle each one. With
assertions, the program simply crashes.
• Performance: Since assertions are generally disabled in release builds, they don’t
impact the performance of the shipped product. Exceptions, being a runtime
mechanism, do introduce some overhead, but modern compilers and runtimes
have optimized this to a large extent.
In general, the best practices recommended for the use of assertions vs. the use
of exception handling are the following.
• Don’t overuse assertions: While they are useful, peppering code with too many
assertions can make the code hard to read. Use them judiciously to check condi-
tions that would indicate a definite bug in the program.
• Use Meaningful exception messages: When throwing exceptions, provide mes-
sages that help diagnose the problem. This aids debugging and can provide
end-users with useful information about what went wrong.
• Don’t catch every exception: It’s tempting to catch all exceptions to prevent a
program from ever crashing. However, not every exception can be handled mean-
ingfully. Sometimes, especially if the program’s state might be compromised, it’s
better to let it terminate.
• Consider performance: In performance-critical code, the overhead of exception
handling might be a concern. However, it’s essential to balance this with the need
for robust error handling.
Both assertions and exception handling play critical roles in building robust
software. By understanding their distinct purposes and applying them judiciously,
developers can ensure that their software is both bug-free during development and
resilient in the face of unexpected runtime errors.
19 int main () {
20 try {
21 std :: ifstream file("data.txt");
22 if (! file. is_open ()) {
23 throw FileReadException ("data.txt");
24 }
25
26 // Code to read data from the file
27 // ...
28 } catch ( const FileReadException & ex) {
29 std :: cerr << "File read exception : " << ex.what () << std
:: endl;
30 } catch ( const std :: exception & ex) {
31 std :: cerr << " Exception caught : " << ex.what () << std ::
endl;
32 }
33
34 return 0;
35 }
38 3 Exception Handling Best Practices
10 except FileNotFoundError :
11 print(f"Error : The file ’{ filename }’ was not found.")
12 except PermissionError :
13 print(f"Error : You don ’t have permission to read the
file ’{ filename }’.")
14 except UnicodeDecodeError :
15 print(f"Error : The file ’{ filename }’ contains
characters that could not be decoded .")
16 except Exception as e:
17 # Catching any other exceptions not specifically
handled above
18 print(f"An unexpected error occurred : {e}")
19
20 return None
21
22 if __name__ == " __main__ ":
23
29 return 0;
30 }
3.7 Logging Exceptions 41
3.7.1 Objectives
The primary objectives of logging exceptions are:
• Record unexpected events and errors that occur during program execution.
• Provide meaningful and detailed information to diagnose issues.
• Track the sequence of events leading to an exception, facilitating debugging and
problem resolution.
• Monitor application health and performance, allowing proactive actions to prevent
issues.
42 3 Exception Handling Best Practices
3.7.2 Challenges
Different programming languages provide various libraries and frameworks for log-
ging exceptions. This section will present examples of logging exceptions in C++
and Python along with an explanation of the techniques used in each language.
7 try {
8 if ( denominator == 0) {
9 throw std :: runtime_error (" Division by zero");
10 }
11 double result = static_cast <double >( numerator ) / denominator ;
12 SPDLOG_INFO (" Result of division : {}", result );
13 } catch ( const std :: exception & ex) {
14 SPDLOG_ERROR (" Exception caught : {}", ex.what ());
15 }
16 }
17
18 int main () {
19
20 try {
21 divide (10 , 0); // Potential division by zero
22 } catch ( const std :: exception & ex) {
23 SPDLOG_ERROR (" Unhandled exception : {}", ex.what ());
24 }
25
26 return 0;
27 }
3.7 Logging Exceptions 43
1. Logging exceptions in C++: When handling exceptions in C++, it’s often helpful
to log information about the exception for debugging and error-reporting purposes.
Logging exceptions allows you to capture relevant details about the exception,
such as the error message, stack trace, timestamp, and any additional contextual
information. In this section, we’ll explore how to log exceptions in C++.
• Using a logging library: To log exceptions in C++, you can utilize a log-
ging library that provides facilities for logging messages with various levels
of severity. Popular logging libraries in C++ include Boost.Log, spdlog,
and log4cpp. These libraries offer features such as logging to files, console
output, and remote servers, as well as customizable log message formatting
and filtering [7, 18, 26].
44 3 Exception Handling Best Practices
We show above an example using the spdlog library to log exceptions. In this
example, we include the spdlog library and its basic file sink for logging into
a file. We define a function divide that performs a division operation and logs
the result using SPDLOG_INFO. If a std::exception is thrown, it is caught
in the catch block, and the exception message is logged using SPDLOG_ERROR.
The main function calls divide and catches any unhandled exceptions, logging
them with SPDLOG_ERROR. Ensure that you have the spdlog library installed
and properly configured for your project.
• Custom logging function: If you prefer a more lightweight approach or don’t
want to rely on external libraries, you can create your own custom logging
function to log exceptions. An example in C++ is shown above. In this example,
we define a custom function logException that takes a std::exception object
as input. Inside the function, we open an output file stream in append mode and
log the exception details, including the timestamp, using the ctime function
and the what method of the exception object. The log file is closed after writing
the exception details. If the log file fails to open, an error message is printed
to std::cerr. The divide function performs a division operation and catches
any thrown exceptions. When an exception occurs, it calls the logException
function to log the exception details.
2. Logging exceptions in Python: Python provides a built-in logging library, al-
lowing developers to log exceptions easily. The following example demonstrates
how to log an exception using Python’s built-in logging library [25].
When a critical error occurs, an application should avoid complete failure. Instead,
it should transition into a "degraded" mode where essential functionalities still
operate. This approach minimizes user disruption. Moreover, recovery strategies are
mechanisms that the application can use to restore full functionality after an error.
The following is an example of this concept in C++. In the example above, when
the advancedFeature function encounters an error, the application doesn’t crash.
Instead, it degrades to basic functionalities and tries to engage potential recovery
procedures.
14 int main () {
15 try {
16 databaseOperation ();
17 } catch ( const std :: runtime_error & e) {
18 handleDatabaseError (e);
19 }
20 return 0;
21 }
While it might be tempting to use empty catch blocks to "silence" errors temporarily,
this can lead to overlooked issues and hidden bugs. An empty catch block captures
the exception but does nothing about it, essentially ignoring the error. This can be
problematic as it masks potential issues, making debugging more challenging in the
future.
The following is an example of this concept in C++.
5 void riskyOperation () {
6 throw std :: runtime_error ("A risky operation failed .");
7 }
8
9 int main () {
10 try {
11 riskyOperation ();
12 } catch ( const std :: runtime_error & e) {
13 // We avoid leaving this catch block empty
14 std :: cerr << " Error during risky operation : " << e.what ()
<< std :: endl;
15 // Further error handling can be performed here
16 }
17 return 0;
18 }
The foundational principles of exception handling are well-known and widely prac-
ticed. They form the bedrock upon which we build reliable and resilient software.
However, as systems evolve in complexity and as the demands for high availability
and fault tolerance increase, developers often need more nuanced and sophisticated
techniques to manage exceptions effectively. In this chapter, we delve deeper into
the realm of exception handling, exploring advanced patterns and techniques that
seasoned developers employ to enhance the robustness of their applications. From
understanding common patterns like Guard Clauses, Circuit Breakers, and Retry
and Backoff programming’s impact on exception handling, this chapter aims to offer
insights that transcend the basics. We will also consider the challenges and solutions
related to handling exceptions in multi-threaded environments and Hierarchical Ex-
ception Handling, crucial topics in today’s era of concurrent computing. Lastly, we
will not only address the act of exception handling itself but also the crucial clean-up
actions that often accompany it. Ensuring that resources are released and system
states are restored is an art in itself, deserving its spotlight.
Exception handling patterns are methods to deal with different types of errors and
exceptions that arise during the execution of a program. Implementing these patterns
helps to maintain the robustness and reliability of software. In this section, we will
discuss some of the most common exception handling patterns and provide examples
for different programming languages. We will also compare the techniques used in
several languages using a table.
The guard clauses pattern, also known as early returns or preconditions, is an ex-
ception handling technique that focuses on handling exceptional scenarios at the
beginning of a function or method. Instead of nesting the main logic inside a try-
catch block, guard clauses enable you to perform upfront checks and validations to
detect exceptional conditions and handle them appropriately. By using guard clauses,
you can improve the code’s readability and maintainability by reducing the level of
nesting and separating error-handling logic from the main flow of the function. This
pattern promotes the principle of "fail fast" by checking for errors early on and
returning from the function if an exceptional condition is encountered.
Let’s explore how guard clauses can be implemented in C++ and Python with
some examples.
1. C++: In this C++ example, the processFile function takes a filename as
input. The first guard clause checks if the filename is empty and throws
an std::invalid_argument exception if it is. The second guard clause
attempts to open the file using an std::ifstream object and throws an
std::runtime_error exception if the file fails to open. This approach allows
for early detection and handling of exceptional scenarios.
2. Python: In the Python example above, the process_file function takes a file-
name as input. The guard clause checks if the filename is empty and raises a
ValueError exception if it is. The main file processing logic is then encapsu-
lated within a try block. If a FileNotFoundError is raised, it is caught and
transformed into an IOError for consistency or additional processing.
By employing guard clauses, you can enhance the readability and maintainability
of your code by handling exceptional conditions early on. This approach helps
separate error handling logic from the main flow, resulting in more concise and
robust code. Please note that the provided examples are for illustrative purposes and
may require additional error handling and customization based on specific use cases
and requirements.
1 public class ExceptionTranslationExample {
2 public static void main( String [] args) {
3 try {
4 performTask ();
5 } catch ( HighLevelException e) {
6 System .out. println (" Error: " + e. getMessage ());
7 }}
8 public static void performTask () throws HighLevelException {
9 try {
10 lowerLevelOperation ();
11 } catch ( LowLevelException e) {
12 throw new HighLevelException ("A high - level error
occurred .", e);
13 }
14 public static void lowerLevelOperation () throws
LowLevelException {
15 // Code that may throw a LowLevelException
16 }}
17 class LowLevelException extends Exception {
18 public LowLevelException ( String message ) {
19 super ( message );
20 }}
21 class HighLevelException extends Exception {
22 public HighLevelException ( String message , Throwable cause ) {
23 super (message , cause);
24 }}
Wrapper exceptions involve catching the original exception and wrapping it in a cus-
tom exception that hides sensitive information before re-throwing it. This approach
allows developers to maintain a consistent interface for handling exceptions while
keeping the original exception available for further processing if needed [74].
An example in Python using a wrapper exception is shown below.
1 @RestControllerAdvice
2 public class GlobalExceptionHandler {
3 @ExceptionHandler (value = { Exception .class })
4 public ResponseEntity <Object > handleAnyException ( Exception ex ,
WebRequest request ) {
5 return new ResponseEntity <>("A sanitized error message ",
HttpStatus . INTERNAL_SERVER_ERROR );
6 }
7 }
Fixed interval retry is a simple retry strategy where a failed operation is retried
after waiting for a fixed amount of time, regardless of the number of attempts. This
approach is easy to implement and understand. However, it may not be the most
efficient solution, as it does not adapt to the varying nature of transient errors [3,38].
Here is an example of fixed interval retry in Python.
1 import time
2
Randomized interval retry is a variation of the fixed interval retry strategy that
introduces a degree of randomness to the waiting time between retries. This strategy
can help reduce contention when multiple clients experience failures simultaneously
and attempt to retry simultaneously [38].
Below is an example of a randomized interval retry in Python. In this example,
the randomized_interval_retry function calculates the waiting time between
retries by choosing a random value between the minimum and maximum intervals.
1 import time
2 import random
3
4 def randomized_interval_retry (func , retries , min_interval ,
max_interval ):
5 for i in range ( retries ):
6 try:
7 return func ()
8 except Exception :
9 if i < retries - 1:
10 interval = random . uniform ( min_interval , max_interval )
11 time.sleep ( interval )
12 else:
13 raise
21 try {
22 thread .join ();
23 } catch (
24 InterruptedException e) {
25 e. printStackTrace ();
26 }
27 }
28 }
Global exception handling is an approach where exceptions are handled at the ap-
plication level, allowing for a centralized point of exception handling across all
threads in the application. This technique can be useful for capturing and logging
unhandled exceptions or performing application-wide cleanup tasks in response
to an exception [30]. An example of global exception handling in Java using the
setDefaultUncaughtExceptionHandler method is shown below.
For example, suppose a piece of code is reading a file. In that case, a local
exception handler might be designed to handle exceptions related to file access,
such as the file not being found or being inaccessible. These handlers would have
specific knowledge about the file and the operations being performed, allowing
them to handle the exception appropriately - perhaps by trying a different file path
or by prompting the user to select a different file.
2. Higher-Level Exception Handling: If a local exception handler cannot resolve an
exception, the exception is passed up to a higher level of the system. This higher-
level handler will have less context about the specific operation that caused the
exception, but it will have a broader understanding of the overall system state.
Using the previous example, if the local handler for a file access exception cannot
resolve the issue (maybe because the file is missing and the whole directory is
gone), it might pass the exception up to a higher-level handler. This handler might
be designed to handle broader I/O exceptions, such as issues with the disk drive or
network. For example, it could handle the exception by logging the error, alerting
the user, or even redirecting operations to a backup drive.
3. Global Exception Handling
If no other handler can deal with an exception, it is passed to a global handler.
This handler is often a last line of defense, designed to catch any exceptions that
were not caught by any other handlers. It has a minimal understanding of the
specific operations that caused the exception but understood the system’s state as
a whole.
Global handlers are often used to prevent a program from crashing entirely when
an unhandled exception occurs. For example, they might log the exception, alert
the user, and attempt to safely close or restart the program.
Sometimes, an exception may reach the top of the hierarchy without being handled.
For example, this could happen if the system doesn’t have a handler for a particular
type of exception or if all the handlers that could catch the exception could not resolve
it. In these cases, the exception is typically handled by a default exception handler.
The default exception handler is a kind of "catch-all" mechanism that is designed
to handle any exceptions that haven’t been handled elsewhere. This handler may have
very little context about the operations that led to the exception, but it can still take
some actions to prevent the entire system from crashing.
For example, the default handler might log the exception, along with any available
information about the system state when the exception occurred. It might also display
a generic error message to the user, and it could attempt to safely shut down or restart
the system to prevent further errors.
66 4 Advanced Exception Handling Techniques
3 try:
4 # Attempt to open a non - existent file
5 file = open(’non_existent_file .txt ’, ’r’)
6 # Attempt to read from the file
7 content = file.read ()
8 # Attempt to parse the content as an integer
9 number = int( content )
10 except FileNotFoundError :
11 # Handle the case where the file does not exist
12 print ("The specified file does not exist.")
13 except IOError :
14 # Handle other I/O errors , such as issues with file read/
write permissions
15 print ("An I/O error occurred .")
16 except ValueError :
17 # Handle the case where the file content could not be parsed
as an integer
18 print ("The file content could not be parsed as an integer .")
19 except Exception :
20 # Handle all other types of exceptions
21 print ("An unexpected error occurred .")
In this Java example, we try to open a non-existent file, read from it, and parse the
content as an integer. We have separate handlers for FileNotFoundException,
IOException, NumberFormatException, and a generic Exception. These han-
dlers catch and handle the exceptions in a hierarchical manner, from more specific
to more general.
In both these examples, handling the exceptions hierarchically allows us to provide
more specific error messages and recover from the error gracefully. If a particular
handler cannot handle the exception, it is passed up the hierarchy to a more general
handler. This is the essence of Hierarchical Exception Handling.
On the other hand, the drawbacks of Hierarchical Exception Handling may be the
following:
1. Can lead to complex code: If not managed properly, the exception hierarchy can
become overly complex, making the code harder to understand and maintain.
2. Performance overhead: Each try-catch block introduces some performance over-
head. If the program is full of try-catch blocks, it can slow down the execution
speed.
3. Risk of catching too many exceptions: Catching too many exceptions at a
high level in the hierarchy can sometimes hide errors and make debugging more
difficult. It’s important only to catch exceptions that you can meaningfully handle.
70 4 Advanced Exception Handling Techniques
4. Can lead to code duplication: If similar exceptions are caught and handled in
the same way in multiple places, it can lead to code duplication. Developers need
to be mindful of this and strive to handle exceptions at the appropriate level to
avoid unnecessary code duplication.
Clean-up actions are crucial for managing resources such as memory, file handles,
and network connections that a program might allocate during its execution. If these
resources are not adequately released, particularly in the event of an exception,
the system can become unstable, leading to degraded performance or crashes [56].
Clean-up actions typically refer to those actions that release resources or restore the
system to a stable state when an exception occurs. Resources here can mean any
assets that the program uses, such as memory, file handles, database connections,
network sockets, or even hardware resources. Failing to clean up these resources
properly can lead to various issues, such as memory leaks, system instability, or data
corruption.
A clean-up action usually includes, but is not limited to:
• Releasing memory that has been dynamically allocated
• Closing file handles that have been opened
• Closing database or network connections that have been established
• Releasing locks to prevent deadlocks
• Restoring the state of the user interface
It is worth noting that the requirement for clean-up actions arises from the very
nature of exceptions - they alter the normal control flow of a program. When an
exception is raised, the current operation may need to be completed, leaving resources
in an inconsistent or unusable state.
4.6 Clean-Up Actions in Exception Handling 71
1 import sqlite3
2 try:
3 conn = sqlite3 . connect (’test.db’)
4 cursor = conn. cursor ()
5 cursor . execute (’SELECT * FROM my_table ’)
6 except sqlite3 . DatabaseError :
7 print (" Database error occurred ")
8 finally :
9 if conn:
10 conn.close ()
4 int main () {
5 try {
6 std :: unique_ptr <int > ptr(new int (10));
7 // Do something that might throw an exception
8 throw std :: runtime_error ("An exception occurred ");
9 } catch (const std :: runtime_error & e) {
10 std :: cout << e.what () << ’\n’;
11 // The memory allocated by ptr will be automatically
freed here
12 }
13 return 0;
14 }
Modern computing not only demands precise execution but also timely and respon-
sive actions. Nowhere is this truer than in the realm of real-time and embedded
systems, where both the logic and the timing of computation play critical roles.
As systems grow more complex and intertwined, handling anomalies or unexpected
events – commonly known as exceptions – in a graceful manner becomes paramount.
The real-time context adds another layer of intricacy as we not only need to address
the exception but also to ensure that the resolution adheres to strict time constraints.
The pivotal role of exception handling in ensuring system stability and pre-
dictability is undeniable. In this chapter, we will embark on an in-depth exploration
of exception handling as it specifically applies to real-time and embedded systems.
We begin by illustrating some typical examples of exceptions that may arise in real-
time contexts. This sets the stage for a detailed examination of the specific constraints
that real-time systems operate under, from the rigidity of timing to the scarcity of
resources.
Diving deeper, we’ll discuss various types of timing exceptions, shedding light on
both hardware and software perspectives. But in a real-time world, is it enough to just
’handle’ an exception? Prioritization becomes essential. Some exceptions might be
more critical than others, demanding immediate attention, while others can be dealt
with in a deferred manner. This leads us to the fascinating interplay of priorities,
deadlines, and their significance in exception handling.
Another intriguing facet is the synergy (and distinctions) between interrupts and
exceptions, especially in the context of a widely used language like C++. How do
we effectively link them? What are the best practices? And more importantly, can
we leverage them together for optimal real-time performance?
Methodologies play a pivotal role in system design. Therefore, we’ll delve into
established methodologies like recovery blocks and proactive monitoring, each de-
signed to address and resolve exceptions while ensuring system uptime and reliability.
The latter sections will introduce us to design patterns tailored for real-time scenar-
ios, each offering unique strategies and solutions for robust exception handling.
Lastly, as Ada remains a popular choice for real-time system development, we’ll
explore its rich features and constructs designed explicitly for exception handling in
such contexts. From defining exception models to the intricate details of tasking and
exception interactions, this section provides comprehensive insights for developers
and architects working with Ada in real-time environments.
1 import time
2 from datetime import datetime , timedelta
3
Listing 5.1: Structured exception handling example in Python for real-time systems
The unique characteristics of real-time and embedded systems pose distinct chal-
lenges for exception handling compared to traditional application development. For
example, in real-time systems, timing constraints and predictability are crucial, as
these systems are required to meet deadlines and respond to external events within
a specified time frame. On the other hand, embedded systems often operate under
resource constraints, such as limited processing power, memory, or power consump-
tion, which impact the choice and implementation of exception-handling techniques.
One of the main challenges in real-time systems is meeting strict timing constraints.
These systems must respond to events or complete tasks within a specified time
frame, often called deadlines. Failure to meet these deadlines can result in degraded
system performance or even catastrophic failure in safety-critical applications [14].
There are two main types of real-time systems:
• Hard real-time systems: Missing a deadline is considered a system failure. Exam-
ples include avionics and automotive control systems.
• Soft real-time systems: Occasional deadline misses are tolerable but may result in
reduced quality of service. Examples include multimedia and telecommunication
systems.
76 5 Exception Handling in Real-Time and Embedded Systems
Predictability and determinism are crucial for real-time systems, as they ensure
consistent system behavior under varying conditions. This is particularly important
for safety-critical applications, where unpredictable behavior can lead to severe
consequences.
Sources of non-determinism in real-time and embedded systems include:
• Hardware-related factors, such as manufacturing variability, aging, and external
disturbances.
• Software-related factors include dynamic memory allocation, dynamic task cre-
ation, and non-deterministic scheduling policies.
5.3 Types of Timing Exceptions in Real-time Systems 77
Real-time and embedded systems often operate in harsh environments and must
be able to handle various types of faults, including hardware failures, software
bugs, and transient faults caused by external factors (e.g., radiation, temperature,
or electromagnetic interference). Ensuring system reliability and fault tolerance is
essential for maintaining system functionality and avoiding catastrophic failures.
Techniques for improving reliability and fault tolerance in real-time and embedded
systems include redundancy, fault isolation, error detection, and error recovery [63].
Redundancy can be achieved through hardware replication (e.g., triple modular
redundancy) or software techniques such as N-version programming or recovery
blocks. Fault isolation techniques aim to prevent the propagation of faults through-
out the system, using methods such as watchdog timers, sandboxing, and memory
protection. Error detection methods include parity checks, error-correcting codes,
and runtime assertions. Finally, error recovery techniques aim to restore system
functionality after a fault has been detected, using methods such as checkpointing,
rollback, and restart.
Real-time systems, by their very nature, rely heavily on the precise timing of op-
erations. A missed deadline or an unexpected delay can have cascading effects,
sometimes even catastrophic. These timing-related exceptions can be categorized
into hardware timing exceptions and software timing exceptions. Let’s explore each
in detail with examples.
78 5 Exception Handling in Real-Time and Embedded Systems
The following example in Python is about waiting for a sensor reading. Let’s say
a sensor is expected to give a reading every second. A delay beyond this could be a
hardware timing exception.
1 import time
2
Software timing exceptions occur due to software processes taking longer than
expected, mismanagement of resources, or the inherent complexity of certain algo-
rithms that cause them to run longer than the allocated time.
In the following example in C++, a function takes longer than expected to execute.
Let’s consider a scenario where data processing takes longer than its allocated time,
causing a software timing exception.
The following example in Python is similar to the above but in Python language.
Algorithms, especially those with high computational complexity, can sometimes
take longer than expected.
1 import time
2
By wrapping potential problem areas in try blocks and catching specific excep-
tions, we can create more resilient systems that can respond more gracefully to
unexpected events. The catch (C++) or except (Python) clauses allow for handling
these exceptions, giving the program an opportunity to recover or at least terminate
gracefully.
80 5 Exception Handling in Real-Time and Embedded Systems
When developing real-time systems, it’s crucial to account for these timing ex-
ceptions. Both hardware and software perspectives need thorough testing and redun-
dancies to ensure that when a timing exception does occur, the system can either
recover gracefully or fail safely.
15 int main () {
16 try {
17 throw CustomException ("A specific issue occurred !",
ExceptionPriority :: CRITICAL );
18 } catch ( const CustomException & e) {
19 if (e. getPriority () == ExceptionPriority :: CRITICAL ) {
20 std :: cerr << " Critical Error: " << e.what () << std ::
endl;
21 // Handle critical error , maybe reset the system or
halt operations
22 } else if (e. getPriority () == ExceptionPriority :: MINOR) {
23 std :: cerr << "Minor Error : " << e.what () << std :: endl
;
24 // Handle minor error , possibly log it and continue
operations
25 }
26 }
27 return 0;
28 }
5.4 Priorities and Deadlines in Exceptions Handling in Real-time Systems 81
In real-time systems, not all exceptions are of equal urgency. Some exceptions, if not
addressed immediately, can lead to more severe consequences than others. Thus, it
becomes paramount to prioritize exceptions and handle them accordingly.
The example above in C++ demonstrates the usage of an enumeration to priori-
tize exceptions. The CustomException class has a priority attribute, which allows
specific error handling based on its urgency.
In the following example in Python, exception classes are used to differentiate
between a critical error and a minor one. By using separate exception classes, the
catch blocks can distinctly handle each error type based on its criticality.
Meeting deadlines is the hallmark of any real-time system. When exceptions occur,
they can potentially disrupt the system’s ability to meet these deadlines. It’s essential
to ensure that the time spent handling exceptions doesn’t breach any time constraints.
In the following example in C++, the program simulates a time-sensitive task. If
an exception occurs during this task and the time spent in handling the exception
(along with the task’s execution time) exceeds the defined deadline (100 milliseconds
in this example), an error message is displayed.
82 5 Exception Handling in Real-Time and Embedded Systems
1 import time
2 def time_sensitive_task ():
3 time.sleep (0.07) # Simulating some task
4 raise Exception ("An error occurred during the task.")
5 start = time.time ()
6 try:
7 time_sensitive_task ()
8 except Exception as e:
9 print(f" Exception : {e}")
10 end = time.time ()
11 elapsed = end - start
12 if elapsed > 0.1:
13 print ("Error : Missed the deadline !")
14 else:
15 print ("Task completed within deadline .")
5.5 Interrupts and Exception Handling in C++ 83
In computer systems, an interrupt is an event that stops the regular flow of a program
and diverts the control of the CPU to handle a specific task or situation. Interrupts
can originate from various sources, including hardware (like a keyboard input) or
software (a specific instruction in a program). Exception handling in C++, on the
other hand, deals with unexpected or exceptional situations that arise during program
execution. This section will explore the relationship between interrupts and exception
handling in C++ and demonstrate their applications with examples.
In the following example in C++ a timer-based event where after a certain period,
a message is printed.
4 void timerInterrupt () {
5 std :: this_thread :: sleep_for (std :: chrono :: seconds (5));
6 std :: cout << " Timer interrupt called !" << std :: endl;
7 }
8
9 int main () {
10 std :: cout << " Starting main program ..." << std :: endl;
11 timerInterrupt (); // Simulating a software interrupt after 5
seconds
12 std :: cout << "End of main program ." << std :: endl;
13 return 0;
14 }
84 5 Exception Handling in Real-Time and Embedded Systems
Both interrupts and exceptions are mechanisms used by computer systems to deal
with unexpected or prioritized events. However, they serve distinct purposes, orig-
inate from different sources, and are handled differently in the realm of system
design.
The similarities in both mechanisms are the following.
• Event Handling: Both interrupts and exceptions are tools to handle events. An
interrupt typically responds to hardware signals, whereas an exception handles
unusual or unexpected software conditions.
• Priority: Both have a notion of priority. For interrupts, certain types might be
given precedence over others. In software exceptions, some exceptions, because
of their critical nature, might be handled before others.
• Preemption: Both can result in the current execution being halted or preempted
to deal with the event.
There are scenarios where it’s useful to link interrupts with exceptions:
1 data = []
2 checkpoint = []
3
4 def save_checkpoint ():
5 global checkpoint
6 checkpoint = data.copy () # Save current state
7
16 try:
17 save_checkpoint ()
18 modify_data ()
19 except Exception as e:
20 print (f" Exception : {e}")
21 rollback ()
22 print ("Data rolled back to the last checkpoint .")
Both the Recovery Blocks and Checkpoints and Rollbacks methodologies allow
RTS to deal with exceptions in a way that minimizes disruptions and ensures timely
responses.
Here, we’ll dive into how you can implement proactive monitoring for exception
handling in C++ and Python.
In the following C++ example, let’s consider a real-time system that processes
data packets. Proactive monitoring would involve checking the integrity of these
packets before they are processed.
4 class DataPacket {
5 public :
6 bool isValid = true; // A simple flag to denote data
integrity
7 };
8
9 class RealTimeSystem {
10 private :
11 DataPacket data;
12
13 public :
14 void receiveData (const DataPacket & packet ) {
15 data = packet ;
16 // Proactive Monitoring
17 if (! data. isValid ) {
18 throw std :: runtime_error (" Invalid data packet
received !");
19 }
20 process ();
21 }
22
23 void process () {
24 std :: cout << " Processing valid data ..." << std :: endl;
25 // ... Further data processing logic
26 }
27 };
28
29 int main () {
30 RealTimeSystem rts;
31 DataPacket packet ;
32 packet . isValid = false; // Simulating invalid data
33
34 try {
35 rts. receiveData ( packet );
36 } catch ( const std :: runtime_error & e) {
37 std :: cerr << " Exception : " << e.what () << std :: endl;
38 // Here , we can decide what steps to take upon catching
an exception
39 }
40
41 return 0;
42 }
5.7 Design Patterns for Exception Handling in Real-Time Systems 91
In the following Python example, let’s use a similar system that processes data
packets.
1 class DataPacket :
2 def __init__ (self):
3 self. is_valid = True # A simple flag to denote data
integrity
4
5 class RealTimeSystem :
6 def __init__ (self):
7 self.data = DataPacket ()
8
25 try:
26 rts. receive_data ( packet )
27 except Exception as e:
28 print (f" Exception : {e}")
29 # Here , we can decide what steps to take upon catching an
exception
In both the above examples, we proactively checked the integrity of data packets
as they were received. If a packet was deemed invalid, an exception was thrown
immediately, before any processing logic could operate on the potentially corrupted
data. Such proactive monitoring can be further enriched with logging, alarms, or
integrations with incident management systems to ensure that potential issues are
not just detected but also acted upon swiftly and effectively.
The Error Handler Pattern is a commonly used design pattern in software engineering
for handling exceptions in real-time systems. The primary objective of this pattern is
to handle exceptions effectively and to ensure that the system can recover from errors
as quickly as possible. The Error Handler Pattern can be implemented in various
ways, including the use of callbacks, event handlers, and exception [65].
One example of implementing the Error Handler Pattern is by using callbacks. In
this approach, a callback function is registered with the error handler. Whenever an
exception occurs, the error handler calls the registered callback function and passes
the exception as a parameter. The callback function can then take appropriate action,
such as logging the error or displaying an error message to the user. Next listing
shows an example of implementing the Error Handler Pattern using callbacks in
Python.
5.7 Design Patterns for Exception Handling in Real-Time Systems 93
1 class ErrorHandler :
2 def init(self):
3 self. callbacks = []
4 def register_callback (self , callback ):
5 self. callbacks . append ( callback )
6
The Error Handler Pattern provides several advantages for handling exceptions in
real-time systems [35]. First, it enables more granular control over exception han-
dling, allowing developers to define different handlers for different types of ex-
ceptions. This approach can help improve the system’s reliability and reduce the
likelihood of catastrophic failures. Additionally, the Error Handler Pattern provides
a centralized location for handling exceptions, making it easier to maintain and debug
the system.
However, the Error Handler Pattern can also have some disadvantages. One po-
tential issue is that the use of callbacks or event handlers can introduce additional
overhead, which may impact the system’s performance. Additionally, the Error Han-
dler Pattern can increase the complexity of the system, making it more difficult to
understand and maintain.
94 5 Exception Handling in Real-Time and Embedded Systems
The primary challenges in designing and implementing real-time systems stem from
their need to simultaneously manage complex interactions with their environment,
handle concurrency, satisfy precise timing constraints, and maintain high reliability
[49].
5.7 Design Patterns for Exception Handling in Real-Time Systems 95
The State Machine pattern is a behavioral design pattern that allows an object to
change its behavior when its internal state changes. It appears as if the object has
changed its class [27]. This pattern can be particularly beneficial in the context of
RTS. It provides a clear and predictable control flow, which is crucial for meeting
the timing constraints of RTS. It also facilitates easy state transition management
and promotes modularity and maintainability [70]. However, the State Machine
pattern has its drawbacks. For instance, the risk of state explosion, which refers
to the exponential growth of states and transitions with increasing complexity, can
make the state machine difficult to manage and maintain. Additionally, as the state
machine becomes more complex, handling shared resources across different states
can become challenging.
In a State Machine, an application’s behavior is determined by its state, and
it can switch from one state to another if certain conditions are met. Each state
can be considered an individual entity that knows how to handle the events it’s
interested in and how to transition to another state. Consider the example above
where we implement a basic traffic light system as a state machine in C++. This
example, while simple, illustrates the basic principles of the State Machine pattern.
The system changes its state in response to an event or condition (a request in this
case), and different behavior is exhibited depending on the system’s current state.
The State Machine pattern has several advantages in the context of RTS:
1. Predictability: The behavior of the system is predictable as it depends on its
current state and the defined state transitions. This predictability is crucial in RTS
where unexpected behavior can have serious consequences [70].
2. Modularity and Maintainability: The system behavior corresponding to a par-
ticular state is encapsulated within that state. This separation of concerns leads to
highly modular code, which is easier to maintain and debug [37].
3. Extensibility: New states can be added to the system without disturbing the exist-
ing states, making the system highly extensible. It helps in adapting to changing
requirements or adding new features [9].
While the State Machine pattern is highly beneficial, it also comes with its own
set of challenges:
1. State Explosion: As the number of states and transitions in the system increases,
managing and maintaining them can become challenging. This problem, known as
state explosion, can complicate the design and implementation of the system [16].
2. Complexity: Although the pattern can make the system more modular and main-
tainable, it can also increase the complexity of the system, particularly when the
number of states and transitions is large. This can lead to errors and increase the
time required for testing and verification [16].
96 5 Exception Handling in Real-Time and Embedded Systems
In the field of Real-Time Systems (RTS), the Supervisory Control pattern emerges
as an essential tool to orchestrate and manage complex operations. This pattern is
applied in a variety of systems, including manufacturing, automation, power grids,
and software systems, where a supervisory controller oversees the operation of
lower-level controllers or subsystems [85].
The supervisory controller ensures that the system operates within the specified
constraints and takes corrective actions whenever necessary. It also makes high-level
decisions based on the overall state of the system, while lower-level controllers or
subsystems focus on their specific tasks. This hierarchical structure helps manage
complexity and improve system modularity. However, implementing the Supervisory
Control pattern in RTS is not a trivial task. The supervisory controller must be
designed to handle multiple concurrent tasks, manage shared resources, and make
decisions under stringent timing constraints, which adds to the complexity of the
system [49]. Another challenge is to ensure that the supervisory controller can
respond in real-time to changes in the system and maintain the overall stability and
performance of the system.
4 class Subsystem {
5 public :
6 virtual void execute () = 0;
7 };
8
23 class SupervisoryController {
24 std :: vector <std :: thread > subsystems ;
25 public :
26 SupervisoryController () {
27 subsystems . push_back (std :: thread ([]( Subsystem * s){ s->
execute (); }, new Subsystem1 ()));
28 subsystems . push_back (std :: thread ([]( Subsystem * s){ s->
execute (); }, new Subsystem2 ()));
29 }
30 void control () {
31 for (std :: thread & subsystem : subsystems ) {
32 subsystem .join ();
33 }
34 }
35 };
The supervisory controller could also include safety mechanisms to handle sub-
system failures or to shut down the system in a controlled way if necessary. For
instance, it might monitor the execution time of the subsystems and take corrective
actions if a subsystem takes too long to complete its task, which could indicate a
problem.
The Supervisory Control pattern provides several advantages in the context of RTS.
1. Centralized Control: The pattern allows for centralized decision-making, which
can simplify the control logic and make the system more predictable.
98 5 Exception Handling in Real-Time and Embedded Systems
Ada’s approach to exception handling in real-time contexts Ada, having been de-
signed with high-reliability, safety-critical, and real-time systems in mind, offers a
robust framework for exception handling that addresses the unique challenges of
real-time environments. This deep-rooted commitment to safety and determinism in
the face of exceptions is manifested in several facets of the language, which will be
described in the following sections [1, 13, 21].
5.8 Exception Handling in Ada for Real-Time Systems 99
In Ada, before an exception can be raised or handled, it must first be declared. The
declaration gives a name to the exception and allows it to be identified in subsequent
parts of the program.
An example of an exception declaration follows.
1 Buffer_Overflow : exception ;
Once an exception is declared, it can be raised using the raise statement followed by
the name of the exception. Optionally, a string can be added to provide a detailed
explanation about the exception.
An example of an exception raising follows.
In this example, if the size of a buffer exceeds its predefined capacity, the
Buffer_Overflow exception is raised with a descriptive message.
1 begin
2 -- some code that might raise Buffer_Overflow
3 exception
4 when Buffer_Overflow =>
5 -- corrective actions to handle the buffer overflow
6 Reset_Buffer ;
7 Put_Line (" Buffer was reset due to an overflow .");
8 end;
1 procedure Outer_Procedure is
2 Buffer_Underflow : exception ;
3
4 procedure Inner_Procedure is
5 begin
6 -- some operations
7 raise Buffer_Underflow ;
8 exception
9 when others =>
10 -- generic handler
11 Put_Line (" Handled in Inner_Procedure .");
12 end Inner_Procedure ;
13
14 begin
15 Inner_Procedure ;
16 exception
17 when Buffer_Underflow =>
18 -- specific handler
19 Put_Line (" Buffer underflow detected in Outer_Procedure .");
20 end Outer_Procedure ;
Ada’s approach to exception handling emphasizes safety and predictability, which are
paramount in real-time systems. One of the key features that support this approach is
Ada’s requirement for explicit exception declarations. This ensures that exceptions
are used intentionally and that their propagation paths are always clear. In many
languages, an exception can be raised (or thrown) without being explicitly declared.
While this offers flexibility, it can also lead to unpredictability, especially in complex
systems. Ada, however, opts for safety over flexibility by necessitating exception
declarations.
Explicit declarations offer a number of advantages:
• Readability and Maintainability: By merely looking at a package or a subpro-
gram specification, a developer can immediately understand the exceptions that
can be raised. This clear contract simplifies maintenance and debugging.
• Safety: Ada’s compiler checks to ensure that only declared exceptions are raised.
This prevents inadvertent errors and enforces disciplined coding practices.
• Documentation: Exception declarations serve as an implicit form of documen-
tation, making the code’s intent clear and aiding in system analysis.
1 Exception_Name : exception ;
2 procedure File_Processor is
3 File_Not_Found : exception ;
4 procedure Read_File is
5 begin
6 -- hypothetical situation where the file doesn ’t exist
7 raise File_Not_Found ;
8 end Read_File ;
9 begin
10 Read_File ;
11 exception
12 when File_Not_Found =>
13 Put_Line (" Error: File not found.");
14 end File_Processor ;
Exceptions in Ada are first-class types, and they must be declared before they can be
raised or handled. Let’s look at the syntax and an illustrative example.
An example of the syntax for declaration in ADA is above. In this example, the ex-
ception File_Not_Found is declared and then raised within the nested Read_File
procedure. The outer File_Processor procedure handles this exception.
5.8 Exception Handling in Ada for Real-Time Systems 103
3 procedure Data_Processor is
4 Data_Error : exception ;
5
6 procedure Process is
7 begin
8 -- hypothetical situation where data processing fails
9 raise Data_Error with "Data out of range ";
10 end Process ;
11
12 begin
13 Process ;
14 exception
15 when Data_Error =>
16 Put_Line (" Error encountered : " & Exception_Message );
17 end Data_Processor ;
1 procedure Process_Data is
2 Invalid_Data : exception ;
3 procedure Validate is
4 begin
5 -- Let ’s say validation fails
6 raise Invalid_Data ;
7 end Validate ;
8 procedure Compute is
9 begin
10 Validate ;
11 exception
12 when Invalid_Data =>
13 Put_Line (" Compute : Data validation failed .");
14 end Compute ;
15 begin
16 Compute ;
17 exception
18 when Invalid_Data =>
19 Put_Line (" Process_Data : Data invalid . Cannot proceed .");
20 end Process_Data ;
1 procedure Transaction_Processor is
2 Transaction_Error : exception ;
3
4 procedure Execute_Transaction is
5 begin
6 -- Simulate a transaction that fails
7 raise Transaction_Error ;
8 end Execute_Transaction ;
9
10 begin
11 begin
12 Execute_Transaction ;
13 exception
14 when Transaction_Error =>
15 Put_Line ("Inner Block: Transaction failed .");
16 -- Maybe try to redo the transaction here
17 end;
18
19 -- Other unrelated code can go here without being affected by
the above exception handling
20
21 exception
22 when Transaction_Error =>
23 Put_Line (" Outer Block: Critical failure in transaction
processing .");
24 end Transaction_Processor ;
5.8 Exception Handling in Ada for Real-Time Systems 105
Scoped handling refers to handling exceptions within a specific portion of the code.
This allows for fine-grained control over which exceptions are handled and where
ensuring specific responses in well-defined parts of the code. Ada achieves this
through the use of blocks, where each block can have its exception-handling section.
An example of the scoped exception handling is above. In the example above, the
inner block tries to handle Transaction_Error, and if that fails, the outer block
can catch it.
The concurrency mechanism in Ada, known as tasking, plays a crucial role in real-
time systems development. Since real-time systems often run multiple operations
concurrently to meet stringent timing requirements, understanding how tasking in-
teracts with exception handling is of paramount importance. Ada has integrated
mechanisms that handle exceptions raised in tasks, ensuring that exceptions in par-
allel activities do not jeopardize the entire system.
In Ada, when a task raises an exception and doesn’t handle it within its own scope,
the exception propagates to the master of the task (which can be the main program or
another task). The exception will then be handled in the master’s exception handlers,
if present.
An example of exception propagation is the following.
If the exception raised in Task_Example isn’t handled within the task body, it
would be propagated and caught by the main program’s exception handler.
When an exception is raised within a task and isn’t caught, the task is terminated.
However, the other tasks can continue their execution unless they are dependent on
the terminated task or are affected by the propagated exception. This ensures that a
fault in one task doesn’t necessarily lead to a complete system breakdown.
An example of task termination is the following.
2 task T1;
3 task T2;
4 task body T1 is
5
6 begin
7 -- Some processing
8 raise Program_Error ;
9 end T1;
10
11 task body T2 is
12 begin
13 -- Some other processing
14 end T2;
15
16 begin
17 -- Main program execution
18
19 exception
20 when Program_Error =>
21 Put_Line (" Exception from T1 caught in main program ");
22 end;
Ada supports Asynchronous Transfer of Control (ATC), where one task can asyn-
chronously interrupt another. This is especially relevant for real-time systems where
one might need to preempt a long-running operation if a more critical task arrives.
Exceptions play a key role in the ATC mechanism.
5.8 Exception Handling in Ada for Real-Time Systems 107
An example of ATC and Exceptions is the following. In this case, T4 cancels the
operation in T3 using the ATC mechanism, which raises the Cancellation exception
in T3.
1 task T3;
2 task T4;
3
4 task body T3 is
5 Cancellation : exception ;
6 begin
7 delay 5.0; -- Simulate some long - running operation
8 raise Cancellation ;
9 end T3;
10
11 task body T4 is
12 begin
13 delay 1.0; -- Wait for a short while
14 T3. Asynchronous_Transfer_of_Control ;
15 end T4;
16
17 begin
18 -- Main program
19 exception
20 when Cancellation =>
21 Put_Line ("T3 was cancelled by T4");
22 end;
Real-time systems are characterized by strict timing requirements. The actions taken
within exception handlers must thus be timely and predictable. This is particularly
significant in Ada given its widespread use in safety-critical domains like aerospace
and defense. Below we delve into the implications of real-time considerations in the
context of handler actions in Ada.
In a real-time context, not only must exceptions be caught, but the handling of these
exceptions must also be completed within a predictable and guaranteed timeframe.
For instance, consider a scenario where a sensor’s data needs to be processed within
a given deadline. If the sensor returns a malfunction error, the system should handle
this exception and possibly switch to a backup sensor within a defined time.
108 5 Exception Handling in Real-Time and Embedded Systems
1
2 procedure Process_Sensor_Data is
3 begin
4 -- Assume Get_Sensor_Data can raise a Sensor_Error
5 Data := Get_Sensor_Data ;
6 exception
7 when Sensor_Error =>
8 -- Handler action
9 Data := Get_Backup_Sensor_Data ;
10 if Clock < Deadline then
11 -- Process backup data within time
12 Process (Data);
13 else
14 -- Log or take alternative action
15 Log_Error (" Sensor processing missed deadline ");
16 end if;
17 end Process_Sensor_Data ;
Exception handling must introduce minimal overhead to ensure that the system
remains responsive. This means the code within the exception handler should be as
concise as possible, especially in high-frequency exception scenarios.
An example of the minimal overhead is the following.
1 procedure Communicate is
2 begin
3 -- Assume Send can raise a Communication_Error
4 Send(Data);
5 exception
6 when Communication_Error =>
7 -- Minimal handler action
8 Increment_Error_Counter ;
9 Retry(Send , Data); -- Retry method with bounded attempts
10 end Communicate ;
Here, instead of complex logging or recovery logic directly inside the handler, a
concise action is taken to ensure minimal overhead.
Real-time systems often have bounded recovery actions to ensure they don’t get stuck
in indefinite recovery loops. This is vital for maintaining real-time guarantees.
An example of the bounded recovery actions is the following.
5.8 Exception Handling in Ada for Real-Time Systems 109
1
2 procedure Update_System is
3 Attempts : Integer := 0;
4 begin
5 -- Assume Update can raise an Update_Failed error
6 Update ;
7 exception
8 when Update_Failed and then Attempts < Max_Attempts =>
9 -- Increment and retry
10 Attempts := Attempts + 1;
11 Retry ( Update );
12 when others =>
13 Log_Error ("Max update attempts reached ");
14 end Update_System ;
1
2 task type Critical_Task is
3 ...
4 end Critical_Task ;
5
6 procedure Handle_Exception is
7 begin
8 -- Assume Handle can raise a Critical_Error
9 Handle (Data);
10 exception
11 when Critical_Error =>
12 -- Non - blocking handler action
13 Signal_Alarm ;
14 -- Allow preemption for critical tasks
15 Yield_To ( Critical_Task );
16 end Handle_Exception ;
References
1. ISO/IEC JTC 1/SC 22/WG 9. Ada 2012 Reference Manual. Language and Standard Libraries:
International Standard ISO/IEC 8652/2012 (E), 2012.
2. J. Albahari and B. Albahari. C# 8.0 in a Nutshell: The Definitive Reference. O’Reilly Media,
Inc., 2020.
3. John Allspaw. The Art of Capacity Planning: Scaling Web Resources in the Cloud. O’Reilly
Media, 2010.
4. N.C. Audsley, A. Burns, M.F. Richardson, and A.J. Wellings. Hard real-time scheduling: The
deadline-monotonic approach. Proceedings of the IEEE Workshop on Real-Time Operating
Systems and Software, pages 133–137, 1991.
5. H. Aydin, Q. Yang, and R. Melhem. Energy-aware partitioning for multiprocessor real-
time systems. Proceedings of the 17th International Parallel and Distributed Processing
Symposium, pages 1135–1142, 2001.
6. Microsoft Azure. Azure architecture center: Transient fault handling, 2021.
7. Bastiaan et-al. Bakker. Log for c++ project, 2021.
8. T. Ball and S.K. Rajamani. Automatically validating temporal safety properties of interfaces.
SPIN, pages 103–122, 2001.
9. Grady Booch, James Rumbaugh, and Ivar Jacobson. Unified modeling language user guide,
The. Addison-Wesley Reading, 1999.
10. Rory Bradford, Mark Erlank, and Andries Van der Berg. Using decorrelated jitter in backoff
algorithms to improve resilience to distributed denial-of-service attacks. In Proc. of the 2018
Annual Conference of the South African Institute of Computer Scientists and Information
Technologists (SAICSIT), pages 27:1–27:7, 2018.
11. Eckel Bruce. Thinking in Java. Prentice Hall, 2006.
12. A. Burns and A. Wellings. Predicting the behaviour of ada programs. Software Engineering
Journal, 8(4):173–181, 1993.
13. Alan Burns and Andy Wellings. Real-Time Systems and Programming Languages: Ada,
Real-Time Java and C/Real-Time POSIX. Addison Wesley, 2012.
14. Giorgio Buttazzo. Hard Real-Time Computing Systems: Predictable Scheduling Algorithms
And Applications. Springer, 2011.
15. A. Cervin, D. Henriksson, B. Lincoln, J. Eker, and K. Årzén. How does control timing affect
performance? analysis and simulation of timing using jitterbug and truetime. IEEE Control
Systems Magazine, 23(3):16–30, 2002.
16. Edmund M Clarke, Orna Grumberg, and Doron A Peled. Model Checking. MIT Press, 1999.
17. N. Collins. Effective logging: Best practices for logging in applications. DZone, 2019.
18. The Boost C++ Community. Chapter 1. boost.log v2, 2021.
19. Cplusplus. Exceptions. http://www.cplusplus.com/doc/tutorial/exceptions/.
20. Jeffrey Dean. Tail at scale. Communications of the ACM, 56(2):74–80, 2013.
21. Truc Dinh et al. Exception handling patterns in real-time systems. In International Conference
on Real-Time Systems, 2015.
22. Allen Downey. Think Python: How to Think Like a Computer Scientist. Green Tea Press, 2008.
23. J. Engblom. Reducing the complexity of complex real-time systems. PhD thesis, 2000.
24. K. Flautner and T. Mudge. Vertigo: Automatic performance-setting for linux. Proceedings of
the 5th Symposium on Operating Systems Design and Implementation, pages 105–116, 2001.
25. Python Software Foundation. Python logging - basic logging tutorial. https://docs.
python.org/3/howto/logging.html.
26. Andrew Gabriel and Guilbert Pollo. Fast c++ logging library, 2021.
27. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design patterns: elements
of reusable object-oriented software. Addison-wesley, 1994.
28. Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design patterns: elements
of reusable object-oriented software. Addison-Wesley Professional, 1995.
29. D. Gay and P. Levis. Bridging the gap: Programming sensor networks with application-specific
virtual machines. ACM Transactions on Sensor Networks, 5(4):1–31, 2009.
© The Author(s), under exclusive license to Springer Nature Switzerland AG 2024 110
P. Mejia Alvarez et al., Exception Handling, SpringerBriefs in Computer Science,
https://doi.org/10.1007/978-3-031-50681-9
References 111
30. B. Goetz, T. Peierls, J. Bloch, J. Bowbeer, D. Holmes, and D. Lea. Java Concurrency in
Practice. Addison-Wesley, 2006.
31. John B Goodenough. Exception handling: issues and a proposed notation. Communications
of the ACM, 18, Issue 12:683–696, 1975.
32. James Gosling, Bill Joy, Guy Steele, and Gilad Bracha. The Java Language Specification.
Addison-Wesley Professional, 2005.
33. James Gosling, Bill Joy, Guy Steele, Gilad Bracha, and Alex Buckley. The Java Language
Specification. Addison-Wesley, 3 edition, 2000.
34. James Gosling, Bill Joy, Guy L. Steele Jr, Gilad Bracha, and Alex Buckley. The Java Language
Specification, Java SE 8 Edition. Addison-Wesley, 2014.
35. Bahram Hamidzadeh, Janos Sztipanovits, and Changjun Jiang. Runtime support for the error
handler pattern in distributed real-time systems. In Proceedings of the 18th IEEE International
Symposium on Object-Oriented Real-Time Distributed Computing (ISORC’03), pages 332–
341. IEEE, 2003.
36. S. Hanenberg and R. Unland. Exception handling in object-oriented programming languages:
A survey. In Proceedings. 14th International Workshop on Database and Expert Systems
Applications, pages 294–298. IEEE, 2003.
37. David Harel. Statecharts: A visual formalism for complex systems. Science of computer
programming, 8(3):231–274, 1987.
38. Joseph L. Hellerstein, Yixin Diao, Sujay Parekh, and Rodney Griffith. Feedback Control of
Computing Systems. John Wiley and Sons, 2002.
39. Apple Inc. Error handling. https://docs.swift.org/swift-
book/LanguageGuide/ErrorHandling.html.
40. Lee Jaewook. C++ multithreading: Thread exception handling. Dr. Dobb’s Journal, 2006.
41. Mathew Johnson and Patrick Thomas. A survey on the role of static analysis in software
verification. Journal of Computer Science and Technology, 25(6):1203–1216, 2010.
42. Robert Johnson and Michael Brown. Improving application security with exception filters. In
International Conference on Secure Software Engineering, 2018.
43. Brian W Kernighan and Dennis Ritchie. C Programming Language. Pearson Education, 2015.
44. M. Klein, T. Ralya, B. Pollak, R. Obenza, and M. Gonzales. A practitioner’s handbook for
real-time analysis: Guide to rate monotonic analysis for real-time systems. Kluwer Academic
Publishers, 1993.
45. Tom Lee and Kevin Park. A centralized approach to exception handling for robust applications.
Journal of Computer Science and Technology, 33(2):250–265, 2020.
46. B. Lewis and D.J. Berg. Multithreaded Programming with PThreads. Prentice Hall, 2004.
47. Ming Li and Jong-Deok Choi. Checkpointing and rollback-recovery for distributed real-time
systems. Journal of Systems Architecture, 51(4):213–226, 2005.
48. C.L. Liu and J.W. Layland. Scheduling algorithms for multiprogramming in a hard-real-time
environment. Journal of the ACM, 20(1):46–61, 1973.
49. Jane W. S. Liu. Real-time systems. Prentice Hall Press, 2000.
50. Qiang Liu, Jun He, and Xing Liu. A study on semantic errors in exception handling. Journal
of Computer and Communications, 4(12):103–109, 2016.
51. C. Lu, J.A. Stankovic, G. Tao, and S.H. Son. Design and evaluation of a feedback control edf
scheduling algorithm. IEEE Transactions on Computers, 54(6):706–720, 2005.
52. H. Mamaghanian, N. Khaled, D. Atienza, and P. Vandergheynst. Compressed sensing for
real-time energy-efficient ecg compression on wireless body sensor nodes. IEEE Transactions
on Biomedical Engineering, 58(9):2456–2466, 2011.
53. Ramiro Martinez and Javier Lopez. Proactive monitoring for fault-tolerant real-time systems.
In International Conference on Real-Time Computing, pages 85–94. IEEE, 2012.
54. Bertrand Meyer. Object-Oriented Software Construction. Prentice Hall, Upper Saddle River,
NJ, USA, 2nd edition, 1997.
55. Microsoft. Handling and throwing exceptions. https://docs.microsoft.com/en-
us/dotnet/standard/exceptions/.
56. Robert Miller and Anand Tripathi. Issues with exception handling in object-oriented systems.
In ECOOP’97—Object-Oriented Programming, pages 85–103. Springer, 1997.
112 References
57. Gustavo Niemeyer and Python Software Foundation. Pep 344 – exception chaining and
embedded tracebacks. https://www.python.org/dev/peps/pep-0344/, 2006.
58. R. Oliva-Garcia, C.M. Fischer-Rubira, and A. Romanovsky. A survey of exception handling
in the c++ standard. Journal of Systems and Software, 80(7):1054–1077, 2007.
59. Oracle. Lesson: Exceptions. https://docs.oracle.com/javase/tutorial/essential/exceptions/.
60. Oracle. Thread.setdefaultuncaughtexceptionhandler. https://docs.oracle.com/javase/8/docs/
api/java/lang/Thread.htmlsetDefaultUncaught ExceptionHandlerjava.lang.Thread.
UncaughtExceptionHandler-.
61. Oracle. The java™ tutorials: Handling errors using exceptions.
https://docs.oracle.com/javase/tutorial/essential/exceptions/, n.d.
62. Python Org. Python documentation. https://docs.python.org/3/tutorial/errors.
html, 2021.
63. D.J. Pradhan. Fault-Tolerant Computer System Design. Prentice Hall, 1996.
64. David Pritchard. Exceptions in C++: The Definitive Guide. Packt Publishing, 2020.
65. L. Pullum, J. Gonzalez, and R. DeLong. Error handling and java exception design in real-time
systems. In Proceedings 10th IEEE International Workshop on Object-oriented Real-time
Dependable Systems, pages 27–34, 2001.
66. Python Software Foundation. Errors and exceptions. https://docs.python.org/3/
tutorial/errors.html.
67. Arnold Robbins. A history of the development of exception handling in programming lan-
guages. ACM SIGPLAN Notices, 38(10):16–25, 2003.
68. McConnell S. Code Complete: A Practical Handbook of Software Construction. Microsoft
Press, 2004.
69. Romany Saad. Exception aggregation – a new validation pattern. https://medium.com/nerd-for
tech/exception-aggregation-a-new-validation-pattern-71d26f2ffee8, 2021.
70. Miro Samek. Practical statecharts in C/C++. CMP Books, 2002.
71. Herbert Schildt. C++: The Complete Reference, Fourth Edition. McGraw-Hill, 2002.
72. L. Sha, T. Abdelzaher, K. Årzén, A. Cervin, T. Baker, A. Burns, G. Buttazzo, M. Caccamo,
J. Lehoczky, and A. Mok. Real time scheduling theory: A historical perspective. Real-Time
Systems, 28(2-3):101–155, 2004.
73. Jason Smith and James Anderson. Recovery blocks in real-time systems. In Proceedings of
the Real-Time Systems Symposium, pages 194–203. IEEE, 1998.
74. John Smith and Jane Doe. Effective error handling with wrapper exceptions. Journal of
Software Engineering, 25(4):123–137, 2019.
75. Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley, 2013.
76. H. Sutter. Exceptional C++ Style. Addison-Wesley, 2005.
77. Herb Sutter. Exceptional C++. Addison-Wesley Professional, Boston, MA, 1st edition, 2000.
78. Herb Sutter. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solu-
tions. Addison-Wesley, 2000.
79. Budd T. Computer Science: An Overview. Pearson, Boston, MA, 12th ed edition, 2015.
80. Apache Tomcat. Error handler. https://tomcat.apache.org/.
81. Stephen Toub. Aggregating exceptions. MSDN Magazine, 24(08), August 2009.
82. Guido Van Rossum and Fred L. Drake Jr. Python Essential Reference. Addison-Wesley, 2007.
83. R. Wilhelm, J. Engblom, A. Ermedahl, N. Holsti, S. Thesing, D. Whalley, G. Bernat, C. Ferdi-
nand, R. Heckmann, T. Mitra, F. Mueller, I. Puaut, P. Puschner, J. Staschulat, and P. Stenström.
The worst-case execution-time problem—overview of methods and survey of tools. ACM
Transactions on Embedded Computing Systems, 7(3):1–53, 2008.
84. P.R. Wilson, M.S. Johnstone, M. Neely, and D. Boles. Dynamic storage allocation: A survey
and critical review. Proceedings of International Workshop on Memory Management, pages
1–116, 1997.
85. W. M. Wonham. Supervisory Control of Discrete Event Systems. Springer, 2018.