1 /* 2 * Copyright 2018 gRPC authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 #include <grpcpp/alarm.h> 19 20 #include <memory> 21 22 #include <grpc/support/log.h> 23 #include <grpc/support/port_platform.h> 24 #include <grpcpp/completion_queue.h> 25 #include <grpcpp/impl/grpc_library.h> 26 #include <grpcpp/support/time.h> 27 #include "src/core/lib/iomgr/exec_ctx.h" 28 #include "src/core/lib/iomgr/timer.h" 29 #include "src/core/lib/surface/completion_queue.h" 30 31 #include <grpc/support/log.h> 32 #include "src/core/lib/debug/trace.h" 33 34 namespace grpc { 35 36 namespace internal { 37 class AlarmImpl : public CompletionQueueTag { 38 public: AlarmImpl()39 AlarmImpl() : cq_(nullptr), tag_(nullptr) { 40 gpr_ref_init(&refs_, 1); 41 grpc_timer_init_unset(&timer_); 42 } ~AlarmImpl()43 ~AlarmImpl() { 44 grpc_core::ExecCtx exec_ctx; 45 if (cq_ != nullptr) { 46 GRPC_CQ_INTERNAL_UNREF(cq_, "alarm"); 47 } 48 } FinalizeResult(void ** tag,bool * status)49 bool FinalizeResult(void** tag, bool* status) override { 50 *tag = tag_; 51 Unref(); 52 return true; 53 } Set(CompletionQueue * cq,gpr_timespec deadline,void * tag)54 void Set(CompletionQueue* cq, gpr_timespec deadline, void* tag) { 55 grpc_core::ExecCtx exec_ctx; 56 GRPC_CQ_INTERNAL_REF(cq->cq(), "alarm"); 57 cq_ = cq->cq(); 58 tag_ = tag; 59 GPR_ASSERT(grpc_cq_begin_op(cq_, this)); 60 GRPC_CLOSURE_INIT(&on_alarm_, 61 [](void* arg, grpc_error* error) { 62 // queue the op on the completion queue 63 AlarmImpl* alarm = static_cast<AlarmImpl*>(arg); 64 alarm->Ref(); 65 grpc_cq_end_op( 66 alarm->cq_, alarm, error, 67 [](void* arg, grpc_cq_completion* completion) {}, 68 arg, &alarm->completion_); 69 }, 70 this, grpc_schedule_on_exec_ctx); 71 grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), 72 &on_alarm_); 73 } Set(gpr_timespec deadline,std::function<void (bool)> f)74 void Set(gpr_timespec deadline, std::function<void(bool)> f) { 75 grpc_core::ExecCtx exec_ctx; 76 // Don't use any CQ at all. Instead just use the timer to fire the function 77 callback_ = std::move(f); 78 Ref(); 79 GRPC_CLOSURE_INIT(&on_alarm_, 80 [](void* arg, grpc_error* error) { 81 AlarmImpl* alarm = static_cast<AlarmImpl*>(arg); 82 alarm->callback_(error == GRPC_ERROR_NONE); 83 alarm->Unref(); 84 }, 85 this, grpc_schedule_on_exec_ctx); 86 grpc_timer_init(&timer_, grpc_timespec_to_millis_round_up(deadline), 87 &on_alarm_); 88 } Cancel()89 void Cancel() { 90 grpc_core::ExecCtx exec_ctx; 91 grpc_timer_cancel(&timer_); 92 } Destroy()93 void Destroy() { 94 Cancel(); 95 Unref(); 96 } 97 98 private: Ref()99 void Ref() { gpr_ref(&refs_); } Unref()100 void Unref() { 101 if (gpr_unref(&refs_)) { 102 delete this; 103 } 104 } 105 106 grpc_timer timer_; 107 gpr_refcount refs_; 108 grpc_closure on_alarm_; 109 grpc_cq_completion completion_; 110 // completion queue where events about this alarm will be posted 111 grpc_completion_queue* cq_; 112 void* tag_; 113 std::function<void(bool)> callback_; 114 }; 115 } // namespace internal 116 117 static internal::GrpcLibraryInitializer g_gli_initializer; 118 Alarm()119 Alarm::Alarm() : alarm_(new internal::AlarmImpl()) { 120 g_gli_initializer.summon(); 121 } 122 SetInternal(CompletionQueue * cq,gpr_timespec deadline,void * tag)123 void Alarm::SetInternal(CompletionQueue* cq, gpr_timespec deadline, void* tag) { 124 // Note that we know that alarm_ is actually an internal::AlarmImpl 125 // but we declared it as the base pointer to avoid a forward declaration 126 // or exposing core data structures in the C++ public headers. 127 // Thus it is safe to use a static_cast to the subclass here, and the 128 // C++ style guide allows us to do so in this case 129 static_cast<internal::AlarmImpl*>(alarm_)->Set(cq, deadline, tag); 130 } 131 SetInternal(gpr_timespec deadline,std::function<void (bool)> f)132 void Alarm::SetInternal(gpr_timespec deadline, std::function<void(bool)> f) { 133 // Note that we know that alarm_ is actually an internal::AlarmImpl 134 // but we declared it as the base pointer to avoid a forward declaration 135 // or exposing core data structures in the C++ public headers. 136 // Thus it is safe to use a static_cast to the subclass here, and the 137 // C++ style guide allows us to do so in this case 138 static_cast<internal::AlarmImpl*>(alarm_)->Set(deadline, std::move(f)); 139 } 140 ~Alarm()141 Alarm::~Alarm() { 142 if (alarm_ != nullptr) { 143 static_cast<internal::AlarmImpl*>(alarm_)->Destroy(); 144 } 145 } 146 Cancel()147 void Alarm::Cancel() { static_cast<internal::AlarmImpl*>(alarm_)->Cancel(); } 148 } // namespace grpc 149