1 /*
2 *
3 * Copyright 2018 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 #ifndef GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
20 #define GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
21
22 #include <functional>
23
24 #include <grpc/impl/codegen/grpc_types.h>
25 #include <grpcpp/impl/codegen/call.h>
26 #include <grpcpp/impl/codegen/channel_interface.h>
27 #include <grpcpp/impl/codegen/config.h>
28 #include <grpcpp/impl/codegen/core_codegen_interface.h>
29 #include <grpcpp/impl/codegen/status.h>
30
31 namespace grpc {
32 namespace internal {
33
34 /// An exception-safe way of invoking a user-specified callback function
35 template <class Func, class Arg>
CatchingCallback(Func && func,Arg && arg)36 void CatchingCallback(Func&& func, Arg&& arg) {
37 #if GRPC_ALLOW_EXCEPTIONS
38 try {
39 func(arg);
40 } catch (...) {
41 // nothing to return or change here, just don't crash the library
42 }
43 #else // GRPC_ALLOW_EXCEPTIONS
44 func(arg);
45 #endif // GRPC_ALLOW_EXCEPTIONS
46 }
47
48 // The contract on these tags is that they are single-shot. They must be
49 // constructed and then fired at exactly one point. There is no expectation
50 // that they can be reused without reconstruction.
51
52 class CallbackWithStatusTag
53 : public grpc_experimental_completion_queue_functor {
54 public:
55 // always allocated against a call arena, no memory free required
delete(void * ptr,std::size_t size)56 static void operator delete(void* ptr, std::size_t size) {
57 assert(size == sizeof(CallbackWithStatusTag));
58 }
59
60 // This operator should never be called as the memory should be freed as part
61 // of the arena destruction. It only exists to provide a matching operator
62 // delete to the operator new so that some compilers will not complain (see
63 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
64 // there are no tests catching the compiler warning.
delete(void *,void *)65 static void operator delete(void*, void*) { assert(0); }
66
CallbackWithStatusTag(grpc_call * call,std::function<void (Status)> f,CompletionQueueTag * ops)67 CallbackWithStatusTag(grpc_call* call, std::function<void(Status)> f,
68 CompletionQueueTag* ops)
69 : call_(call), func_(std::move(f)), ops_(ops) {
70 g_core_codegen_interface->grpc_call_ref(call);
71 functor_run = &CallbackWithStatusTag::StaticRun;
72 }
~CallbackWithStatusTag()73 ~CallbackWithStatusTag() {}
status_ptr()74 Status* status_ptr() { return &status_; }
75
76 // force_run can not be performed on a tag if operations using this tag
77 // have been sent to PerformOpsOnCall. It is intended for error conditions
78 // that are detected before the operations are internally processed.
force_run(Status s)79 void force_run(Status s) {
80 status_ = std::move(s);
81 Run(true);
82 }
83
84 private:
85 grpc_call* call_;
86 std::function<void(Status)> func_;
87 CompletionQueueTag* ops_;
88 Status status_;
89
StaticRun(grpc_experimental_completion_queue_functor * cb,int ok)90 static void StaticRun(grpc_experimental_completion_queue_functor* cb,
91 int ok) {
92 static_cast<CallbackWithStatusTag*>(cb)->Run(static_cast<bool>(ok));
93 }
Run(bool ok)94 void Run(bool ok) {
95 void* ignored = ops_;
96
97 GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &ok));
98 GPR_CODEGEN_ASSERT(ignored == ops_);
99
100 // Last use of func_ or status_, so ok to move them out
101 CatchingCallback(std::move(func_), std::move(status_));
102
103 func_ = nullptr; // reset to clear this out for sure
104 status_ = Status(); // reset to clear this out for sure
105 g_core_codegen_interface->grpc_call_unref(call_);
106 }
107 };
108
109 class CallbackWithSuccessTag
110 : public grpc_experimental_completion_queue_functor {
111 public:
112 // always allocated against a call arena, no memory free required
delete(void * ptr,std::size_t size)113 static void operator delete(void* ptr, std::size_t size) {
114 assert(size == sizeof(CallbackWithSuccessTag));
115 }
116
117 // This operator should never be called as the memory should be freed as part
118 // of the arena destruction. It only exists to provide a matching operator
119 // delete to the operator new so that some compilers will not complain (see
120 // https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
121 // there are no tests catching the compiler warning.
delete(void *,void *)122 static void operator delete(void*, void*) { assert(0); }
123
CallbackWithSuccessTag(grpc_call * call,std::function<void (bool)> f,CompletionQueueTag * ops)124 CallbackWithSuccessTag(grpc_call* call, std::function<void(bool)> f,
125 CompletionQueueTag* ops)
126 : call_(call), func_(std::move(f)), ops_(ops) {
127 g_core_codegen_interface->grpc_call_ref(call);
128 functor_run = &CallbackWithSuccessTag::StaticRun;
129 }
130
ops()131 CompletionQueueTag* ops() { return ops_; }
132
133 // force_run can not be performed on a tag if operations using this tag
134 // have been sent to PerformOpsOnCall. It is intended for error conditions
135 // that are detected before the operations are internally processed.
force_run(bool ok)136 void force_run(bool ok) { Run(ok); }
137
138 private:
139 grpc_call* call_;
140 std::function<void(bool)> func_;
141 CompletionQueueTag* ops_;
142
StaticRun(grpc_experimental_completion_queue_functor * cb,int ok)143 static void StaticRun(grpc_experimental_completion_queue_functor* cb,
144 int ok) {
145 static_cast<CallbackWithSuccessTag*>(cb)->Run(static_cast<bool>(ok));
146 }
Run(bool ok)147 void Run(bool ok) {
148 void* ignored = ops_;
149 bool new_ok = ok;
150 GPR_CODEGEN_ASSERT(ops_->FinalizeResult(&ignored, &new_ok));
151 GPR_CODEGEN_ASSERT(ignored == ops_);
152
153 // Last use of func_, so ok to move it out for rvalue call above
154 CatchingCallback(std::move(func_), ok);
155
156 func_ = nullptr; // reset to clear this out for sure
157 g_core_codegen_interface->grpc_call_unref(call_);
158 }
159 };
160
161 } // namespace internal
162 } // namespace grpc
163
164 #endif // GRPCPP_IMPL_CODEGEN_CALLBACK_COMMON_H
165