• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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