blob: fa586286d1caa8e04d3ed459ad43a3e58ff83f0a [file] [log] [blame]
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <functional>
#include <grpcpp/impl/codegen/callback_common.h>
#include <grpcpp/impl/codegen/status.h>
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/surface/completion_queue.h"
namespace grpc {
namespace internal {
namespace {
template <class Func, class Arg>
void CatchingCallback(Func&& func, Arg&& arg) {
#if GRPC_ALLOW_EXCEPTIONS
try {
func(arg);
} catch (...) {
// nothing to return or change here, just don't crash the library
}
#else // GRPC_ALLOW_EXCEPTIONS
func(arg);
#endif // GRPC_ALLOW_EXCEPTIONS
}
class CallbackWithSuccessImpl : public grpc_core::CQCallbackInterface {
public:
static void operator delete(void* ptr, std::size_t size) {
assert(size == sizeof(CallbackWithSuccessImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
CallbackWithSuccessImpl(grpc_call* call, CallbackWithSuccessTag* parent,
std::function<void(bool)> f)
: call_(call), parent_(parent), func_(std::move(f)) {
grpc_call_ref(call);
}
void Run(bool ok) override {
void* ignored = parent_->ops();
bool new_ok = ok;
GPR_ASSERT(parent_->ops()->FinalizeResult(&ignored, &new_ok));
GPR_ASSERT(ignored == parent_->ops());
// Last use of func_ or ok, so ok to move them out for rvalue call above
CatchingCallback(std::move(func_), std::move(ok));
func_ = nullptr; // reset to clear this out for sure
grpc_call_unref(call_);
}
private:
grpc_call* call_;
CallbackWithSuccessTag* parent_;
std::function<void(bool)> func_;
};
class CallbackWithStatusImpl : public grpc_core::CQCallbackInterface {
public:
static void operator delete(void* ptr, std::size_t size) {
assert(size == sizeof(CallbackWithStatusImpl));
}
// This operator should never be called as the memory should be freed as part
// of the arena destruction. It only exists to provide a matching operator
// delete to the operator new so that some compilers will not complain (see
// https://github.com/grpc/grpc/issues/11301) Note at the time of adding this
// there are no tests catching the compiler warning.
static void operator delete(void*, void*) { assert(0); }
CallbackWithStatusImpl(grpc_call* call, CallbackWithStatusTag* parent,
std::function<void(Status)> f)
: call_(call), parent_(parent), func_(std::move(f)), status_() {
grpc_call_ref(call);
}
void Run(bool ok) override {
void* ignored = parent_->ops();
GPR_ASSERT(parent_->ops()->FinalizeResult(&ignored, &ok));
GPR_ASSERT(ignored == parent_->ops());
// Last use of func_ or status_, so ok to move them out
CatchingCallback(std::move(func_), std::move(status_));
func_ = nullptr; // reset to clear this out for sure
grpc_call_unref(call_);
}
Status* status_ptr() { return &status_; }
private:
grpc_call* call_;
CallbackWithStatusTag* parent_;
std::function<void(Status)> func_;
Status status_;
};
} // namespace
CallbackWithSuccessTag::CallbackWithSuccessTag(grpc_call* call,
std::function<void(bool)> f,
CompletionQueueTag* ops)
: impl_(new (grpc_call_arena_alloc(call, sizeof(CallbackWithSuccessImpl)))
CallbackWithSuccessImpl(call, this, std::move(f))),
ops_(ops) {}
void CallbackWithSuccessTag::force_run(bool ok) { impl_->Run(ok); }
CallbackWithStatusTag::CallbackWithStatusTag(grpc_call* call,
std::function<void(Status)> f,
CompletionQueueTag* ops)
: ops_(ops) {
auto* impl = new (grpc_call_arena_alloc(call, sizeof(CallbackWithStatusImpl)))
CallbackWithStatusImpl(call, this, std::move(f));
impl_ = impl;
status_ = impl->status_ptr();
}
void CallbackWithStatusTag::force_run(Status s) {
*status_ = std::move(s);
impl_->Run(true);
}
} // namespace internal
} // namespace grpc