Skip to content
File

Blob: src/workerd/util/thread-scopes.h

cpp133 lines
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#pragma once
6// This file contains several horrible hacks involving setting a thread-local value within some
7// scope in the call stack, and then being able to check the value from deeper in the stack,
8// without passing down an object. We use this pattern to signal hints across modules that do not
9// directly call each other, where it would be excessively inconvenient to pass the value down the
10// stack, perhaps because there is code in between that we do not control (e.g., V8).
11//
12// This is an anti-pattern and these should be considered HORRIBLE HACKS... but they get their jobs
13// done for the time being.
14 
15#include <kj/common.h>
16 
17#include <cinttypes>
18 
19namespace workerd {
20 
21// Normally, we prohibit V8 worker threads, but in some cases it's useful to temporarily allow
22// them. Create this on the stack to temporarily allow V8 code running in the current thread to
23// spawn worker threads.
24//
25// In particular this is used when loading Wasm modules, to properly enable Liftoff and Tier-up.
26class AllowV8BackgroundThreadsScope {
27 public:
28 AllowV8BackgroundThreadsScope();
29 ~AllowV8BackgroundThreadsScope() noexcept(false);
30 
31 static bool isActive();
32 
33 KJ_DISALLOW_COPY_AND_MOVE(AllowV8BackgroundThreadsScope);
34};
35 
36// Tracks whether the process hosts isolates from multiple parties that don't know about each
37// other. In such a case, we must take additional precautions against Spectre, and prohibit
38// functionality which cannot be made Spectre-safe.
39//
40// (Note that simply turning this on is NOT sufficient to enable Spectre protection. Instead, this
41// is mostly used as a safeguard to *disable* functionality that is known not to be Spectre-safe.)
42//
43// This is actually a process-level flag rather than thread-level. Once a process becomes
44// multi-tenant it cannot go back, since secrets could persist in memory.
45bool isMultiTenantProcess();
46 
47// Tracks whether the process hosts isolates from multiple parties that don't know about each
48// other. In such a case, we must take additional precautions against Spectre, and prohibit
49// functionality which cannot be made Spectre-safe.
50//
51// (Note that simply turning this on is NOT sufficient to enable Spectre protection. Instead, this
52// is mostly used as a safeguard to *disable* functionality that is known not to be Spectre-safe.)
53//
54// This is actually a process-level flag rather than thread-level. Once a process becomes
55// multi-tenant it cannot go back, since secrets could persist in memory.
56void setMultiTenantProcess();
57 
58// Tracks whether the process should run in "predictable mode" for testing purposes. This causes
59// random number generators to return static results instead, changes some timers to return zero,
60// etc. This should only be used in tests.
61bool isPredictableModeForTest();
62 
63// Tracks whether the process should run in "predictable mode" for testing purposes. This causes
64// random number generators to return static results instead, changes some timers to return zero,
65// etc. This should only be used in tests.
66void setPredictableModeForTest();
67 
68// When enabled, forces a full V8 garbage collection at key points where the KJ event loop
69// re-enters JavaScript (e.g., awaitIo continuations). This helps detect KJ async objects that
70// are reachable from the JS heap without proper IoOwn wrapping — such objects would violate
71// DISALLOW_KJ_IO_DESTRUCTORS_SCOPE when collected. This makes tests significantly slower and
72// should only be used for targeted stress testing.
73//
74// Can be enabled via setGcStressModeForTest() (used by workerd's --gc-stress CLI flag) or by
75// setting the WORKERD_GC_STRESS=1 environment variable (useful for binaries without a dedicated
76// CLI flag, e.g., edgeworker tests where the test-runner spawns the server as a subprocess).
77bool isGcStressModeForTest();
78void setGcStressModeForTest();
79 
80// RAII class which allows the thread's active watchdog to observe forward progress through
81// changes in a uint64_t. Use this in places where your code cannot call Watchdog::checkIn() and
82// may block for longer than the watchdog timeout, but can still observe forward progress.
83class ThreadProgressCounter {
84 public:
85 // When a ProgressCounter is instantiated, it saves the current value of `counter`. When
86 // Watchdog::tryHandleSignal() is called with an active ProgressCounter on the thread, the
87 // function compares this saved value with the (possibly updated) current value. If they differ,
88 // we consider the process to have exhibited forward progress. Note that we don't make any
89 // assumption of a less-than relationship between consecutive counter values -- you could use
90 // random values if you want.
91 //
92 // It is expected that all read/write operations to `counter` are atomic.
93 //
94 // ProgressCounters are reentrant, like v8::Lockers.
95 explicit ThreadProgressCounter(uint64_t& counter);
96 
97 ~ThreadProgressCounter() noexcept(false);
98 KJ_DISALLOW_COPY_AND_MOVE(ThreadProgressCounter);
99 
100 // Returns true if progress has been made since the last call to update().
101 static bool hasProgress();
102 
103 // Updates the saved progress value so that hasProgress() now returns false until the next time
104 // the counter is updated.
105 static void acknowledgeProgress();
106 
107 private:
108 uint64_t savedValue;
109 uint64_t& counter;
110 
111 friend class Watchdog;
112};
113 
114// ======================================================================================
115 
116// Create on stack in scopes where any attempt to take an isolate lock should log a warning.
117// Isolate locks can block for a relatively long time, so we especially try to avoid taking
118// them while any other locks are held.
119class WarnAboutIsolateLockScope {
120 public:
121 WarnAboutIsolateLockScope();
122 ~WarnAboutIsolateLockScope() noexcept(false);
123 KJ_DISALLOW_COPY(WarnAboutIsolateLockScope);
124 WarnAboutIsolateLockScope(WarnAboutIsolateLockScope&&);
125 void release();
126 
127 static void maybeWarn();
128 
129 private:
130 bool released = false;
131};
132} // namespace workerd