| /** | |
| Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you 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. | |
| */ | |
| /** | |
| * @file AsyncTimer.cc | |
| */ | |
| #include "tscpp/api/AsyncTimer.h" | |
| #include "ts/ts.h" | |
| #include "logging_internal.h" | |
| using namespace atscppapi; | |
| struct atscppapi::AsyncTimerState { | |
| TSCont cont_ = nullptr; | |
| AsyncTimer::Type type_; | |
| int period_in_ms_; | |
| int initial_period_in_ms_; | |
| TSThreadPool thread_pool_; | |
| TSAction initial_timer_action_ = nullptr; | |
| TSAction periodic_timer_action_ = nullptr; | |
| AsyncTimer *timer_ = nullptr; | |
| std::shared_ptr<AsyncDispatchControllerBase> dispatch_controller_{}; | |
| AsyncTimerState(AsyncTimer::Type type, int period_in_ms, int initial_period_in_ms, TSThreadPool thread_pool, AsyncTimer *timer) | |
| : type_(type), | |
| period_in_ms_(period_in_ms), | |
| initial_period_in_ms_(initial_period_in_ms), | |
| thread_pool_(thread_pool), | |
| timer_(timer) | |
| { | |
| } | |
| }; | |
| namespace | |
| { | |
| int | |
| handleTimerEvent(TSCont cont, TSEvent /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */) | |
| { | |
| AsyncTimerState *state = static_cast<AsyncTimerState *>(TSContDataGet(cont)); | |
| if (state->initial_timer_action_) { | |
| LOG_DEBUG("Received initial timer event."); | |
| state->initial_timer_action_ = nullptr; // mark it so that it won't be canceled later on | |
| if (state->type_ == AsyncTimer::TYPE_PERIODIC) { | |
| LOG_DEBUG("Scheduling periodic event now"); | |
| state->periodic_timer_action_ = TSContScheduleEveryOnPool(state->cont_, state->period_in_ms_, state->thread_pool_); | |
| } | |
| } | |
| if (!state->dispatch_controller_->dispatch()) { | |
| LOG_DEBUG("Receiver has died. Destroying timer"); | |
| delete state->timer_; // auto-destruct only in this case | |
| } | |
| return 0; | |
| } | |
| } // namespace | |
| AsyncTimer::AsyncTimer(Type type, int period_in_ms, int initial_period_in_ms, TSThreadPool thread_pool) | |
| { | |
| state_ = new AsyncTimerState(type, period_in_ms, initial_period_in_ms, thread_pool, this); | |
| state_->cont_ = TSContCreate(handleTimerEvent, TSMutexCreate()); | |
| TSContDataSet(state_->cont_, static_cast<void *>(state_)); | |
| } | |
| void | |
| AsyncTimer::run() | |
| { | |
| state_->dispatch_controller_ = getDispatchController(); // keep a copy in state so that cont handler can use it | |
| int one_off_timeout_in_ms = 0; | |
| int regular_timeout_in_ms = 0; | |
| if (state_->type_ == AsyncTimer::TYPE_ONE_OFF) { | |
| one_off_timeout_in_ms = state_->period_in_ms_; | |
| } else { | |
| one_off_timeout_in_ms = state_->initial_period_in_ms_; | |
| regular_timeout_in_ms = state_->period_in_ms_; | |
| } | |
| if (one_off_timeout_in_ms) { | |
| LOG_DEBUG("Scheduling initial/one-off event"); | |
| state_->initial_timer_action_ = TSContScheduleOnPool(state_->cont_, one_off_timeout_in_ms, state_->thread_pool_); | |
| } else if (regular_timeout_in_ms) { | |
| LOG_DEBUG("Scheduling regular timer events"); | |
| state_->periodic_timer_action_ = TSContScheduleEveryOnPool(state_->cont_, regular_timeout_in_ms, state_->thread_pool_); | |
| } | |
| } | |
| void | |
| AsyncTimer::cancel() | |
| { | |
| // Assume this object is locked and the state isn't being updated elsewhere. | |
| // Note that is not the same as the contained continuation being locked. | |
| TSCont contp{state_->cont_}; // save this | |
| if (!contp) { | |
| LOG_DEBUG("Already canceled"); | |
| return; | |
| } | |
| auto mutex{TSContMutexGet(contp)}; | |
| TSMutexLock(mutex); // prevent event dispatch for the continuation during this cancel. | |
| if (state_->initial_timer_action_) { | |
| LOG_DEBUG("Canceling initial timer action"); | |
| TSActionCancel(state_->initial_timer_action_); | |
| } | |
| if (state_->periodic_timer_action_) { | |
| LOG_DEBUG("Canceling periodic timer action"); | |
| TSActionCancel(state_->periodic_timer_action_); | |
| } | |
| state_->cont_ = nullptr; | |
| TSMutexUnlock(mutex); | |
| LOG_DEBUG("Destroying cont"); | |
| TSContDestroy(contp); | |
| } | |
| AsyncTimer::~AsyncTimer() | |
| { | |
| cancel(); | |
| delete state_; | |
| } |