Date: Sat, 6 Jun 2026 19:03:15 +0100
In my work this week, I submitted a pull request containing code like this:
struct S {
optional<T> var;
void reset(void) noexcept
{
lock_guard mylock{some_global_mutex};
var.reset();
}
}
The pull request went through code review, and a colleague of mine
responded to say:
"You've marked that function 'noexcept' but 'mutex::lock' might throw"
My response to him was:
"If a mutex fails to lock, the entire process is fried"
Mulling over this for a bit longer, I think that the Standard should
make a distinction between "normal exceptions" and "lost-all-hope
exceptions".
For instance if you think a mutex might ever fail to lock in your
program, I think you should do something like the following:
Step 1 - When the process starts, use 'std::fopen' to open a file on
disk for writing.
Step 2 - Invoke 'std::set_terminate_handler' to register a handler
that writes critical data to disk with 'std::fwrite', then
'std::fclose', and then invokes 'std::_Exit' to immediately kill the
process.
Step 3 - Mark the aforementioned 'S::reset' method as 'noexcept', and
just let the terminate handler save critical data if 'mutex::lock'
throws.
I don't think it's good enough that the Standard simply says that
'mutex::lock' might throw 'std::system_error', reason being that this
isn't something you can just catch and recover from. If a mutex fails
to lock, you need to do one of the two following:
Strategy 1 - Kill the entire process like I described above (i.e. let
the terminate handler save critical data and call 'std::_Exit').
Strategy 2 - If not killing the entire process, at least mark a
portion of the program as unusable -- and this is where I would use a
"Do not resuscitate" flag (i.e. a DNR flag).
A few years ago when I was programming security cameras in embedded
Linux, there was a background process that managed the mounting of an
encrypted volume. Sometimes the encrypted volume would spontaneously
unmount. We found that if you ever re-mounted the volume after it
spontaneously unmounted, you would get unreliable behaviour and data
corruption. For this reason I introduced a 'Do not resuscitate' flag.
If the encrypted volume were ever to spontaneously unmount, and as
soon as this was detected, the DNR flag would be set -- meaning that
the next time a script tried to mount the encrypted volume, the DNR
flag would forbid its re-mount. This strategy minimised data
corruption.
I don't think C++ programmers should be accommodating the throwing of
'std::system_error' from 'mutex::lock', for example by refraining from
marking 'S::reset' as 'noexcept'. Instead, C++ should have two
categories of exception -- normal exceptions and lost-all-hope
exceptions. So if you were to look up "mutex::lock" on
'cppreference.com', instead of it saying:
"Throws std::system_error when errors occur, including errors from the
underlying operating system that would prevent lock from meeting its
specifications. The mutex is not locked in the case of any exception
being thrown."
Perhaps it would say something like:
"Invokes the 'lost_all_hope' terminate handler when errors occur,
including errors from the underlying operating system that would
prevent lock from meeting its specifications."
struct S {
optional<T> var;
void reset(void) noexcept
{
lock_guard mylock{some_global_mutex};
var.reset();
}
}
The pull request went through code review, and a colleague of mine
responded to say:
"You've marked that function 'noexcept' but 'mutex::lock' might throw"
My response to him was:
"If a mutex fails to lock, the entire process is fried"
Mulling over this for a bit longer, I think that the Standard should
make a distinction between "normal exceptions" and "lost-all-hope
exceptions".
For instance if you think a mutex might ever fail to lock in your
program, I think you should do something like the following:
Step 1 - When the process starts, use 'std::fopen' to open a file on
disk for writing.
Step 2 - Invoke 'std::set_terminate_handler' to register a handler
that writes critical data to disk with 'std::fwrite', then
'std::fclose', and then invokes 'std::_Exit' to immediately kill the
process.
Step 3 - Mark the aforementioned 'S::reset' method as 'noexcept', and
just let the terminate handler save critical data if 'mutex::lock'
throws.
I don't think it's good enough that the Standard simply says that
'mutex::lock' might throw 'std::system_error', reason being that this
isn't something you can just catch and recover from. If a mutex fails
to lock, you need to do one of the two following:
Strategy 1 - Kill the entire process like I described above (i.e. let
the terminate handler save critical data and call 'std::_Exit').
Strategy 2 - If not killing the entire process, at least mark a
portion of the program as unusable -- and this is where I would use a
"Do not resuscitate" flag (i.e. a DNR flag).
A few years ago when I was programming security cameras in embedded
Linux, there was a background process that managed the mounting of an
encrypted volume. Sometimes the encrypted volume would spontaneously
unmount. We found that if you ever re-mounted the volume after it
spontaneously unmounted, you would get unreliable behaviour and data
corruption. For this reason I introduced a 'Do not resuscitate' flag.
If the encrypted volume were ever to spontaneously unmount, and as
soon as this was detected, the DNR flag would be set -- meaning that
the next time a script tried to mount the encrypted volume, the DNR
flag would forbid its re-mount. This strategy minimised data
corruption.
I don't think C++ programmers should be accommodating the throwing of
'std::system_error' from 'mutex::lock', for example by refraining from
marking 'S::reset' as 'noexcept'. Instead, C++ should have two
categories of exception -- normal exceptions and lost-all-hope
exceptions. So if you were to look up "mutex::lock" on
'cppreference.com', instead of it saying:
"Throws std::system_error when errors occur, including errors from the
underlying operating system that would prevent lock from meeting its
specifications. The mutex is not locked in the case of any exception
being thrown."
Perhaps it would say something like:
"Invokes the 'lost_all_hope' terminate handler when errors occur,
including errors from the underlying operating system that would
prevent lock from meeting its specifications."
Received on 2026-06-06 18:03:29
