From d8cee7085b3951173dedfdd6a20b585446c1115c Mon Sep 17 00:00:00 2001
From: Daniel Cheng thread_local Variables
other thread_local
variables are subject to the same
initialization-order issues as static variables (and more besides).
thread_local
variable instances are not destroyed before their
-thread terminates, so they do not have the destruction-order issues of static
-variables.
thread_local
variables have a subtle destruction-order issue:
+during thread shutdown, thread_local
variables will be destroyed
+in the opposite order of their initialization (as is generally true in C++).
+If code triggered by the destructor of any thread_local
variable
+refers to any already-destroyed thread_local
on that thread, we will
+get a particularly hard to diagnose use-after-free.
thread_local
variable may trigger execution of
- an unpredictable and uncontrollable amount of other code.thread_local
variables are effectively global variables,
and have all the drawbacks of global variables other than lack of
thread-safety.thread_local
variable scales with
the number of running threads (in the worst case), which can be quite large
in a program.thread_local
.thread_local
may not be as efficient as certain compiler
- intrinsics.thread_local
unless they are also
+ static
.thread_local
variables
+ have complex destructors. In particular, the destructor of any such variable must not
+ call any code (transitively) that refers to any potentially-destroyed
+ thread_local
. This property is hard to enforce.thread_local
s. Specifically, skipping destructors for globals and static
+ variables is allowable because their lifetimes end at program shutdown. Thus, any "leak"
+ is managed immediately by the OS cleaning up our memory and other resources. By
+ contrast, skipping destructors for thread_local
variables leads to resource
+ leaks proportional to the total number of threads that terminate during the lifetime of
+ the program.thread_local
variables inside a function have no safety
- concerns, so they can be used without restriction. Note that you can use
+
thread_local
variables at class or namespace scope must be
+ initialized with a true compile-time constant (i.e., they must have no
+ dynamic initialization). To enforce this, thread_local
variables
+ at class or namespace scope must be annotated with
+
+
+
+ ABSL_CONST_INIT
+ (or constexpr
, but that should be rare):
ABSL_CONST_INIT thread_local Foo foo = ...; ++ +
thread_local
variables inside a function have no initialization
+ concerns, but still risk use-after-free during thread exit. Note that you can use
a function-scope thread_local
to simulate a class- or
namespace-scope thread_local
by defining a function or
static method that exposes it:
thread_local
variables at class or namespace scope must be
-initialized with a true compile-time constant (i.e., they must have no
-dynamic initialization). To enforce this, thread_local
variables
-at class or namespace scope must be annotated with
-
-
-
-ABSL_CONST_INIT
-(or constexpr
, but that should be rare):
ABSL_CONST_INIT thread_local Foo foo = ...; -+
Note that thread_local
variables will be destroyed whenever a thread exits.
+ If the destructor of any such variable refers to any other (potentially-destroyed)
+ thread_local
we will suffer from hard to diagnose use-after-free bugs.
+ Prefer trivial types, or types that provably run no user-provided code at destruction to
+ minimize the potential of accessing any other thread_local
.
+
thread_local
should be preferred over other mechanisms for
defining thread-local data.
void*
. Use this
only if you know what you are doing and you understand the aliasing
- issues. Also, consider the alternative
- absl::bit_cast
.
+ issues. Also, consider dereferencing the pointer (without a cast) and
+ using absl::bit_cast
to cast the resulting value.
absl::bit_cast
to interpret the raw bits of a
value using a different type of the same size (a type pun), such as
@@ -4226,8 +4248,8 @@ The names of variables (including function parameters) and data members are
-all lowercase, with underscores between words. Data members of classes (but not
-structs) additionally have trailing underscores. For instance:
+snake_case
(all lowercase, with underscores between words). Data members of classes
+(but not structs) additionally have trailing underscores. For instance:
a_local_variable
, a_struct_data_member
,
a_class_data_member_
.
For example:
-std::string table_name; // OK - lowercase with underscore. +std::string table_name; // OK - snake_case.std::string tableName; // Bad - mixed case. @@ -4288,7 +4310,22 @@Constant Names
see Storage Duration for details) should be named this way. This convention is optional for variables of other storage classes, e.g., automatic -variables, otherwise the usual variable naming rules apply. +variables; otherwise the usual variable naming rules apply. For example: + +void ComputeFoo(absl::string_view suffix) { + // Either of these is acceptable. + const absl::string_view kPrefix = "prefix"; + const absl::string_view prefix = "prefix"; + ... +} ++ +void ComputeFoo(absl::string_view suffix) { + // Bad - different invocations of ComputeFoo give kCombined different values. + const std::string kCombined = absl::StrCat(kPrefix, suffix); + ... +} +Function Names
@@ -4450,12 +4487,19 @@File Comments
Start each file with license boilerplate.
-File comments describe the contents of a file. If a file declares, -implements, or tests exactly one abstraction that is documented by a comment -at the point of declaration, file comments are not required. All other files -must have file comments.
+If a source file (such as a
-.h
file) declares multiple user-facing abstractions +(common functions, related classes, etc.), include a comment describing the collection of those +abstractions. Include enough detail for future authors to know what does not fit there. However, +the detailed documentation about individual abstractions belongs with those abstractions, not at the +file level.Legal Notice and Author Line
+For instance, if you write a file comment for
frobber.h
, you do not need +to include a file comment infrobber.cc
or +frobber_test.cc
. On the other hand, if you write a collection of classes in +registered_objects.cc
that has no associated header file, you must include a file +comment inregistered_objects.cc
. + +Legal Notice and Author Line
@@ -4471,17 +4515,6 @@Legal Notice and Author Line
New files should usually not contain copyright notice or author line. -File Contents
- -If a
- -.h
declares multiple abstractions, the file-level comment -should broadly describe the contents of the file, and how the abstractions are -related. A 1 or 2 sentence file-level comment may be sufficient. The detailed -documentation about individual abstractions belongs with those abstractions, -not at the file level.Do not duplicate comments in both the
-.h
and the -.cc
. Duplicated comments diverge.Class Comments
Every non-obvious class or struct declaration should have an @@ -4759,23 +4792,18 @@
TODO Comments
- +about the problem referenced by the
TODO
s should include the stringTODO
in all caps, followed by the -name, e-mail address, bug ID, or other +bug ID, name, e-mail address, or other identifier of the person or issue with the best context -about the problem referenced by theTODO
. The -main purpose is to have a consistentTODO
that -can be searched to find out how to get more details upon -request. ATODO
is not a commitment that the -person referenced will fix the problem. Thus when you create -aTODO
with a name, it is almost always your -name that is given.TODO
. +-// TODO(kl@gmail.com): Use a "*" here for concatenation operator. -// TODO(Zeke) change this to use relations. -// TODO(bug 12345): remove the "Last visitors" feature. +// TODO: bug 12345678 - Remove this after the 2047q4 compatibility window expires. +// TODO: example.com/my-design-doc - Manually fix up this code the next time it's touched. +// TODO(bug 12345678): Update this list after the Foo service is turned down. +// TODO(John): Use a "\*" here for concatenation operator.