From cc5aa2093127e9676e5ae67e22b6424460d34979 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Thu, 1 Jul 2021 14:16:46 +0200
Subject: [PATCH] ENH: simplify exit/abort handling and jobinfo (code
 reduction)

- handle failures more robustly
- add static shutdown() for similarity with Pstream etc.
---
 applications/test/error/Test-error.C          |  54 +++--
 src/OSspecific/MSwindows/signals/sigFpe.C     |   4 +-
 src/OSspecific/MSwindows/signals/sigInt.C     |   4 +-
 src/OSspecific/MSwindows/signals/sigQuit.C    |   4 +-
 src/OSspecific/MSwindows/signals/sigSegv.C    |   4 +-
 .../MSwindows/signals/sigStopAtWriteNow.C     |   4 +-
 src/OSspecific/POSIX/signals/sigFpe.C         |   4 +-
 src/OSspecific/POSIX/signals/sigInt.C         |   4 +-
 src/OSspecific/POSIX/signals/sigQuit.C        |   4 +-
 src/OSspecific/POSIX/signals/sigSegv.C        |   4 +-
 .../POSIX/signals/sigStopAtWriteNow.C         |   4 +-
 src/OpenFOAM/Make/files                       |   1 +
 src/OpenFOAM/db/error/IOerror.C               |  85 ++------
 src/OpenFOAM/db/error/error.C                 |  46 +++--
 src/OpenFOAM/db/error/error.H                 |  68 +++---
 src/OpenFOAM/db/error/messageStream.H         |  16 +-
 src/OpenFOAM/global/JobInfo/JobInfo.C         | 195 +++++++++++-------
 src/OpenFOAM/global/JobInfo/JobInfo.H         |  84 ++++++--
 src/OpenFOAM/global/argList/argList.C         |  17 +-
 19 files changed, 344 insertions(+), 262 deletions(-)

diff --git a/applications/test/error/Test-error.C b/applications/test/error/Test-error.C
index 550726330ba..3e25fe9da0b 100644
--- a/applications/test/error/Test-error.C
+++ b/applications/test/error/Test-error.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2020 OpenCFD Ltd.
+    Copyright (C) 2020-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -37,10 +37,13 @@ using namespace Foam;
 
 int main(int argc, char *argv[])
 {
-    #if 0
+    argList::addBoolOption("no-throw", "Use error, no exceptions");
+    argList::addBoolOption("ioerror", "Use IOerror instead");
+
     argList::noBanner();
     argList args(argc, argv);
 
+    #if 0
     if (true)
     {
         InfoErr<< "Called with " << (args.size()-1) << " args\n";
@@ -49,7 +52,13 @@ int main(int argc, char *argv[])
     }
     #endif
 
-    FatalError.throwExceptions();
+    const bool useIOerr = args.found("ioerror");
+
+    if (!args.found("no-throw"))
+    {
+        FatalIOError.throwExceptions();
+        FatalError.throwExceptions();
+    }
 
     try
     {
@@ -60,17 +69,34 @@ int main(int argc, char *argv[])
 
         IOWarningInFunction(dict) << "warning 3" << endl;
 
-        FatalErrorInFunction
-            << "This is an error from 1" << nl
-            << "Explanation to follow:" << endl;
-
-        FatalErrorInFunction
-            << "Error 2"
-            << exit(FatalError);
+        if (useIOerr)
+        {
+            FatalIOErrorInFunction(dict)
+                << "This is an error from 1" << nl
+                << "Explanation to follow:" << endl;
+
+            FatalIOError
+                << "Error 2"
+                << exit(FatalIOError);
+        }
+        else
+        {
+            FatalErrorInFunction
+                << "This is an error from 1" << nl
+                << "Explanation to follow:" << endl;
+
+            FatalError
+                << "Error 2"
+                << exit(FatalError);
+        }
+    }
+    catch (const Foam::IOerror& err)
+    {
+        Serr<< "Caught IO error " << err << nl << endl;
     }
     catch (const Foam::error& err)
     {
-        Serr<< "Caught Foam error " << err << nl << endl;
+        Serr<< "Caught error " << err << nl << endl;
     }
 
     try
@@ -79,9 +105,13 @@ int main(int argc, char *argv[])
             << "Error# 3"
             << exit(FatalError);
     }
+    catch (const Foam::IOerror& err)
+    {
+        Serr<< "Caught IO error " << err << nl << endl;
+    }
     catch (const Foam::error& err)
     {
-        Serr<< "Caught Foam error " << err << nl << endl;
+        Serr<< "Caught error " << err << nl << endl;
     }
 
     return 0;
