lockfree_event.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright 2017 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
20 
22 
23 #include <grpc/support/log.h>
24 
27 
29 
30 /* 'state' holds the to call when the fd is readable or writable respectively.
31  It can contain one of the following values:
32  kClosureReady : The fd has an I/O event of interest but there is no
33  closure yet to execute
34 
35  kClosureNotReady : The fd has no I/O event of interest
36 
37  closure ptr : The closure to be executed when the fd has an I/O
38  event of interest
39 
40  shutdown_error | kShutdownBit :
41  'shutdown_error' field ORed with kShutdownBit.
42  This indicates that the fd is shutdown. Since all
43  memory allocations are word-aligned, the lower two
44  bits of the shutdown_error pointer are always 0. So
45  it is safe to OR these with kShutdownBit
46 
47  Valid state transitions:
48 
49  <closure ptr> <-----3------ kClosureNotReady -----1-------> kClosureReady
50  | | ^ | ^ | |
51  | | | | | | |
52  | +--------------4----------+ 6 +---------2---------------+ |
53  | | |
54  | v |
55  +-----5-------> [shutdown_error | kShutdownBit] <-------7---------+
56 
57  For 1, 4 : See SetReady() function
58  For 2, 3 : See NotifyOn() function
59  For 5,6,7: See SetShutdown() function */
60 
61 namespace grpc_core {
62 
64 
66  /* Perform an atomic store to start the state machine.
67 
68  Note carefully that LockfreeEvent *MAY* be used whilst in a destroyed
69  state, while a file descriptor is on a freelist. In such a state it may
70  be SetReady'd, and so we need to perform an atomic operation here to
71  ensure no races */
73 }
74 
76  gpr_atm curr;
77  do {
79  if (curr & kShutdownBit) {
80 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
82 #else
84 #endif
85  } else {
86  GPR_ASSERT(curr == kClosureNotReady || curr == kClosureReady);
87  }
88  /* we CAS in a shutdown, no error value here. If this event is interacted
89  with post-deletion (see the note in the constructor) we want the bit
90  pattern to prevent error retention in a deleted object */
91  } while (!gpr_atm_no_barrier_cas(&state_, curr,
92  kShutdownBit /* shutdown, no error */));
93 }
94 
96  while (true) {
97  /* This load needs to be an acquire load because this can be a shutdown
98  * error that we might need to reference. Adding acquire semantics makes
99  * sure that the shutdown error has been initialized properly before us
100  * referencing it. */
104  "LockfreeEvent::NotifyOn: %p curr=%" PRIxPTR " closure=%p", this,
105  curr, closure);
106  }
107  switch (curr) {
108  case kClosureNotReady: {
109  /* kClosureNotReady -> <closure>.
110 
111  We're guaranteed by API that there's an acquire barrier before here,
112  so there's no need to double-dip and this can be a release-only.
113 
114  The release itself pairs with the acquire half of a set_ready full
115  barrier. */
117  reinterpret_cast<gpr_atm>(closure))) {
118  return; /* Successful. Return */
119  }
120 
121  break; /* retry */
122  }
123 
124  case kClosureReady: {
125  /* Change the state to kClosureNotReady. Schedule the closure if
126  successful. If not, the state most likely transitioned to shutdown.
127  We should retry.
128 
129  This can be a no-barrier cas since the state is being transitioned to
130  kClosureNotReady; set_ready and set_shutdown do not schedule any
131  closure when transitioning out of CLOSURE_NO_READY state (i.e there
132  is no other code that needs to 'happen-after' this) */
135  return; /* Successful. Return */
136  }
137 
138  break; /* retry */
139  }
140 
141  default: {
142  /* 'curr' is either a closure or the fd is shutdown(in which case 'curr'
143  contains a pointer to the shutdown-error). If the fd is shutdown,
144  schedule the closure with the shutdown error */
145  if ((curr & kShutdownBit) > 0) {
146 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
147  grpc_error_handle shutdown_err =
149 #else
150  grpc_error_handle shutdown_err =
151  reinterpret_cast<grpc_error_handle>(curr & ~kShutdownBit);
152 #endif
155  "FD Shutdown", &shutdown_err, 1));
156  return;
157  }
158 
159  /* There is already a closure!. This indicates a bug in the code */
161  "LockfreeEvent::NotifyOn: notify_on called with a previous "
162  "callback still pending");
163  abort();
164  }
165  }
166  }
167 
168  GPR_UNREACHABLE_CODE(return );
169 }
170 
172 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
173  intptr_t status_ptr = internal::StatusAllocHeapPtr(shutdown_error);
174  gpr_atm new_state = status_ptr | kShutdownBit;
175 #else
176  gpr_atm new_state = reinterpret_cast<gpr_atm>(shutdown_error) | kShutdownBit;
177 #endif
178 
179  while (true) {
183  "LockfreeEvent::SetShutdown: %p curr=%" PRIxPTR " err=%s",
184  &state_, curr, grpc_error_std_string(shutdown_error).c_str());
185  }
186  switch (curr) {
187  case kClosureReady:
188  case kClosureNotReady:
189  /* Need a full barrier here so that the initial load in notify_on
190  doesn't need a barrier */
191  if (gpr_atm_full_cas(&state_, curr, new_state)) {
192  return true; /* early out */
193  }
194  break; /* retry */
195 
196  default: {
197  /* 'curr' is either a closure or the fd is already shutdown */
198 
199  /* If fd is already shutdown, we are done */
200  if ((curr & kShutdownBit) > 0) {
201 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
202  internal::StatusFreeHeapPtr(status_ptr);
203 #else
204  GRPC_ERROR_UNREF(shutdown_error);
205 #endif
206  return false;
207  }
208 
209  /* Fd is not shutdown. Schedule the closure and move the state to
210  shutdown state.
211  Needs an acquire to pair with setting the closure (and get a
212  happens-after on that edge), and a release to pair with anything
213  loading the shutdown state. */
214  if (gpr_atm_full_cas(&state_, curr, new_state)) {
215  ExecCtx::Run(DEBUG_LOCATION, reinterpret_cast<grpc_closure*>(curr),
217  "FD Shutdown", &shutdown_error, 1));
218  return true;
219  }
220 
221  /* 'curr' was a closure but now changed to a different state. We will
222  have to retry */
223  break;
224  }
225  }
226  }
227 
228  GPR_UNREACHABLE_CODE(return false);
229 }
230 
232  while (true) {
234 
236  gpr_log(GPR_DEBUG, "LockfreeEvent::SetReady: %p curr=%" PRIxPTR, &state_,
237  curr);
238  }
239 
240  switch (curr) {
241  case kClosureReady: {
242  /* Already ready. We are done here */
243  return;
244  }
245 
246  case kClosureNotReady: {
247  /* No barrier required as we're transitioning to a state that does not
248  involve a closure */
250  return; /* early out */
251  }
252  break; /* retry */
253  }
254 
255  default: {
256  /* 'curr' is either a closure or the fd is shutdown */
257  if ((curr & kShutdownBit) > 0) {
258  /* The fd is shutdown. Do nothing */
259  return;
260  }
261  /* Full cas: acquire pairs with this cas' release in the event of a
262  spurious set_ready; release pairs with this or the acquire in
263  notify_on (or set_shutdown) */
264  else if (gpr_atm_full_cas(&state_, curr, kClosureNotReady)) {
265  ExecCtx::Run(DEBUG_LOCATION, reinterpret_cast<grpc_closure*>(curr),
267  return;
268  }
269  /* else the state changed again (only possible by either a racing
270  set_ready or set_shutdown functions. In both these cases, the closure
271  would have been scheduled for execution. So we are done here */
272  return;
273  }
274  }
275  }
276 }
277 
278 } // namespace grpc_core
trace.h
grpc_polling_trace
grpc_core::DebugOnlyTraceFlag grpc_polling_trace
grpc_core::LockfreeEvent::DestroyEvent
void DestroyEvent()
Definition: lockfree_event.cc:75
gpr_atm_no_barrier_load
#define gpr_atm_no_barrier_load(p)
Definition: impl/codegen/atm_gcc_atomic.h:53
GRPC_ERROR_NONE
#define GRPC_ERROR_NONE
Definition: error.h:234
log.h
gpr_atm_no_barrier_store
#define gpr_atm_no_barrier_store(p, value)
Definition: impl/codegen/atm_gcc_atomic.h:56
grpc_core
Definition: call_metric_recorder.h:31
grpc_core::internal::StatusAllocHeapPtr
uintptr_t StatusAllocHeapPtr(absl::Status s)
Definition: status_helper.cc:421
grpc_core::LockfreeEvent::kShutdownBit
@ kShutdownBit
Definition: lockfree_event.h:65
gpr_atm_rel_cas
#define gpr_atm_rel_cas(p, o, n)
Definition: impl/codegen/atm_gcc_sync.h:76
GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING
#define GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(desc, errs, count)
Definition: error.h:307
lockfree_event.h
GRPC_TRACE_FLAG_ENABLED
#define GRPC_TRACE_FLAG_ENABLED(f)
Definition: debug/trace.h:114
DEBUG_LOCATION
#define DEBUG_LOCATION
Definition: debug_location.h:41
grpc_core::LockfreeEvent::LockfreeEvent
LockfreeEvent()
Definition: lockfree_event.cc:63
GPR_ASSERT
#define GPR_ASSERT(x)
Definition: include/grpc/impl/codegen/log.h:94
gen_stats_data.c_str
def c_str(s, encoding='ascii')
Definition: gen_stats_data.py:38
gpr_log
GPRAPI void gpr_log(const char *file, int line, gpr_log_severity severity, const char *format,...) GPR_PRINT_FORMAT_CHECK(4
gpr_atm_acq_load
#define gpr_atm_acq_load(p)
Definition: impl/codegen/atm_gcc_atomic.h:52
grpc_core::internal::StatusGetFromHeapPtr
absl::Status StatusGetFromHeapPtr(uintptr_t ptr)
Get the status from a heap ptr.
Definition: status_helper.cc:432
grpc_core::LockfreeEvent::SetShutdown
bool SetShutdown(grpc_error_handle shutdown_error)
Definition: lockfree_event.cc:171
intptr_t
_W64 signed int intptr_t
Definition: stdint-msvc2008.h:118
gpr_atm_no_barrier_cas
#define gpr_atm_no_barrier_cas(p, o, n)
Definition: impl/codegen/atm_gcc_sync.h:74
GPR_ERROR
#define GPR_ERROR
Definition: include/grpc/impl/codegen/log.h:57
GPR_UNREACHABLE_CODE
#define GPR_UNREACHABLE_CODE(STATEMENT)
Definition: impl/codegen/port_platform.h:652
grpc_core::internal::StatusFreeHeapPtr
void StatusFreeHeapPtr(uintptr_t ptr)
Frees the allocated status at heap ptr.
Definition: status_helper.cc:427
grpc_core::TraceFlag
Definition: debug/trace.h:63
gpr_atm
intptr_t gpr_atm
Definition: impl/codegen/atm_gcc_atomic.h:32
grpc_core::LockfreeEvent::NotifyOn
void NotifyOn(grpc_closure *closure)
Definition: lockfree_event.cc:95
grpc_error_std_string
std::string grpc_error_std_string(grpc_error_handle error)
Definition: error.cc:944
grpc_core::LockfreeEvent::kClosureReady
@ kClosureReady
Definition: lockfree_event.h:65
grpc_core::LockfreeEvent::InitEvent
void InitEvent()
Definition: lockfree_event.cc:65
grpc_core::LockfreeEvent::SetReady
void SetReady()
Definition: lockfree_event.cc:231
exec_ctx.h
closure
Definition: proxy.cc:59
GRPC_ERROR_UNREF
#define GRPC_ERROR_UNREF(err)
Definition: error.h:262
grpc_core::ExecCtx::Run
static void Run(const DebugLocation &location, grpc_closure *closure, grpc_error_handle error)
Definition: exec_ctx.cc:98
GPR_DEBUG
#define GPR_DEBUG
Definition: include/grpc/impl/codegen/log.h:55
grpc_error
Definition: error_internal.h:42
grpc_core::LockfreeEvent::state_
gpr_atm state_
Definition: lockfree_event.h:67
grpc_closure
Definition: closure.h:56
grpc_core::LockfreeEvent::kClosureNotReady
@ kClosureNotReady
Definition: lockfree_event.h:65
port_platform.h
gpr_atm_full_cas
#define gpr_atm_full_cas(p, o, n)
Definition: impl/codegen/atm_gcc_sync.h:77


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:16