Skip to content

Commit 1843e59

Browse files
committed
tools: lint thread_local in src
Several Environments can share a thread, so state in src/ that belongs to one of them cannot be kept in a thread_local. Add a cpplint rule that rejects thread_local in src/ unless the declaration is marked with NOLINTNEXTLINE(runtime/thread_local), and mark the existing uses, which are per-thread by design. Signed-off-by: Shelley Vohr <shelley.vohr@gmail.com>
1 parent d9a25c0 commit 1843e59

9 files changed

Lines changed: 33 additions & 0 deletions

File tree

‎src/api/environment.cc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,7 @@ Maybe<void> InitializePrimordials(Local<Context> context,
10481048
// in the first place. However, creating BuiltinLoader instances is
10491049
// relatively cheap and all the scripts that we may want to run at
10501050
// startup are always present in it.
1051+
// NOLINTNEXTLINE(runtime/thread_local)
10511052
thread_local builtins::BuiltinLoader builtin_loader;
10521053
// Primordials can always be just eagerly compiled.
10531054
builtin_loader.SetEagerCompile();

‎src/env.cc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,7 @@ void Environment::ClosePerEnvHandles() {
14421442
close_and_finish(reinterpret_cast<uv_handle_t*>(&task_queues_async_));
14431443
}
14441444

1445+
// NOLINTNEXTLINE(runtime/thread_local)
14451446
thread_local int handle_cleanup_depth = 0;
14461447

14471448
void Environment::CleanupHandles() {

‎src/node_binding.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ struct dl_wrap {
180180
static Mutex dlhandles_mutex;
181181
static std::unordered_set<dl_wrap*, dl_wrap::hash, dl_wrap::equal>
182182
dlhandles;
183+
// NOLINTNEXTLINE(runtime/thread_local)
183184
static thread_local std::string dlerror_storage;
184185

185186
char* wrapped_dlerror() {
@@ -286,6 +287,7 @@ using v8::Value;
286287
// Globals per process
287288
static node_module* modlist_internal;
288289
static node_module* modlist_linked;
290+
// NOLINTNEXTLINE(runtime/thread_local)
289291
static thread_local node_module* thread_local_modpending;
290292

291293
// This is set by node::Init() which is used by embedders

‎src/node_debug.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ using v8::Number;
2424
using v8::Object;
2525
using v8::Value;
2626

27+
// NOLINTNEXTLINE(runtime/thread_local)
2728
thread_local std::unordered_map<FastStringKey, int, FastStringKey::Hash>
2829
generic_usage_counters;
30+
// NOLINTNEXTLINE(runtime/thread_local)
2931
thread_local std::unordered_map<FastStringKey, int, FastStringKey::Hash>
3032
v8_fast_api_call_counts;
3133

‎src/node_errors.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,11 @@ static std::string GetErrorSource(Isolate* isolate,
190190
}
191191

192192
static std::atomic<bool> is_in_oom{false};
193+
// NOLINTNEXTLINE(runtime/thread_local)
193194
static thread_local std::atomic<bool> is_retrieving_js_stacktrace{false};
194195
// This is thread-local because it only guards re-entrancy within the current
195196
// thread's uncaught-exception path; no cross-thread synchronization is needed.
197+
// NOLINTNEXTLINE(runtime/thread_local)
196198
static thread_local bool is_in_uncaught_exception = false;
197199
MaybeLocal<StackTrace> GetCurrentStackTrace(Isolate* isolate, int frame_count) {
198200
if (isolate == nullptr) {

‎src/node_internals.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ class InternalCallbackScope {
286286
// Non-zero while an Environment on this thread is closing its handles with JS
287287
// disallowed isolate-wide; InternalCallbackScope re-allows it for the other
288288
// Environments whose callbacks run in those loop turns.
289+
// NOLINTNEXTLINE(runtime/thread_local)
289290
extern thread_local int handle_cleanup_depth;
290291

291292
class DebugSealHandleScope {

‎src/quic/data.cc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ using v8::Undefined;
3333
using v8::Value;
3434

3535
namespace quic {
36+
// NOLINTNEXTLINE(runtime/thread_local)
3637
thread_local int DebugIndentScope::indent_ = 0;
3738

3839
Path::Path(const SocketAddress& local, const SocketAddress& remote) {

‎src/quic/defs.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class DebugIndentScope final {
395395
}
396396

397397
private:
398+
// NOLINTNEXTLINE(runtime/thread_local)
398399
static thread_local int indent_;
399400
};
400401

‎tools/cpplint.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@
350350
"runtime/printf_format",
351351
"runtime/references",
352352
"runtime/string",
353+
"runtime/thread_local",
353354
"runtime/threadsafe_fn",
354355
"runtime/vlog",
355356
"runtime/v8_persistent",
@@ -7446,6 +7447,25 @@ def CheckStringValueUsage(filename, lines, error):
74467447
'Use node::TwoByteValue instead.')
74477448

74487449

7450+
def CheckThreadLocalUsage(filename, lines, error):
7451+
"""Logs an error if thread_local is used in src/.
7452+
Args:
7453+
filename: The name of the current file.
7454+
lines: An array of strings, each representing a line of the file.
7455+
error: The function to call with any errors found.
7456+
"""
7457+
if not (filename.startswith('src/') or filename.startswith('src\\')):
7458+
return
7459+
7460+
for linenum, line in enumerate(lines):
7461+
if re.search(r'\bthread_local\b', line.split('//', 1)[0]):
7462+
error(filename, linenum, 'runtime/thread_local', 5,
7463+
'Several Environments can share a thread, so keep state that '
7464+
'belongs to one on the Environment or its BindingData. Mark '
7465+
'intentionally per-thread state with '
7466+
'NOLINTNEXTLINE(runtime/thread_local).')
7467+
7468+
74497469
def ProcessLine(
74507470
filename,
74517471
file_extension,
@@ -7609,6 +7629,8 @@ def ProcessFileData(filename, file_extension, lines, error, extra_check_function
76097629

76107630
CheckStringValueUsage(filename, lines, error)
76117631

7632+
CheckThreadLocalUsage(filename, lines, error)
7633+
76127634

76137635
def ProcessConfigOverrides(filename):
76147636
"""Loads the configuration files and processes the config overrides.

0 commit comments

Comments
 (0)