diff --git a/src/OSspecific/MSwindows/signals/sigFpe.C b/src/OSspecific/MSwindows/signals/sigFpe.C
index a00e7929df6..0a06c4c30ad 100644
--- a/src/OSspecific/MSwindows/signals/sigFpe.C
+++ b/src/OSspecific/MSwindows/signals/sigFpe.C
@@ -7,7 +7,7 @@
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
     Copyright (C) 2011 Symscape
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -87,7 +87,7 @@ void Foam::sigFpe::sigHandler(int)
 {
     resetHandler("SIGFPE", SIGFPE);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     clearFpe();
     ::raise(SIGFPE);            // Throw signal (to old handler)
diff --git a/src/OSspecific/MSwindows/signals/sigInt.C b/src/OSspecific/MSwindows/signals/sigInt.C
index 2c8db81f28b..c4114968ba3 100644
--- a/src/OSspecific/MSwindows/signals/sigInt.C
+++ b/src/OSspecific/MSwindows/signals/sigInt.C
@@ -7,7 +7,7 @@
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
     Copyright (C) 2011 Symscape
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -47,7 +47,7 @@ void Foam::sigInt::sigHandler(int)
 {
     resetHandler("SIGINT", SIGINT);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     ::raise(SIGINT);            // Throw signal (to old handler)
 }
 
diff --git a/src/OSspecific/MSwindows/signals/sigQuit.C b/src/OSspecific/MSwindows/signals/sigQuit.C
index 40de3250186..cb33f036ae0 100644
--- a/src/OSspecific/MSwindows/signals/sigQuit.C
+++ b/src/OSspecific/MSwindows/signals/sigQuit.C
@@ -7,7 +7,7 @@
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
     Copyright (C) 2011 Symscape
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -49,7 +49,7 @@ void Foam::sigQuit::sigHandler(int)
 {
     resetHandler("SIGBREAK", SIGBREAK);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     ::raise(SIGBREAK);          // Throw signal (to old handler)
 }
diff --git a/src/OSspecific/MSwindows/signals/sigSegv.C b/src/OSspecific/MSwindows/signals/sigSegv.C
index ac802524706..9fe56674f49 100644
--- a/src/OSspecific/MSwindows/signals/sigSegv.C
+++ b/src/OSspecific/MSwindows/signals/sigSegv.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,7 +46,7 @@ void Foam::sigSegv::sigHandler(int)
 {
     resetHandler("SIGSEGV", SIGSEGV);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     ::raise(SIGSEGV);           // Throw signal (to old handler)
 }
diff --git a/src/OSspecific/MSwindows/signals/sigStopAtWriteNow.C b/src/OSspecific/MSwindows/signals/sigStopAtWriteNow.C
index 0923e110d49..25a7234923a 100644
--- a/src/OSspecific/MSwindows/signals/sigStopAtWriteNow.C
+++ b/src/OSspecific/MSwindows/signals/sigStopAtWriteNow.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -95,7 +95,7 @@ void Foam::sigStopAtWriteNow::sigHandler(int)
 {
     resetHandler("stopAtWriteNow", signal_);
 
-    jobInfo.signalEnd(); // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
 
     if (runTimePtr_)
     {
diff --git a/src/OSspecific/POSIX/signals/sigFpe.C b/src/OSspecific/POSIX/signals/sigFpe.C
index 29a95852597..110fff0131c 100644
--- a/src/OSspecific/POSIX/signals/sigFpe.C
+++ b/src/OSspecific/POSIX/signals/sigFpe.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2016-2020 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -121,7 +121,7 @@ void Foam::sigFpe::sigHandler(int)
 
     resetHandler("SIGFPE", SIGFPE);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     ::raise(SIGFPE);            // Throw signal (to old handler)
 
diff --git a/src/OSspecific/POSIX/signals/sigInt.C b/src/OSspecific/POSIX/signals/sigInt.C
index 699d0329778..f50b20dca83 100644
--- a/src/OSspecific/POSIX/signals/sigInt.C
+++ b/src/OSspecific/POSIX/signals/sigInt.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,7 +46,7 @@ void Foam::sigInt::sigHandler(int)
 {
     resetHandler("SIGINT", SIGINT);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     ::raise(SIGINT);            // Throw signal (to old handler)
 }
 
diff --git a/src/OSspecific/POSIX/signals/sigQuit.C b/src/OSspecific/POSIX/signals/sigQuit.C
index ea61d8db5a6..dda6ff025bd 100644
--- a/src/OSspecific/POSIX/signals/sigQuit.C
+++ b/src/OSspecific/POSIX/signals/sigQuit.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2017-2019 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,7 +46,7 @@ void Foam::sigQuit::sigHandler(int)
 {
     resetHandler("SIGQUIT", SIGQUIT);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     ::raise(SIGQUIT);           // Throw signal (to old handler)
 }
diff --git a/src/OSspecific/POSIX/signals/sigSegv.C b/src/OSspecific/POSIX/signals/sigSegv.C
index ac802524706..9fe56674f49 100644
--- a/src/OSspecific/POSIX/signals/sigSegv.C
+++ b/src/OSspecific/POSIX/signals/sigSegv.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2018-2019 OpenCFD Ltd.
+    Copyright (C) 2018-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -46,7 +46,7 @@ void Foam::sigSegv::sigHandler(int)
 {
     resetHandler("SIGSEGV", SIGSEGV);
 
-    jobInfo.signalEnd();        // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
     error::printStack(Perr);
     ::raise(SIGSEGV);           // Throw signal (to old handler)
 }
diff --git a/src/OSspecific/POSIX/signals/sigStopAtWriteNow.C b/src/OSspecific/POSIX/signals/sigStopAtWriteNow.C
index 4ca10d49dc6..f6644c34319 100644
--- a/src/OSspecific/POSIX/signals/sigStopAtWriteNow.C
+++ b/src/OSspecific/POSIX/signals/sigStopAtWriteNow.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2016-2019 OpenCFD Ltd.
+    Copyright (C) 2016-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -97,7 +97,7 @@ void Foam::sigStopAtWriteNow::sigHandler(int)
 {
     resetHandler("stopAtWriteNow", signal_);
 
-    jobInfo.signalEnd(); // Update jobInfo file
+    JobInfo::shutdown();        // From running -> finished
 
     if (runTimePtr_)
     {
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index cd25b075df9..5f519001307 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -2,6 +2,7 @@ global/foamConfig.Cver
 global/globals.C
 /* global/constants/constants.C in globals.C */
 /* global/constants/dimensionedConstants.C in globals.C */
+/* global/JobInfo/JobInfo.C in globals.C */
 global/argList/argList.C
 global/argList/argListHelp.C
 global/clock/clock.C
diff --git a/src/OpenFOAM/db/error/IOerror.C b/src/OpenFOAM/db/error/IOerror.C
index 588aea16390..93acd448e38 100644
--- a/src/OpenFOAM/db/error/IOerror.C
+++ b/src/OpenFOAM/db/error/IOerror.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -159,9 +159,7 @@ Foam::IOerror::operator Foam::dictionary() const
 {
     dictionary errDict(error::operator dictionary());
 
-    errDict.remove("type");
-    errDict.add("type", word("Foam::IOerror"));
-
+    errDict.add("type", word("Foam::IOerror"), true);  // overwrite
     errDict.add("ioFileName", ioFileName());
     errDict.add("ioStartLineNumber", ioStartLineNumber());
     errDict.add("ioEndLineNumber", ioEndLineNumber());
@@ -172,75 +170,29 @@ Foam::IOerror::operator Foam::dictionary() const
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-void Foam::IOerror::exitOrAbort(const int errNo, const bool isAbort)
+void Foam::IOerror::exiting(const int errNo, const bool isAbort)
 {
-    if (!throwing_ && JobInfo::constructed)
+    if (throwing_)
     {
-        jobInfo.add("FatalIOError", operator dictionary());
-        if (isAbort || error::useAbort())
-        {
-            jobInfo.abort();
-        }
-        else
+        if (!isAbort)
         {
-            jobInfo.exit();
-        }
-    }
+            // Make a copy of the error to throw
+            IOerror errorException(*this);
 
-    if (throwing_ && !isAbort)
-    {
-        // Make a copy of the error to throw
-        IOerror errorException(*this);
-
-        // Reset the message buffer for the next error message
-        messageStreamPtr_->reset();
+            // Reset the message buffer for the next error message
+            messageStreamPtr_->reset();
 
-        throw errorException;
-    }
-    else if (error::useAbort())
-    {
-        Perr<< nl << *this << nl
-            << "\nFOAM aborting (FOAM_ABORT set)\n" << endl;
-        error::printStack(Perr);
-        std::abort();
-    }
-    else if (Pstream::parRun())
-    {
-        if (isAbort)
-        {
-            Perr<< nl << *this << nl
-                << "\nFOAM parallel run aborting\n" << endl;
-            error::printStack(Perr);
-            Pstream::abort();
-        }
-        else
-        {
-            Perr<< nl << *this << nl
-                << "\nFOAM parallel run exiting\n" << endl;
-            Pstream::exit(errNo);
+            throw errorException;
+            return;
         }
     }
-    else
+    else if (JobInfo::constructed)
     {
-        if (isAbort)
-        {
-            Perr<< nl << *this << nl
-                << "\nFOAM aborting\n" << endl;
-            error::printStack(Perr);
-
-            #ifdef _WIN32
-            std::exit(1);  // Prefer exit() to avoid unnecessary warnings
-            #else
-            std::abort();
-            #endif
-        }
-        else
-        {
-            Perr<< nl << *this << nl
-                << "\nFOAM exiting\n" << endl;
-            std::exit(errNo);
-        }
+        jobInfo.add("FatalIOError", operator dictionary());
+        JobInfo::shutdown(isAbort || error::useAbort());
     }
+
+    simpleExit(errNo, isAbort);
 }
 
 
@@ -248,13 +200,13 @@ void Foam::IOerror::exitOrAbort(const int errNo, const bool isAbort)
 
 void Foam::IOerror::exit(const int)
 {
-    exitOrAbort(1, false);
+    exiting(1, false);
 }
 
 
 void Foam::IOerror::abort()
 {
-    exitOrAbort(1, true);
+    exiting(1, true);
 }
 
 
@@ -323,7 +275,6 @@ void Foam::IOerror::write(Ostream& os, const bool includeTitle) const
 Foam::Ostream& Foam::operator<<(Ostream& os, const IOerror& err)
 {
     err.write(os);
-
     return os;
 }
 
diff --git a/src/OpenFOAM/db/error/error.C b/src/OpenFOAM/db/error/error.C
index 270db524cf3..46ada0c4681 100644
--- a/src/OpenFOAM/db/error/error.C
+++ b/src/OpenFOAM/db/error/error.C
@@ -199,32 +199,37 @@ Foam::error::operator Foam::dictionary() const
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-void Foam::error::exitOrAbort(const int errNo, const bool isAbort)
+void Foam::error::exiting(const int errNo, const bool isAbort)
 {
-    if (!throwing_ && JobInfo::constructed)
+    if (throwing_)
     {
-        jobInfo.add("FatalError", operator dictionary());
-        if (isAbort || error::useAbort())
-        {
-            jobInfo.abort();
-        }
-        else
+        if (!isAbort)
         {
-            jobInfo.exit();
+            // Make a copy of the error to throw
+            error errorException(*this);
+
+            // Reset the message buffer for the next error message
+            messageStreamPtr_->reset();
+
+            throw errorException;
+            return;
         }
     }
-
-    if (throwing_ && !isAbort)
+    else if (JobInfo::constructed)
     {
-        // Make a copy of the error to throw
-        error errorException(*this);
+        jobInfo.add("FatalError", operator dictionary());
+        JobInfo::shutdown(isAbort || error::useAbort());
+    }
+
+    simpleExit(errNo, isAbort);
+}
 
-        // Reset the message buffer for the next error message
-        messageStreamPtr_->reset();
 
-        throw errorException;
-    }
-    else if (error::useAbort())
+// * * * * * * * * * * * * Protected Member Functions  * * * * * * * * * * * //
+
+void Foam::error::simpleExit(const int errNo, const bool isAbort)
+{
+    if (error::useAbort())
     {
         Perr<< nl << *this << nl
             << "\nFOAM aborting (FOAM_ABORT set)\n" << endl;
@@ -287,13 +292,13 @@ void Foam::error::clear() const
 
 void Foam::error::exit(const int errNo)
 {
-    exitOrAbort(errNo, false);
+    exiting(errNo, false);
 }
 
 
 void Foam::error::abort()
 {
-    exitOrAbort(1, true);
+    exiting(1, true);
 }
 
 
@@ -345,7 +350,6 @@ void Foam::error::write(Ostream& os, const bool includeTitle) const
 Foam::Ostream& Foam::operator<<(Ostream& os, const error& err)
 {
     err.write(os);
-
     return os;
 }
 
diff --git a/src/OpenFOAM/db/error/error.H b/src/OpenFOAM/db/error/error.H
index 3ce8c20db2d..62cc4d48fad 100644
--- a/src/OpenFOAM/db/error/error.H
+++ b/src/OpenFOAM/db/error/error.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2015 OpenFOAM Foundation
-    Copyright (C) 2015-2020 OpenCFD Ltd.
+    Copyright (C) 2015-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -75,8 +75,8 @@ class error
 {
     // Private Member Functions
 
-        //- Common code for exit or abort
-        void exitOrAbort(const int errNo, const bool isAbort);
+        //- Exit or abort code, with exception and jobinfo handling
+        void exiting(const int errNo, const bool isAbort);
 
 
 protected:
@@ -90,6 +90,12 @@ protected:
         std::unique_ptr<OStringStream> messageStreamPtr_;
 
 
+    // Protected Member Functions
+
+        //- Exit or abort, without throwing or job control handling
+        void simpleExit(const int errNo, const bool isAbort);
+
+
 public:
 
     // Constructors
@@ -126,48 +132,34 @@ public:
         //- Clear any messages
         void clear() const;
 
-        const string& functionName() const
+        const string& functionName() const noexcept
         {
             return functionName_;
         }
 
-        const string& sourceFileName() const
+        const string& sourceFileName() const noexcept
         {
             return sourceFileName_;
         }
 
-        label sourceFileLineNumber() const
+        label sourceFileLineNumber() const noexcept
         {
             return sourceFileLineNumber_;
         }
 
-        //- Return the current exception throwing (on or off)
-        bool throwing() const
+        //- Return the current exception throwing state (on or off)
+        bool throwing() const noexcept
         {
             return throwing_;
         }
 
-        //- Activate/deactivate exception throwing
+        //- Specify exception throwing (default is activate)
         //  \return the previous throwing state
-        inline bool throwExceptions(bool doThrow)
+        bool throwExceptions(bool doThrow = true) noexcept
         {
-            const bool prev = throwing_;
+            bool old = throwing_;
             throwing_ = doThrow;
-            return prev;
-        }
-
-        //- Activate exception throwing
-        //  \return the previous throwing state
-        inline bool throwExceptions()
-        {
-            return throwExceptions(true);
-        }
-
-        //- Deactivate exception throwing
-        //  \return the previous throwing state
-        inline bool dontThrowExceptions()
-        {
-            return throwExceptions(false);
+            return old;
         }
 
         //- Define basic print message
@@ -204,7 +196,7 @@ public:
         //- Convert to OSstream
         operator OSstream&();
 
-        //- Create and return a dictionary representation of the error
+        //- Return a dictionary representation of the error
         operator dictionary() const;
 
 
@@ -228,6 +220,16 @@ public:
 
         //- Print error message
         void write(Ostream& os, const bool includeTitle = true) const;
+
+
+    // Housekeeping
+
+        //- Deactivate exception throwing
+        //  \return the previous throwing state
+        bool dontThrowExceptions() noexcept
+        {
+            return throwExceptions(false);
+        }
 };
 
 
@@ -249,8 +251,8 @@ class IOerror
 
     // Private Member Functions
 
-        //- Common code for exit or abort
-        void exitOrAbort(const int errNo, const bool isAbort);
+        //- Exit or abort code, with exception and jobinfo handling
+        void exiting(const int errNo, const bool isAbort);
 
 
 public:
@@ -270,17 +272,17 @@ public:
 
     // Member Functions
 
-        const string& ioFileName() const
+        const string& ioFileName() const noexcept
         {
             return ioFileName_;
         }
 
-        label ioStartLineNumber() const
+        label ioStartLineNumber() const noexcept
         {
             return ioStartLineNumber_;
         }
 
-        label ioEndLineNumber() const
+        label ioEndLineNumber() const noexcept
         {
             return ioEndLineNumber_;
         }
@@ -329,7 +331,7 @@ public:
             const string& msg
         );
 
-        //- Create and return a dictionary representation of the error
+        //- Return a dictionary representation of the error
         operator dictionary() const;
 
 
diff --git a/src/OpenFOAM/db/error/messageStream.H b/src/OpenFOAM/db/error/messageStream.H
index 1ab03aa95be..81ba4252a06 100644
--- a/src/OpenFOAM/db/error/messageStream.H
+++ b/src/OpenFOAM/db/error/messageStream.H
@@ -127,25 +127,27 @@ public:
         explicit messageStream(const dictionary& dict);
 
 
-    // Member functions
+    // Member Functions
 
         //- The title of this error type
-        const string& title() const
+        const string& title() const noexcept
         {
             return title_;
         }
 
         //- The maximum number of errors before program termination
-        int maxErrors() const
+        int maxErrors() const noexcept
         {
             return maxErrors_;
         }
 
-        //- Non-const access to the maximum number of errors before
-        //- program termination to enable user to reset it
-        int& maxErrors()
+        //- Set the maximum number of errors before program termination
+        //  \return the previous value for maxErrors
+        int maxErrors(int nErrors) noexcept
         {
-            return maxErrors_;
+            int old = maxErrors_;
+            maxErrors_ = nErrors;
+            return old;
         }
 
         //- Convert to OSstream
diff --git a/src/OpenFOAM/global/JobInfo/JobInfo.C b/src/OpenFOAM/global/JobInfo/JobInfo.C
index aedfe250cf6..d014befbaea 100644
--- a/src/OpenFOAM/global/JobInfo/JobInfo.C
+++ b/src/OpenFOAM/global/JobInfo/JobInfo.C
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011-2016 OpenFOAM Foundation
-    Copyright (C) 2017-2018 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -27,9 +27,9 @@ License
 \*---------------------------------------------------------------------------*/
 
 #include "JobInfo.H"
-#include "OSspecific.H"
 #include "clock.H"
 #include "OFstream.H"
+#include "OSspecific.H"
 #include "Pstream.H"
 #include "foamVersion.H"
 
@@ -48,45 +48,115 @@ License
 bool Foam::JobInfo::writeJobInfo(Foam::debug::infoSwitch("writeJobInfo", 0));
 Foam::JobInfo Foam::jobInfo;
 
+// Foam::JobInfo::constructed  defined in globals.C
+
+
+// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
+
+namespace Foam
+{
+
+// Ensure given directory exists (called on master only)
+static inline bool ensureJobDirExists(const fileName& dir)
+{
+    if (!Foam::isDir(dir) && !Foam::mkDir(dir))
+    {
+        std::cerr
+            << "WARNING: no JobInfo directory: " << dir << nl
+            << "    disabling JobInfo" << nl;
+
+        return false;
+    }
+
+    return true;
+}
+
+
+// Write dictionary entries (called on master only)
+static inline bool writeJobDict(Ostream& os, const dictionary& dict)
+{
+    if (os.good())
+    {
+        dict.writeEntries(os, true);  // With extraNewLine=true
+        return true;
+    }
+
+    return false;
+}
+
+} // End namespace Foam
+
+
+// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
+
+void Foam::JobInfo::disable() noexcept
+{
+    writeJobInfo = false;
+}
+
+
+void Foam::JobInfo::shutdown()
+{
+    jobInfo.jobEnding();
+}
+
+
+void Foam::JobInfo::shutdown(bool isAbort)
+{
+    if (isAbort)
+    {
+        jobInfo.jobEnding("abort");
+    }
+    else
+    {
+        jobInfo.jobEnding("exit");
+    }
+}
+
 
 // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
 
-bool Foam::JobInfo::write(Ostream& os) const
+void Foam::JobInfo::jobEnding()
 {
-    if (writeJobInfo && Pstream::master())
+    if (!running_.empty())
     {
-        if (os.good())
-        {
-            dictionary::write(os, false);
-            return true;
-        }
-        else
+        if (!Foam::mv(running_, finished_))
         {
-            return false;
+            Foam::rm(running_);
         }
     }
 
-    return true;
+    running_.clear();
+    finished_.clear();
+    constructed = false;
 }
 
 
-void Foam::JobInfo::end(const word& terminationType)
+void Foam::JobInfo::jobEnding(const word& terminationType)
 {
-    if (writeJobInfo && constructed && Pstream::master())
+    if (writeJobInfo && !finished_.empty())
     {
         add("cpuTime", cpuTime_.elapsedCpuTime());
         add("endDate", clock::date());
         add("endTime", clock::clockTime());
 
-        if (!found("termination"))
+        if (!terminationType.empty() && !found("termination"))
         {
             add("termination", terminationType);
         }
 
-        Foam::rm(runningDir_/jobFileName_);
-        write(OFstream(finishedDir_/jobFileName_)());
+        Foam::rm(running_);
+        OFstream os(finished_);
+        if (!writeJobDict(os, *this))
+        {
+            std::cerr
+                << "WARNING: could not write JobInfo file: "
+                << finished_ << nl;
+        }
     }
 
+    running_.clear();
+    finished_.clear();
     constructed = false;
 }
 
@@ -94,14 +164,15 @@ void Foam::JobInfo::end(const word& terminationType)
 // * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //
 
 Foam::JobInfo::JobInfo()
-:
-    jobFileName_(),
-    runningDir_(),
-    finishedDir_(),
-    cpuTime_()
 {
-    name() = "JobInfo";
+    if (constructed)
+    {
+        std::cerr
+            << "WARNING: JobInfo was already constructed. "
+               "Should be a singleton!!" << nl;
+    }
 
+    // Only populate on master process, and when enabled
     if (writeJobInfo && Pstream::master())
     {
         string jobDir = Foam::getEnv("FOAM_JOB_DIR");
@@ -109,31 +180,23 @@ Foam::JobInfo::JobInfo()
         {
             jobDir = home()/FOAM_RESOURCE_USER_CONFIG_DIRNAME/"jobControl";
         }
-
-        jobFileName_ = hostName() + '.' + Foam::name(pid());
-        runningDir_  = jobDir/"runningJobs";
-        finishedDir_ = jobDir/"finishedJobs";
-
-        if (!isDir(jobDir) && !mkDir(jobDir))
-        {
-            FatalErrorInFunction
-                << "No JobInfo directory: " << jobDir
-                << Foam::exit(FatalError);
-        }
-        if (!isDir(runningDir_) && !mkDir(runningDir_))
+        string jobFile = hostName() + '.' + Foam::name(pid());
+        running_  = jobDir/"runningJobs"/jobFile;
+        finished_ = jobDir/"finishedJobs"/jobFile;
+
+        if
+        (
+            !ensureJobDirExists(jobDir)
+         || !ensureJobDirExists(running_.path())
+         || !ensureJobDirExists(finished_.path())
+        )
         {
-            FatalErrorInFunction
-                << "No JobInfo directory: " << runningDir_
-                << Foam::exit(FatalError);
-        }
-        if (!isDir(finishedDir_) && !mkDir(finishedDir_))
-        {
-            FatalErrorInFunction
-                << "No JobInfo directory: " << finishedDir_
-                << Foam::exit(FatalError);
+            running_.clear();
+            finished_.clear();
         }
     }
 
+    dictionary::name() = "JobInfo";
     constructed = true;
 }
 
@@ -142,7 +205,7 @@ Foam::JobInfo::JobInfo()
 
 Foam::JobInfo::~JobInfo()
 {
-    signalEnd();
+    jobEnding();
 }
 
 
@@ -150,45 +213,29 @@ Foam::JobInfo::~JobInfo()
 
 void Foam::JobInfo::write() const
 {
-    if (writeJobInfo && constructed && Pstream::master())
+    if (writeJobInfo && !running_.empty())
     {
-        const fileName output = runningDir_/jobFileName_;
-        if (!write(OFstream(output)()))
+        OFstream os(running_);
+        if (!writeJobDict(os, *this))
         {
-            FatalErrorInFunction
-                << "Failed to write to JobInfo file " << output
-                << Foam::exit(FatalError);
+            std::cerr
+                << "WARNING: could not write JobInfo file: "
+                << running_ << nl;
+
+            // Normally does not happen
+            const_cast<fileName&>(running_).clear();
         }
     }
 }
 
 
-void Foam::JobInfo::end()
-{
-    end("normal");
-}
+void Foam::JobInfo::stop() { jobEnding("normal"); }
 
+void Foam::JobInfo::exit() { jobEnding("exit"); }
 
-void Foam::JobInfo::exit()
-{
-    end("exit");
-}
+void Foam::JobInfo::abort() { jobEnding("abort"); }
 
-
-void Foam::JobInfo::abort()
-{
-    end("abort");
-}
-
-
-void Foam::JobInfo::signalEnd() const
-{
-    if (writeJobInfo && constructed && Pstream::master())
-    {
-        Foam::mv(runningDir_/jobFileName_, finishedDir_/jobFileName_);
-    }
-    constructed = false;
-}
+void Foam::JobInfo::signalEnd() { jobEnding(); }
 
 
 // ************************************************************************* //
diff --git a/src/OpenFOAM/global/JobInfo/JobInfo.H b/src/OpenFOAM/global/JobInfo/JobInfo.H
index d330b0e78df..1cf6a9917d1 100644
--- a/src/OpenFOAM/global/JobInfo/JobInfo.H
+++ b/src/OpenFOAM/global/JobInfo/JobInfo.H
@@ -6,7 +6,7 @@
      \\/     M anipulation  |
 -------------------------------------------------------------------------------
     Copyright (C) 2011 OpenFOAM Foundation
-    Copyright (C) 2017 OpenCFD Ltd.
+    Copyright (C) 2017-2021 OpenCFD Ltd.
 -------------------------------------------------------------------------------
 License
     This file is part of OpenFOAM.
@@ -28,7 +28,8 @@ Class
     Foam::JobInfo
 
 Description
-    Helper class for recording information about run/finished jobs.
+    Helper class for recording information about run/finished jobs,
+    acts like global singleton.
 
     Writes the following files:
       - $FOAM_JOB_DIR/runningJobs
@@ -36,6 +37,11 @@ Description
 
     If FOAM_JOB_DIR is unset, defaults to ~/.OpenFOAM/jobControl
 
+Note
+    JobInfo is treated as a largely failsafe operation.
+    If the directories cannot be written to,
+    only a warning (not an error) is emitted.
+
 SourceFiles
     JobInfo.C
 
@@ -45,7 +51,6 @@ SourceFiles
 #define JobInfo_H
 
 #include "dictionary.H"
-#include "fileName.H"
 #include "cpuTime.H"
 
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@@ -61,18 +66,34 @@ class JobInfo
 :
     public dictionary
 {
-    // Private data
+    // Private Data
+
+        //- Full path to "running" job information file (master only)
+        fileName running_;
+
+        //- Full path to "finished" job information file (master only)
+        fileName finished_;
+
+        //- Timing information
+        cpuTime cpuTime_;
 
-        //- The name of the job file
-        string   jobFileName_;
-        fileName runningDir_;
-        fileName finishedDir_;
-        cpuTime  cpuTime_;
 
     // Private Member Functions
 
-        bool write(Ostream& os) const;
-        void end(const word& terminationType);
+        //- Relocate job file from "running" to "finished" directory.
+        //  Invalidates job contents, clears 'constructed'.
+        void jobEnding();
+
+        //- Add elapsed times, termination type, remove file
+        //- from "running" and create in "finished" directory.
+        //  Invalidates job contents, clears 'constructed'.
+        void jobEnding(const word& terminationType);
+
+        //- No copy construct
+        JobInfo(const JobInfo&) = delete;
+
+        //- No copy assignment
+        void operator=(const JobInfo&) = delete;
 
 
 public:
@@ -86,34 +107,55 @@ public:
 
     // Constructors
 
-        //- Construct null
+        //- Default construct
         JobInfo();
 
 
-    //- Destructor
-    //  Update job info and relocate the file from running to finished.
+    //- Destructor, relocates the job file from running to finished.
     ~JobInfo();
 
 
+    // Static Member Functions
+
+        //- Disallow JobInfo by forcing writeJobInfo (InfoSwitch) off.
+        static void disable() noexcept;
+
+        //- Simple shutdown (finalize) of JobInfo
+        //  Relocates the job file from "running" to "finished".
+        //  Invalidates job contents, clears 'constructed'.
+        static void shutdown();
+
+        //- Exit or abort shutdown (finalize) of JobInfo
+        static void shutdown(bool isAbort);
+
+
     // Member Functions
 
-        //- Write the job info to its file in the runningJobs directory
+        //- Write job info to its file in the "running" jobs directory
         void write() const;
 
-        //- End with "termination=normal"
-        void end();
+        //- Job end with "normal" termination
+        void stop();
 
-        //- End with "termination=exit"
+        //- Job end with "exit" termination
         void exit();
 
-        //- End with "termination=abort"
+        //- Job end with "abort" termination
         void abort();
 
-        //- Update job info and relocate the file from running to finished.
-        void signalEnd() const;
+        //- Relocate job file from "running" to "finished" directory.
+        //  Invalidates job contents, clears 'constructed'.
+        void signalEnd();
+
+
+    // Housekeeping
+
+        //- Same as stop
+        void end() { stop(); }
 };
 
 
+// Job information bookkeeping (global)
 extern JobInfo jobInfo;
 
 
diff --git a/src/OpenFOAM/global/argList/argList.C b/src/OpenFOAM/global/argList/argList.C
index f4b26f38f84..9eb2fb42935 100644
--- a/src/OpenFOAM/global/argList/argList.C
+++ b/src/OpenFOAM/global/argList/argList.C
@@ -486,7 +486,7 @@ void Foam::argList::noFunctionObjects(bool addWithOption)
 
 void Foam::argList::noJobInfo()
 {
-    JobInfo::writeJobInfo = false;
+    JobInfo::disable();
 }
 
 
@@ -1063,12 +1063,9 @@ void Foam::argList::parse
         jobInfo.add("startDate", dateString);
         jobInfo.add("startTime", timeString);
         jobInfo.add("userName", userName());
+
+        jobInfo.add("foamApi", foamVersion::api);
         jobInfo.add("foamVersion", word(foamVersion::version));
-        jobInfo.add("code", executable_);
-        jobInfo.add("argList", commandLine_);
-        jobInfo.add("currentDir", cwd());
-        jobInfo.add("PPID", ppid());
-        jobInfo.add("PGID", pgid());
 
         // Add build information - only use the first word
         {
@@ -1081,6 +1078,12 @@ void Foam::argList::parse
             jobInfo.add("foamBuild", build);
         }
 
+        jobInfo.add("code", executable_);
+        jobInfo.add("argList", commandLine_);
+        jobInfo.add("currentDir", cwd());
+        jobInfo.add("PPID", ppid());
+        jobInfo.add("PGID", pgid());
+
         // Load additional libraries (verbosity according to banner setting)
         libs().open(bannerEnabled());
     }
@@ -1635,7 +1638,7 @@ void Foam::argList::parse
 
 Foam::argList::~argList()
 {
-    jobInfo.end();
+    jobInfo.stop();     // Normal job termination
 
     // Delete file handler to flush any remaining IO
     Foam::fileHandler(nullptr);
-- 
GitLab