Skip to content
File

Blob: src/workerd/util/thread-scopes.c++

4.6 KB
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4 
5#include "thread-scopes.h"
6 
7#include <kj/debug.h>
8 
9#include <atomic>
10#include <cstdlib>
11 
12namespace workerd {
13 
14using kj::uint;
15 
16namespace {
17 
18thread_local uint allowV8BackgroundThreadScopeCount = 0;
19 
20bool multiTenantProcess = false;
21bool predictableMode = false;
22bool gcStressMode = false;
23 
24// This variable is read in signal handlers, so use atomic stores and compiler barriers as
25// needed in regular code. Atomic loads are unnecessary, because we're not synchronizing with
26// other threads.
27thread_local ThreadProgressCounter* activeProgressCounter = nullptr;
28 
29} // namespace
30 
31AllowV8BackgroundThreadsScope::AllowV8BackgroundThreadsScope() {
32 ++allowV8BackgroundThreadScopeCount;
33}
34 
35AllowV8BackgroundThreadsScope::~AllowV8BackgroundThreadsScope() noexcept(false) {
36 --allowV8BackgroundThreadScopeCount;
37}
38 
39bool AllowV8BackgroundThreadsScope::isActive() {
40 return allowV8BackgroundThreadScopeCount > 0;
41}
42 
43bool isMultiTenantProcess() {
44 return multiTenantProcess;
45}
46 
47void setMultiTenantProcess() {
48 multiTenantProcess = true;
49}
50 
51bool isPredictableModeForTest() {
52 return predictableMode;
53}
54 
55void setPredictableModeForTest() {
56 predictableMode = true;
57}
58 
59bool isGcStressModeForTest() {
60 // Also honor the WORKERD_GC_STRESS environment variable so that gc-stress mode can be enabled
61 // in binaries that don't have a --gc-stress CLI flag (e.g., edgeworker's prod subcommand).
62 // The env var is checked once and cached; thread-safe via C++ static local initialization.
63 static const bool fromEnv = [] {
64 const char* val = std::getenv("WORKERD_GC_STRESS");
65 return val != nullptr && val[0] != '0' && val[0] != '\0';
66 }();
67 return gcStressMode || fromEnv;
68}
69 
70void setGcStressModeForTest() {
71 gcStressMode = true;
72}
73 
74ThreadProgressCounter::ThreadProgressCounter(uint64_t& counter)
75 : savedValue(__atomic_load_n(&counter, __ATOMIC_RELAXED)),
76 counter(counter) {
77 if (activeProgressCounter == nullptr) {
78 // Release compiler barrier guarantees we're initialized before signal handlers can see us.
79 std::atomic_signal_fence(std::memory_order_release);
80 __atomic_store_n(&activeProgressCounter, this, __ATOMIC_RELAXED);
81 } else {
82 // Another progress counter is active on this thread, likely meaning we reentered.
83 }
84}
85 
86ThreadProgressCounter::~ThreadProgressCounter() noexcept(false) {
87 auto& self = KJ_ASSERT_NONNULL(
88 activeProgressCounter, "~ProgressCounter() with no active progress counter.");
89 if (&self == this) {
90 // Acquire compiler barrier to prevent any teardown from leaking above this nullification.
91 KJ_DEFER({
92 __atomic_store_n(&activeProgressCounter, nullptr, __ATOMIC_RELAXED);
93 std::atomic_signal_fence(std::memory_order_acquire);
94 });
95 } else {
96 // Nothing to do, tearing down reentered progress counter.
97 }
98}
99 
100bool ThreadProgressCounter::hasProgress() {
101 KJ_IF_SOME(progressCounter, activeProgressCounter) {
102 // The counter itself may be incremented by any thread, but there's no real synchronization
103 // concern, so we can use relaxed memory ordering. If the machine is so bogged down that a
104 // stale value causes a false positive, then crashing seems reasonable.
105 auto currentValue = __atomic_load_n(&progressCounter.counter, __ATOMIC_RELAXED);
106 
107 // `savedValue` is only ever accessed by our own thread, so no need for atomics here.
108 if (progressCounter.savedValue != currentValue) {
109 return true;
110 }
111 }
112 
113 return false;
114}
115 
116void ThreadProgressCounter::acknowledgeProgress() {
117 KJ_IF_SOME(progressCounter, activeProgressCounter) {
118 progressCounter.savedValue = __atomic_load_n(&progressCounter.counter, __ATOMIC_RELAXED);
119 }
120}
121 
122// ======================================================================================
123 
124namespace {
125thread_local uint warnAboutIsolateLockScopeCount = 0;
126} // namespace
127 
128WarnAboutIsolateLockScope::WarnAboutIsolateLockScope() {
129 ++warnAboutIsolateLockScopeCount;
130}
131 
132WarnAboutIsolateLockScope::~WarnAboutIsolateLockScope() noexcept(false) {
133 if (!released) release();
134}
135 
136WarnAboutIsolateLockScope::WarnAboutIsolateLockScope(WarnAboutIsolateLockScope&& other)
137 : released(other.released) {
138 other.released = true;
139}
140 
141void WarnAboutIsolateLockScope::release() {
142 if (!released) {
143 --warnAboutIsolateLockScopeCount;
144 released = true;
145 }
146}
147 
148void WarnAboutIsolateLockScope::maybeWarn() {
149 if (warnAboutIsolateLockScopeCount > 0) {
150 KJ_LOG(WARNING, "taking isolate lock at a bad time", kj::getStackTrace());
151 }
152}
153 
154} // namespace workerd