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 
19 #include <grpc/support/port_platform.h>
20 
21 #include "src/core/lib/iomgr/lockfree_event.h"
22 
23 #include <grpc/support/log.h>
24 
25 #include "src/core/lib/debug/trace.h"
26 #include "src/core/lib/iomgr/exec_ctx.h"
27 
28 extern grpc_core::DebugOnlyTraceFlag grpc_polling_trace;
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 
LockfreeEvent()63 LockfreeEvent::LockfreeEvent() { InitEvent(); }
64 
InitEvent()65 void LockfreeEvent::InitEvent() {
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 */
72   gpr_atm_no_barrier_store(&state_, kClosureNotReady);
73 }
74 
DestroyEvent()75 void LockfreeEvent::DestroyEvent() {
76   gpr_atm curr;
77   do {
78     curr = gpr_atm_no_barrier_load(&state_);
79     if (curr & kShutdownBit) {
80 #ifdef GRPC_ERROR_IS_ABSEIL_STATUS
81       internal::StatusFreeHeapPtr(curr & ~kShutdownBit);
82 #else
83       GRPC_ERROR_UNREF((grpc_error_handle)(curr & ~kShutdownBit));
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 
NotifyOn(grpc_closure * closure)95 void LockfreeEvent::NotifyOn(grpc_closure* closure) {
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. */
101     gpr_atm curr = gpr_atm_acq_load(&state_);
102     if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
103       gpr_log(GPR_DEBUG,
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. */
116         if (gpr_atm_rel_cas(&state_, kClosureNotReady,
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) */
133         if (gpr_atm_no_barrier_cas(&state_, kClosureReady, kClosureNotReady)) {
134           ExecCtx::Run(DEBUG_LOCATION, closure, GRPC_ERROR_NONE);
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 =
148               internal::StatusGetFromHeapPtr(curr & ~kShutdownBit);
149 #else
150           grpc_error_handle shutdown_err =
151               reinterpret_cast<grpc_error_handle>(curr & ~kShutdownBit);
152 #endif
153           ExecCtx::Run(DEBUG_LOCATION, closure,
154                        GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
155                            "FD Shutdown", &shutdown_err, 1));
156           return;
157         }
158 
159         /* There is already a closure!. This indicates a bug in the code */
160         gpr_log(GPR_ERROR,
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 
SetShutdown(grpc_error_handle shutdown_error)171 bool LockfreeEvent::SetShutdown(grpc_error_handle shutdown_error) {
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) {
180     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
181     if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
182       gpr_log(GPR_DEBUG,
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),
216                        GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(
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 
SetReady()231 void LockfreeEvent::SetReady() {
232   while (true) {
233     gpr_atm curr = gpr_atm_no_barrier_load(&state_);
234 
235     if (GRPC_TRACE_FLAG_ENABLED(grpc_polling_trace)) {
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 */
249         if (gpr_atm_no_barrier_cas(&state_, kClosureNotReady, kClosureReady)) {
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),
266                        GRPC_ERROR_NONE);
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
279