mirror of
https://github.com/isocpp/CppCoreGuidelines.git
synced 2024-03-22 13:30:58 +08:00
Merge branch 'thread-tooling' of https://github.com/tituswinters/CppCoreGuidelines into tituswinters-thread-tooling
This commit is contained in:
commit
20403c8d6d
|
@ -7365,9 +7365,9 @@ But heed the warning: [Avoid "naked" `union`s](#Ru-naked)
|
||||||
##### Example
|
##### Example
|
||||||
|
|
||||||
// Short string optimization
|
// Short string optimization
|
||||||
|
|
||||||
constexpr size_t buffer_size = 16; // Slightly larger than the size of a pointer
|
constexpr size_t buffer_size = 16; // Slightly larger than the size of a pointer
|
||||||
|
|
||||||
class Immutable_string {
|
class Immutable_string {
|
||||||
public:
|
public:
|
||||||
Immutable_string(const char* str) :
|
Immutable_string(const char* str) :
|
||||||
|
@ -7380,18 +7380,18 @@ But heed the warning: [Avoid "naked" `union`s](#Ru-naked)
|
||||||
strcpy_s(string_ptr, size + 1, str);
|
strcpy_s(string_ptr, size + 1, str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~Immutable_string()
|
~Immutable_string()
|
||||||
{
|
{
|
||||||
if (size >= buffer_size)
|
if (size >= buffer_size)
|
||||||
delete string_ptr;
|
delete string_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* get_str() const
|
const char* get_str() const
|
||||||
{
|
{
|
||||||
return (size < buffer_size) ? string_buffer : string_ptr;
|
return (size < buffer_size) ? string_buffer : string_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// If the string is short enough, we store the string itself
|
// If the string is short enough, we store the string itself
|
||||||
// instead of a pointer to the string.
|
// instead of a pointer to the string.
|
||||||
|
@ -7399,7 +7399,7 @@ But heed the warning: [Avoid "naked" `union`s](#Ru-naked)
|
||||||
char* string_ptr;
|
char* string_ptr;
|
||||||
char string_buffer[buffer_size];
|
char string_buffer[buffer_size];
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t size;
|
const size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11697,16 +11697,40 @@ this can be a security risk.
|
||||||
|
|
||||||
##### Enforcement
|
##### Enforcement
|
||||||
|
|
||||||
Some is possible, do at least something.
|
When possible, rely on tooling enforcement, but be aware that any tooling
|
||||||
There are commercial and open-source tools that try to address this problem, but static tools often have many false positives and run-time tools often have a significant cost.
|
solution has costs and blind spots. Defense in depth (multiple tools, multiple
|
||||||
We hope for better tools.
|
approaches) is particularly valuable here.
|
||||||
|
|
||||||
Help the tools:
|
In the realm of static enforcement,
|
||||||
|
both [clang](http://clang.llvm.org/docs/ThreadSafetyAnalysis.html) and some
|
||||||
|
older versions of [GCC](https://gcc.gnu.org/wiki/ThreadSafetyAnnotation) have
|
||||||
|
some support for static annotation of thread safety properties. Consistent use
|
||||||
|
of this technique turns many classes of thread-safety errors into compile-time
|
||||||
|
errors. The annotations are generally local (marking a particular member
|
||||||
|
variable as guarded by a particular mutex), and are usually easy to
|
||||||
|
learn. However, as with many static tools, it can often present false
|
||||||
|
negatives - cases that should have been caught but were allowed.
|
||||||
|
|
||||||
* less global data
|
Clang's [Thread Sanitizer](http://clang.llvm.org/docs/ThreadSanitizer.html) (aka
|
||||||
* fewer `static` variables
|
TSAN) is a powerful example of dynamic tools: it changes the build and execution
|
||||||
* more use of stack memory (and don't pass pointers around too much)
|
of your program to add bookkeeping on memory access, absolutely identifying data
|
||||||
* more immutable data (literals, `constexpr`, and `const`)
|
races in a given execution of your binary. The cost for this is both memory
|
||||||
|
(5-10x in most cases) and CPU slowdown (2-20x). Dynamic tools like this are best
|
||||||
|
when applied to integration tests, canary pushes, or unittests that operate on
|
||||||
|
multiple threads. Workload matters: When TSAN identifies a problem, it is
|
||||||
|
effectively always an actual data race, but it can only identify races seen in a
|
||||||
|
given execution.
|
||||||
|
|
||||||
|
There are many other tools, both commercial and open-source. Thread safety is
|
||||||
|
challenging, often getting the better of experienced programmers: tooling is an
|
||||||
|
important strategy to mitigate those risks.
|
||||||
|
|
||||||
|
There are other ways you can mitigate the chance of data races:
|
||||||
|
|
||||||
|
* Avoid global data
|
||||||
|
* Avoid `static` variables
|
||||||
|
* More use of value types on the stack (and don't pass pointers around too much)
|
||||||
|
* More use of immutable data (literals, `constexpr`, and `const`)
|
||||||
|
|
||||||
### <a name="Rconc-data"></a>CP.3: Minimize explicit sharing of writable data
|
### <a name="Rconc-data"></a>CP.3: Minimize explicit sharing of writable data
|
||||||
|
|
||||||
|
@ -12709,7 +12733,7 @@ Example with thread-safe static local variables of C++11.
|
||||||
static My_class my_object; // Constructor called only once
|
static My_class my_object; // Constructor called only once
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
class My_class
|
class My_class
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -12732,7 +12756,7 @@ Double-checked locking is easy to mess up. If you really need to write your own
|
||||||
|
|
||||||
##### Example, bad
|
##### Example, bad
|
||||||
|
|
||||||
Even if the following example works correctly on most hardware platforms, it is not guaranteed to work by the C++ standard. The x_init.load(memory_order_relaxed) call may see a value from outside of the lock guard.
|
Even if the following example works correctly on most hardware platforms, it is not guaranteed to work by the C++ standard. The x_init.load(memory_order_relaxed) call may see a value from outside of the lock guard.
|
||||||
|
|
||||||
atomic<bool> x_init;
|
atomic<bool> x_init;
|
||||||
|
|
||||||
|
@ -12749,12 +12773,12 @@ Even if the following example works correctly on most hardware platforms, it is
|
||||||
One of the conventional patterns is below.
|
One of the conventional patterns is below.
|
||||||
|
|
||||||
std::atomic<int> state;
|
std::atomic<int> state;
|
||||||
|
|
||||||
// If state == SOME_ACTION_NEEDED maybe an action is needed, maybe not, we need to
|
// If state == SOME_ACTION_NEEDED maybe an action is needed, maybe not, we need to
|
||||||
// check again in a lock. However, if state != SOME_ACTION_NEEDED, then we can be
|
// check again in a lock. However, if state != SOME_ACTION_NEEDED, then we can be
|
||||||
// sure that an action is not needed. This is the basic assumption of double-checked
|
// sure that an action is not needed. This is the basic assumption of double-checked
|
||||||
// locking.
|
// locking.
|
||||||
|
|
||||||
if (state == SOME_ACTION_NEEDED)
|
if (state == SOME_ACTION_NEEDED)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutex);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
'
|
'
|
||||||
0xFF0000
|
0xFF0000
|
||||||
0b0101'0101
|
0b0101'0101
|
||||||
|
10x
|
||||||
'14
|
'14
|
||||||
|
20x
|
||||||
2D
|
2D
|
||||||
2K
|
2K
|
||||||
2ndEdition
|
2ndEdition
|
||||||
|
@ -69,6 +71,7 @@ CComPtr
|
||||||
cerr
|
cerr
|
||||||
chrono
|
chrono
|
||||||
cin
|
cin
|
||||||
|
Clang's
|
||||||
class'
|
class'
|
||||||
clib
|
clib
|
||||||
Cline99
|
Cline99
|
||||||
|
@ -492,6 +495,7 @@ toolchains
|
||||||
TotallyOrdered
|
TotallyOrdered
|
||||||
TP
|
TP
|
||||||
tradeoff
|
tradeoff
|
||||||
|
TSAN
|
||||||
TSs
|
TSs
|
||||||
tt
|
tt
|
||||||
typeid
|
typeid
|
||||||
|
|
Loading…
Reference in New Issue
Block a user