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