Wednesday, May 26, 2021

Digest for comp.lang.c++@googlegroups.com - 25 updates in 4 topics

Juha Nieminen <nospam@thanks.invalid>: May 26 11:51AM

I have noticed a rather odd, and somewhat annoying, design decision in
std::thread, which breaks RAII design.
 
Normally when you use a C++ class from the standard library which allocates
some resource, the class in question will make sure to properly freeing
that resource when the object is destroyed, which is par for the course in
good RAII design.
 
For example, rather obviously, if you create an object of type std::vector,
it will delete its allocated memory (and destroy the elements) when it's
itself destroyed.
 
A more noteworthy example of something that allocates a resource other than
memory is std::ofstream: When it's destroyed, if it has a file open, it will
properly close the file.
 
This adds safety because it minimizes the risk of mistakes, especially since
functions may be exited at surprising places thanks to exceptions. Like:
 
void foo()
{
std::ofstream os("somefile");
 
doSomething(); // this might throw!
 
// ...
}
 
No matter how that function is exited, even if an exception is thrown,
that std::ofstream object will close the file so that the file handle isn't
leaked.
 
But now we come to std::thread. Suppose you have something like:
 
void foo()
{
std::thread t(someFunction);
 
doSomething();
 
signalTheThreadToStop();
t.join();
}
 
The problem with this is that if doSomething() throws an exception, the
program will crash. It doesn't even matter that you may be prepared for
the exception, like:
 
try { foo(); }
catch(...) { std::cout << "Exception thrown!\n"; }
 
the program will still crash, because std::thread does not like being
destroyed without being joined or detached, so it just terminates if that's
the case.
 
This is quite inconvenient, and as far as I know the standard library doesn't
really offer any utility to make disposing of the thread properly more
automatic. You'll have to write your own "thread handler" that will terminate
the thread properly when the thread object is destroyed, so that the
program won't crash if the function is exited unexpectedly.
 
This feels very counter to RAII principles, where objects should take care
of the resources they have allocated, rather than demanding the calling code
to manually do so.
Bonita Montero <Bonita.Montero@gmail.com>: May 26 02:04PM +0200

> the program will still crash, because std::thread does not like being
> destroyed without being joined or detached, so it just terminates if that's
> the case.
 
Where's the problem ? Use a scrope-guard which waits for the end of the
thread or a jthread with C++.
 
That's my scope-guard class:
 
#pragma once
#include <cassert>
#include "debug_exceptions.h"
 
template<typename T>
struct invoke_on_destruct
{
private:
T m_t;
bool m_enabled;
 
public:
invoke_on_destruct( T t ) :
m_t( t ), m_enabled( true )
{
}
~invoke_on_destruct()
{
if( m_enabled )
{
try_debug
{
m_t();
}
catch_debug
{
assert(false);
}
}
}
void disable_and_invoke()
{
m_enabled = false;
m_t();
}
void disable()
{
m_enabled = false;
}
void enable()
{
m_enabled = true;
}
};
 
#if defined(IOD_SHORT)
template<typename C>
using iod = invoke_on_destruct<C>;
#define IOD(varName, lambdaName) \
iod<decltype(lambdaName)> varName( lambdaName );

No comments: