Coverage Report

Created: 2026-09-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/abseil-cpp/absl/base/internal/low_level_scheduling.h
Line
Count
Source
1
// Copyright 2017 The Abseil Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//      https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
//
15
// Core interfaces and definitions used by by low-level interfaces such as
16
// SpinLock.
17
18
#ifndef ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
19
#define ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_
20
21
#include <atomic>
22
#include <cstdint>
23
24
#include "absl/base/config.h"
25
#include "absl/base/internal/raw_logging.h"
26
#include "absl/base/internal/scheduling_mode.h"
27
#include "absl/base/internal/thread_identity.h"
28
#include "absl/base/macros.h"
29
30
// The following two declarations exist so SchedulingGuard may friend them with
31
// the appropriate language linkage.  These callbacks allow libc internals, such
32
// as function level statics, to schedule cooperatively when locking.
33
extern "C" bool __google_disable_rescheduling(void);
34
extern "C" void __google_enable_rescheduling(bool disable_result);
35
36
namespace absl {
37
ABSL_NAMESPACE_BEGIN
38
class CondVar;
39
class Mutex;
40
41
namespace synchronization_internal {
42
int MutexDelay(int32_t c, int mode);
43
}  // namespace synchronization_internal
44
45
namespace base_internal {
46
47
class SchedulingHelper;  // To allow use of SchedulingGuard.
48
class SpinLock;          // To allow use of SchedulingGuard.
49
50
// SchedulingGuard
51
// Provides guard semantics that may be used to disable cooperative rescheduling
52
// of the calling thread within specific program blocks.  This is used to
53
// protect resources (e.g. low-level SpinLocks or Domain code) that cooperative
54
// scheduling depends on.
55
//
56
// Domain implementations capable of rescheduling in reaction to involuntary
57
// kernel thread actions (e.g blocking due to a pagefault or syscall) must
58
// guarantee that an annotated thread is not allowed to (cooperatively)
59
// reschedule until the annotated region is complete.
60
//
61
// It is an error to attempt to use a cooperatively scheduled resource (e.g.
62
// Mutex) within a rescheduling-disabled region.
63
//
64
// All methods are async-signal safe.
65
class SchedulingGuard {
66
 public:
67
  // Returns true iff the calling thread may be cooperatively rescheduled.
68
  static bool ReschedulingIsAllowed();
69
  SchedulingGuard(const SchedulingGuard&) = delete;
70
  SchedulingGuard& operator=(const SchedulingGuard&) = delete;
71
72
  // Disable cooperative rescheduling of the calling thread.  It may still
73
  // initiate scheduling operations (e.g. wake-ups), however, it may not itself
74
  // reschedule.  Nestable.  The returned result is opaque, clients should not
75
  // attempt to interpret it.
76
  // REQUIRES: Result must be passed to a pairing EnableScheduling().
77
  static bool DisableRescheduling();
78
79
  // Marks the end of a rescheduling disabled region, previously started by
80
  // DisableRescheduling().
81
  // REQUIRES: Pairs with innermost call (and result) of DisableRescheduling().
82
  static void EnableRescheduling(bool disable_result);
83
84
  // A scoped helper for {Disable, Enable}Rescheduling().
85
  // REQUIRES: destructor must run in same thread as constructor.
86
  struct ScopedDisable {
87
0
    ScopedDisable() { disabled = SchedulingGuard::DisableRescheduling(); }
88
0
    ~ScopedDisable() { SchedulingGuard::EnableRescheduling(disabled); }
89
90
    bool disabled;
91
  };
92
93
  // A scoped helper to enable rescheduling temporarily.
94
  // REQUIRES: destructor must run in same thread as constructor.
95
  class ScopedEnable {
96
   public:
97
    ScopedEnable();
98
    ~ScopedEnable();
99
100
   private:
101
    int scheduling_disabled_depth_;
102
  };
103
};
104
105
//------------------------------------------------------------------------------
106
// End of public interfaces.
107
//------------------------------------------------------------------------------
108
109
0
inline bool SchedulingGuard::ReschedulingIsAllowed() {
110
0
  ThreadIdentity* identity = CurrentThreadIdentityIfPresent();
111
0
  if (identity != nullptr) {
112
0
    ThreadIdentity::SchedulerState* state = &identity->scheduler_state;
113
0
    // For a thread to be eligible for re-scheduling it must have a bound
114
0
    // schedulable (otherwise it's not cooperative) and not be within a
115
0
    // SchedulerGuard region.
116
0
    return state->bound_schedulable.load(std::memory_order_relaxed) !=
117
0
               nullptr &&
118
0
           state->scheduling_disabled_depth.load(std::memory_order_relaxed) ==
119
0
               0;
120
0
  } else {
121
0
    // Cooperative threads always have a ThreadIdentity.
122
0
    return false;
123
0
  }
124
0
}
125
126
// We don't use [[nodiscard]] here as some clients (e.g.
127
// FinishPotentiallyBlockingRegion()) cannot yet properly consume it.
128
744k
inline bool SchedulingGuard::DisableRescheduling() {
129
744k
  ThreadIdentity* identity;
130
744k
  identity = CurrentThreadIdentityIfPresent();
131
744k
  if (identity != nullptr) {
132
    // The depth is accessed concurrently from other threads, so it must be
133
    // atomic, but it's only mutated from this thread, so we don't need an
134
    // atomic increment.
135
744k
    int old_val = identity->scheduler_state.scheduling_disabled_depth.load(
136
744k
        std::memory_order_relaxed);
137
744k
    identity->scheduler_state.scheduling_disabled_depth.store(
138
744k
        old_val + 1, std::memory_order_relaxed);
139
744k
    return true;
140
744k
  } else {
141
4
    return false;
142
4
  }
143
744k
}
144
145
744k
inline void SchedulingGuard::EnableRescheduling(bool disable_result) {
146
744k
  if (!disable_result) {
147
    // There was no installed thread identity at the time that scheduling was
148
    // disabled, so we have nothing to do.  This is an implementation detail
149
    // that may change in the future, clients may not depend on it.
150
    // EnableRescheduling() must always be called.
151
1
    return;
152
1
  }
153
154
744k
  ThreadIdentity* identity;
155
  // A thread identity exists, see above
156
744k
  identity = CurrentThreadIdentityIfPresent();
157
  // The depth is accessed concurrently from other threads, so it must be
158
  // atomic, but it's only mutated from this thread, so we don't need an atomic
159
  // decrement.
160
744k
  int old_val = identity->scheduler_state.scheduling_disabled_depth.load(
161
744k
      std::memory_order_relaxed);
162
744k
  identity->scheduler_state.scheduling_disabled_depth.store(
163
744k
      old_val - 1, std::memory_order_relaxed);
164
744k
}
165
166
0
inline SchedulingGuard::ScopedEnable::ScopedEnable() {
167
0
  ThreadIdentity* identity;
168
0
  identity = CurrentThreadIdentityIfPresent();
169
0
  if (identity != nullptr) {
170
0
    scheduling_disabled_depth_ =
171
0
        identity->scheduler_state.scheduling_disabled_depth.load(
172
0
            std::memory_order_relaxed);
173
0
    if (scheduling_disabled_depth_ != 0) {
174
      // The store below does not need to be compare_exchange because
175
      // the value is never modified concurrently (only accessed).
176
0
      identity->scheduler_state.scheduling_disabled_depth.store(
177
0
          0, std::memory_order_relaxed);
178
0
    }
179
0
  } else {
180
0
    scheduling_disabled_depth_ = 0;
181
0
  }
182
0
}
183
184
0
inline SchedulingGuard::ScopedEnable::~ScopedEnable() {
185
0
  if (scheduling_disabled_depth_ == 0) {
186
0
    return;
187
0
  }
188
0
  ThreadIdentity* identity = CurrentThreadIdentityIfPresent();
189
  // itentity is guaranteed to exist, see the constructor above.
190
0
  identity->scheduler_state.scheduling_disabled_depth.store(
191
0
      scheduling_disabled_depth_, std::memory_order_relaxed);
192
0
}
193
194
}  // namespace base_internal
195
ABSL_NAMESPACE_END
196
}  // namespace absl
197
198
#endif  // ABSL_BASE_INTERNAL_LOW_LEVEL_SCHEDULING_H_