summaryrefslogtreecommitdiff
path: root/src/components/application_manager/src/request_controller_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/application_manager/src/request_controller_impl.cc')
-rw-r--r--src/components/application_manager/src/request_controller_impl.cc707
1 files changed, 707 insertions, 0 deletions
diff --git a/src/components/application_manager/src/request_controller_impl.cc b/src/components/application_manager/src/request_controller_impl.cc
new file mode 100644
index 0000000000..76d4a70330
--- /dev/null
+++ b/src/components/application_manager/src/request_controller_impl.cc
@@ -0,0 +1,707 @@
+/*
+ * Copyright (c) 2016, Ford Motor Company
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Ford Motor Company nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "application_manager/request_controller.h"
+
+#include "application_manager/commands/command_request_impl.h"
+#include "application_manager/commands/request_from_mobile_impl.h"
+#include "application_manager/commands/request_to_hmi.h"
+#include "application_manager/request_controller_impl.h"
+#include "utils/logger.h"
+#include "utils/timer_task_impl.h"
+
+namespace application_manager {
+
+namespace request_controller {
+
+using namespace sync_primitives;
+
+SDL_CREATE_LOG_VARIABLE("RequestController")
+
+RequestControllerImpl::RequestControllerImpl(
+ const RequestControlerSettings& settings,
+ RequestTimeoutHandler& request_timeout_handler,
+ event_engine::EventDispatcher& event_dispatcher)
+ : threads::AsyncRunner("RequestController async runner")
+ , pool_state_(TPoolState::UNDEFINED)
+ , pool_size_(settings.thread_pool_size())
+ , request_tracker_(settings)
+ , duplicate_message_count_()
+ , timer_("AM RequestCtrlTimer",
+ new timer::TimerTaskImpl<RequestControllerImpl>(
+ this, &RequestControllerImpl::TimeoutThread))
+ , timer_stop_flag_(false)
+ , is_low_voltage_(false)
+ , settings_(settings)
+ , request_timeout_handler_(request_timeout_handler)
+ , event_dispatcher_(event_dispatcher) {
+ SDL_LOG_AUTO_TRACE();
+ InitializeThreadpool();
+ timer_.Start(0, timer::kSingleShot);
+}
+
+RequestControllerImpl::~RequestControllerImpl() {
+ SDL_LOG_AUTO_TRACE();
+ Stop();
+}
+
+void RequestControllerImpl::Stop() {
+ SDL_LOG_AUTO_TRACE();
+
+ {
+ sync_primitives::AutoLock auto_lock(timer_lock);
+ timer_stop_flag_ = true;
+ timer_condition_.Broadcast();
+ }
+
+ if (pool_state_ != TPoolState::STOPPED) {
+ DestroyThreadpool();
+ }
+
+ SDL_LOG_DEBUG("Stopping async runner");
+ AsyncRunner::Stop();
+
+ SDL_LOG_DEBUG("Stopping timeout tracker");
+ timer_.Stop();
+}
+
+void RequestControllerImpl::InitializeThreadpool() {
+ SDL_LOG_AUTO_TRACE();
+
+ // TODO(DK): Consider lazy loading threads instead of creating all at once
+ pool_state_ = TPoolState::STARTED;
+ char name[50];
+ for (uint32_t i = 0; i < pool_size_; ++i) {
+ snprintf(name, sizeof(name) / sizeof(name[0]), "AM Pool %u", i);
+ pool_.push_back(threads::CreateThread(name, new Worker(this)));
+ pool_[i]->Start();
+ SDL_LOG_DEBUG("Request thread initialized: " << name);
+ }
+}
+
+void RequestControllerImpl::DestroyThreadpool() {
+ SDL_LOG_AUTO_TRACE();
+ {
+ AutoLock auto_lock(mobile_request_list_lock_);
+ pool_state_ = TPoolState::STOPPED;
+ SDL_LOG_DEBUG("Broadcasting STOP signal to all threads...");
+ cond_var_.Broadcast(); // notify all threads we are shutting down
+ }
+ for (size_t i = 0; i < pool_.size(); ++i) {
+ threads::Thread* thread = pool_[i];
+ thread->Stop(threads::Thread::kThreadSoftStop);
+ delete thread->GetDelegate();
+ threads::DeleteThread(thread);
+ }
+ pool_.clear();
+}
+
+RequestControllerImpl::TResult RequestControllerImpl::CheckPosibilitytoAdd(
+ const RequestPtr request, const mobile_apis::HMILevel::eType level) {
+ SDL_LOG_AUTO_TRACE();
+ if (!CheckPendingRequestsAmount(settings_.pending_requests_amount())) {
+ SDL_LOG_ERROR("Too many pending request");
+ return RequestController::TResult::TOO_MANY_PENDING_REQUESTS;
+ }
+
+ const TrackResult track_result =
+ request_tracker_.Track(request->connection_key(), level);
+
+ if (TrackResult::kNoneLevelMaxRequestsExceeded == track_result) {
+ SDL_LOG_ERROR("Too many application requests in hmi level NONE");
+ return RequestController::TResult::NONE_HMI_LEVEL_MANY_REQUESTS;
+ }
+
+ if (TrackResult::kMaxRequestsExceeded == track_result) {
+ SDL_LOG_ERROR("Too many application requests");
+ return RequestController::TResult::TOO_MANY_REQUESTS;
+ }
+
+ if (IsLowVoltage()) {
+ SDL_LOG_ERROR("Impossible to add request due to Low Voltage is active");
+ return RequestController::TResult::INVALID_DATA;
+ }
+
+ return TResult::SUCCESS;
+}
+
+bool RequestControllerImpl::CheckPendingRequestsAmount(
+ const uint32_t pending_requests_amount) {
+ SDL_LOG_AUTO_TRACE();
+
+ if (pending_requests_amount > 0) {
+ const size_t pending_requests_size = mobile_request_list_.size();
+ const bool available_to_add =
+ pending_requests_amount > pending_requests_size;
+ if (!available_to_add) {
+ SDL_LOG_WARN("Pending requests count " << pending_requests_size
+ << " exceed application limit "
+ << pending_requests_amount);
+ }
+ return available_to_add;
+ }
+ SDL_LOG_DEBUG("CheckPendingRequestsAmount disabled");
+ return true;
+}
+
+RequestController::TResult RequestControllerImpl::AddMobileRequest(
+ const RequestPtr request, const mobile_apis::HMILevel::eType& hmi_level) {
+ SDL_LOG_AUTO_TRACE();
+ if (!request) {
+ SDL_LOG_ERROR("Null Pointer request");
+ cond_var_.NotifyOne();
+ return TResult::INVALID_DATA;
+ }
+ SDL_LOG_DEBUG("correlation_id : " << request->correlation_id()
+ << "connection_key : "
+ << request->connection_key());
+ RequestController::TResult result = CheckPosibilitytoAdd(request, hmi_level);
+ if (TResult::SUCCESS == result) {
+ AutoLock auto_lock_list(mobile_request_list_lock_);
+ mobile_request_list_.push_back(request);
+ SDL_LOG_DEBUG("Waiting for execution: " << mobile_request_list_.size());
+ // wake up one thread that is waiting for a task to be available
+ }
+ cond_var_.NotifyOne();
+ return result;
+}
+
+RequestController::TResult RequestControllerImpl::AddHMIRequest(
+ const RequestPtr request) {
+ SDL_LOG_AUTO_TRACE();
+
+ if (request.use_count() == 0) {
+ SDL_LOG_ERROR("HMI request pointer is invalid");
+ return RequestController::TResult::INVALID_DATA;
+ }
+ SDL_LOG_DEBUG(" correlation_id : " << request->correlation_id());
+
+ const uint64_t timeout_in_mseconds =
+ static_cast<uint64_t>(request->default_timeout());
+ RequestInfoPtr request_info_ptr =
+ std::make_shared<HMIRequestInfo>(request, timeout_in_mseconds);
+
+ if (0 == timeout_in_mseconds) {
+ SDL_LOG_DEBUG(
+ "Default timeout was set to 0."
+ "RequestController will not track timeout of this request.");
+ }
+
+ if (IsLowVoltage()) {
+ SDL_LOG_ERROR("Impossible to add request due to Low Voltage is active");
+ return RequestController::TResult::INVALID_DATA;
+ }
+
+ waiting_for_response_.Add(request_info_ptr);
+ SDL_LOG_DEBUG("Waiting for response count:" << waiting_for_response_.Size());
+
+ NotifyTimer();
+ return RequestController::TResult::SUCCESS;
+}
+
+void RequestControllerImpl::AddNotification(const RequestPtr ptr) {
+ SDL_LOG_AUTO_TRACE();
+
+ if (IsLowVoltage()) {
+ SDL_LOG_ERROR(
+ "Impossible to add notification due to Low Voltage is active");
+ return;
+ }
+ notification_list_.push_back(ptr);
+}
+
+void RequestControllerImpl::RemoveNotification(
+ const commands::Command* notification) {
+ SDL_LOG_AUTO_TRACE();
+ std::list<RequestPtr>::iterator it = notification_list_.begin();
+ for (; notification_list_.end() != it;) {
+ if (it->get() == notification) {
+ notification_list_.erase(it++);
+ SDL_LOG_DEBUG("Notification removed");
+ return;
+ } else {
+ ++it;
+ }
+ }
+ SDL_LOG_DEBUG("Cannot find notification");
+}
+
+bool RequestControllerImpl::RetainRequestInstance(
+ const uint32_t connection_key, const uint32_t correlation_id) {
+ SDL_LOG_AUTO_TRACE();
+ auto request = waiting_for_response_.Find(connection_key, correlation_id);
+ if (request) {
+ retained_mobile_requests_.insert(request);
+ SDL_LOG_DEBUG("Request (" << connection_key << ", " << correlation_id
+ << ") has been retained");
+ }
+ SDL_LOG_DEBUG(
+ "Total retained requests: " << retained_mobile_requests_.size());
+
+ return static_cast<bool>(request);
+}
+
+bool RequestControllerImpl::RemoveRetainedRequest(
+ const uint32_t connection_key, const uint32_t correlation_id) {
+ SDL_LOG_AUTO_TRACE();
+ for (auto it = retained_mobile_requests_.begin();
+ it != retained_mobile_requests_.end();
+ ++it) {
+ if ((*it)->request()->connection_key() == connection_key &&
+ (*it)->request()->correlation_id() == correlation_id) {
+ SDL_LOG_DEBUG("Removing request (" << connection_key << ", "
+ << correlation_id << ")");
+ retained_mobile_requests_.erase(it);
+
+ SDL_LOG_DEBUG(
+ "Total retained requests: " << retained_mobile_requests_.size());
+ return true;
+ }
+ }
+
+ SDL_LOG_ERROR("Can't find request (" << connection_key << ", "
+ << correlation_id << ")");
+ return false;
+}
+
+bool RequestControllerImpl::IsStillWaitingForResponse(
+ const uint32_t connection_key, const uint32_t correlation_id) const {
+ auto request = waiting_for_response_.Find(connection_key, correlation_id);
+ return static_cast<bool>(request);
+}
+
+void RequestControllerImpl::TerminateRequest(const uint32_t correlation_id,
+ const uint32_t connection_key,
+ const int32_t function_id,
+ bool force_terminate) {
+ SDL_LOG_AUTO_TRACE();
+ SDL_LOG_DEBUG("correlation_id = "
+ << correlation_id << " connection_key = " << connection_key
+ << " function_id = " << function_id
+ << " force_terminate = " << force_terminate);
+
+ {
+ AutoLock auto_lock(duplicate_message_count_lock_);
+ auto dup_it = duplicate_message_count_.find(correlation_id);
+ if (duplicate_message_count_.end() != dup_it) {
+ --duplicate_message_count_[correlation_id];
+ if (0 == duplicate_message_count_[correlation_id]) {
+ duplicate_message_count_.erase(dup_it);
+ }
+ SDL_LOG_DEBUG(
+ "Ignoring termination request due to duplicate correlation "
+ "ID being sent");
+ return;
+ }
+ }
+
+ RequestInfoPtr request =
+ waiting_for_response_.Find(connection_key, correlation_id);
+ if (!request) {
+ SDL_LOG_WARN("Request was not found in waiting_for_response");
+ return;
+ }
+ if (request->request()->function_id() != function_id) {
+ SDL_LOG_ERROR("Request and response function_id's don't match");
+ return;
+ }
+ if (force_terminate || request->request()->AllowedToTerminate()) {
+ event_dispatcher_.remove_observer(
+ static_cast<hmi_apis::FunctionID::eType>(function_id), correlation_id);
+ waiting_for_response_.RemoveRequest(request);
+ if (RequestInfo::HMIRequest == request->request_type()) {
+ request_timeout_handler_.RemoveRequest(request->requestId());
+ }
+
+ } else {
+ SDL_LOG_WARN("Request was not terminated "
+ << "correlation_id = " << correlation_id
+ << " function_id = " << function_id);
+ }
+ NotifyTimer();
+}
+
+void RequestControllerImpl::OnMobileResponse(
+ const uint32_t mobile_correlation_id,
+ const uint32_t connection_key,
+ const int32_t function_id) {
+ SDL_LOG_AUTO_TRACE();
+ TerminateRequest(mobile_correlation_id, connection_key, function_id);
+}
+
+void RequestControllerImpl::OnHMIResponse(const uint32_t correlation_id,
+ const int32_t function_id) {
+ SDL_LOG_AUTO_TRACE();
+ TerminateRequest(correlation_id, RequestInfo::kHmiConnectionKey, function_id);
+}
+
+void RequestControllerImpl::TerminateWaitingForExecutionAppRequests(
+ const uint32_t app_id) {
+ SDL_LOG_AUTO_TRACE();
+ SDL_LOG_DEBUG("app_id: " << app_id << "Waiting for execution"
+ << mobile_request_list_.size());
+ AutoLock auto_lock(mobile_request_list_lock_);
+ std::list<RequestPtr>::iterator request_it = mobile_request_list_.begin();
+ while (mobile_request_list_.end() != request_it) {
+ RequestPtr request = (*request_it);
+ if ((request.use_count() != 0) && (request->connection_key() == app_id)) {
+ mobile_request_list_.erase(request_it++);
+ } else {
+ ++request_it;
+ }
+ }
+ SDL_LOG_DEBUG("Waiting for execution " << mobile_request_list_.size());
+}
+
+void RequestControllerImpl::scheduleRequestsCleanup(
+ const RequestInfoPtrs& requests) {
+ SDL_LOG_AUTO_TRACE();
+ AsyncRun(new RequestCleanerDelegate(requests));
+}
+
+void RequestControllerImpl::TerminateWaitingForResponseAppRequests(
+ const uint32_t app_id) {
+ SDL_LOG_AUTO_TRACE();
+ SDL_LOG_DEBUG("Waiting for response count before cleanup: "
+ << waiting_for_response_.Size());
+
+ RequestInfoPtrs requests_to_terminate;
+
+ auto pending_requests =
+ waiting_for_response_.GetRequestsByConnectionKey(app_id);
+ for (auto it = pending_requests.begin(); it != pending_requests.end(); ++it) {
+ auto request_ptr = (*it)->request();
+ if (!request_ptr->CleanUp()) {
+ SDL_LOG_WARN("Request with corr_id: "
+ << request_ptr->correlation_id()
+ << " can't be terminated right now. Keep in the queue");
+ continue;
+ }
+
+ SDL_LOG_DEBUG(
+ "Removing request with corr_id: " << request_ptr->correlation_id());
+ requests_to_terminate.push_back(*it);
+ waiting_for_response_.RemoveRequest(*it);
+ }
+
+ SDL_LOG_DEBUG("Waiting for response count after cleanup: "
+ << waiting_for_response_.Size());
+
+ // It is necessary to release requests references after some short amount of
+ // time because here might be a possible data races in event dispatcher
+ // between GetNextObserver() and IncrementReferenceCount() when reference to
+ // corresponding request is destroyed by that function right after CleanUp()
+ // This micro delay gives a time to event dispatcher to figure out that
+ // request was finalized and event should not be handled
+ if (!requests_to_terminate.empty()) {
+ SDL_LOG_DEBUG("Scheduling cleanup for " << requests_to_terminate.size()
+ << " requests for app " << app_id);
+ scheduleRequestsCleanup(requests_to_terminate);
+ }
+}
+
+void RequestControllerImpl::TerminateAppRequests(const uint32_t app_id) {
+ SDL_LOG_AUTO_TRACE();
+ SDL_LOG_DEBUG("app_id : " << app_id
+ << "Requests waiting for execution count : "
+ << mobile_request_list_.size()
+ << "Requests waiting for response count : "
+ << waiting_for_response_.Size());
+
+ TerminateWaitingForExecutionAppRequests(app_id);
+ TerminateWaitingForResponseAppRequests(app_id);
+ NotifyTimer();
+}
+
+void RequestControllerImpl::TerminateAllHMIRequests() {
+ SDL_LOG_AUTO_TRACE();
+ TerminateWaitingForResponseAppRequests(RequestInfo::kHmiConnectionKey);
+}
+
+void RequestControllerImpl::TerminateAllMobileRequests() {
+ SDL_LOG_AUTO_TRACE();
+
+ waiting_for_response_.RemoveMobileRequests();
+ SDL_LOG_DEBUG("Mobile Requests waiting for response cleared");
+ AutoLock waiting_execution_auto_lock(mobile_request_list_lock_);
+ mobile_request_list_.clear();
+ SDL_LOG_DEBUG("Mobile Requests waiting for execution cleared");
+ NotifyTimer();
+}
+
+void RequestControllerImpl::UpdateRequestTimeout(const uint32_t app_id,
+ const uint32_t correlation_id,
+ const uint32_t new_timeout) {
+ SDL_LOG_AUTO_TRACE();
+ SDL_LOG_DEBUG("app_id : " << app_id
+ << " mobile_correlation_id : " << correlation_id
+ << " new_timeout : " << new_timeout);
+
+ if (new_timeout == 0) {
+ SDL_LOG_DEBUG(
+ "New_timeout is NULL. RequestCtrl will "
+ "not manage this request any more");
+ }
+
+ RequestInfoPtr request_info =
+ waiting_for_response_.Find(app_id, correlation_id);
+ if (request_info) {
+ if (0 == request_info->timeout_msec()) {
+ SDL_LOG_INFO(
+ "Request with zero timeout is not updating, "
+ "manual control is assumed");
+ return;
+ }
+
+ waiting_for_response_.RemoveRequest(request_info);
+ request_info->updateTimeOut(new_timeout);
+ waiting_for_response_.Add(request_info);
+ NotifyTimer();
+ SDL_LOG_INFO("Timeout updated for "
+ << " app_id: " << app_id << " correlation_id: "
+ << correlation_id << " new_timeout (ms): " << new_timeout);
+ } else {
+ SDL_LOG_ERROR("Can't find request with "
+ << " app_id: " << app_id
+ << " correlation_id: " << correlation_id);
+ }
+}
+
+void RequestControllerImpl::OnLowVoltage() {
+ SDL_LOG_AUTO_TRACE();
+ is_low_voltage_ = true;
+}
+
+void RequestControllerImpl::OnWakeUp() {
+ SDL_LOG_AUTO_TRACE();
+ TerminateAllHMIRequests();
+ TerminateAllMobileRequests();
+ is_low_voltage_ = false;
+ SDL_LOG_DEBUG("Terminate old requests done");
+}
+
+bool RequestControllerImpl::IsLowVoltage() {
+ SDL_LOG_TRACE("result: " << is_low_voltage_);
+ return is_low_voltage_;
+}
+
+void RequestControllerImpl::TimeoutThread() {
+ SDL_LOG_AUTO_TRACE();
+
+ if (TPoolState::STOPPED == pool_state_) {
+ SDL_LOG_DEBUG("Thread pool has been stopped. Skipping timer restart");
+ return;
+ }
+
+ SDL_LOG_DEBUG(
+ "ENTER Waiting fore response count: " << waiting_for_response_.Size());
+ sync_primitives::AutoLock auto_lock(timer_lock);
+ while (!timer_stop_flag_) {
+ RequestInfoPtr probably_expired =
+ waiting_for_response_.FrontWithNotNullTimeout();
+ if (!probably_expired) {
+ timer_condition_.Wait(auto_lock);
+ continue;
+ }
+ if (!probably_expired->isExpired()) {
+ SDL_LOG_DEBUG("Timeout for "
+ << (RequestInfo::HMIRequest ==
+ probably_expired->request_type()
+ ? "HMI"
+ : "Mobile")
+ << " request id: " << probably_expired->requestId()
+ << " connection_key: " << probably_expired->app_id()
+ << " NOT expired");
+ const date_time::TimeDuration current_time = date_time::getCurrentTime();
+ const date_time::TimeDuration end_time = probably_expired->end_time();
+ if (current_time < end_time) {
+ const uint32_t msecs =
+ static_cast<uint32_t>(date_time::getmSecs(end_time - current_time));
+ SDL_LOG_DEBUG("Sleep for " << msecs << " millisecs");
+ timer_condition_.WaitFor(auto_lock, msecs);
+ }
+ continue;
+ }
+ SDL_LOG_INFO("Timeout for "
+ << (RequestInfo::HMIRequest == probably_expired->request_type()
+ ? "HMI"
+ : "Mobile")
+ << " request id: " << probably_expired->requestId()
+ << " connection_key: " << probably_expired->app_id()
+ << " is expired");
+ const uint32_t expired_request_id = probably_expired->requestId();
+ const uint32_t expired_app_id = probably_expired->app_id();
+
+ probably_expired->request()->HandleTimeOut();
+
+ if (RequestInfo::kHmiConnectionKey == probably_expired->app_id()) {
+ SDL_LOG_DEBUG("Erase HMI request: " << probably_expired->requestId());
+ waiting_for_response_.RemoveRequest(probably_expired);
+ if (RequestInfo::HMIRequest == probably_expired->request_type()) {
+ request_timeout_handler_.RemoveRequest(expired_request_id);
+ }
+ }
+ probably_expired = waiting_for_response_.FrontWithNotNullTimeout();
+ if (probably_expired) {
+ if (expired_request_id == probably_expired->requestId() &&
+ expired_app_id == probably_expired->app_id()) {
+ SDL_LOG_DEBUG("Expired request wasn't removed");
+ break;
+ }
+ }
+ }
+ SDL_LOG_DEBUG(
+ "EXIT Waiting for response count : " << waiting_for_response_.Size());
+}
+
+RequestControllerImpl::Worker::Worker(RequestControllerImpl* request_controller)
+ : request_controller_(request_controller), stop_flag_(false) {}
+
+RequestControllerImpl::Worker::~Worker() {}
+
+void RequestControllerImpl::Worker::threadMain() {
+ SDL_LOG_AUTO_TRACE();
+ AutoLock auto_lock(thread_lock_);
+ while (!stop_flag_) {
+ // Try to pick a request
+ AutoLock auto_lock(request_controller_->mobile_request_list_lock_);
+
+ while ((request_controller_->pool_state_ != TPoolState::STOPPED) &&
+ (request_controller_->mobile_request_list_.empty())) {
+ // Wait until there is a task in the queue
+ // Unlock mutex while wait, then lock it back when signaled
+ SDL_LOG_INFO("Unlocking and waiting");
+ request_controller_->cond_var_.Wait(auto_lock);
+ SDL_LOG_INFO("Signaled and locking");
+ }
+
+ // If the thread was shutdown, return from here
+ if (request_controller_->pool_state_ == TPoolState::STOPPED) {
+ break;
+ }
+
+ if (request_controller_->mobile_request_list_.empty()) {
+ SDL_LOG_WARN("Mobile request list is empty");
+ break;
+ }
+
+ RequestPtr request_ptr(request_controller_->mobile_request_list_.front());
+ request_controller_->mobile_request_list_.pop_front();
+
+ bool init_res = request_ptr->Init(); // to setup specific
+ // default timeout
+
+ const uint32_t timeout_in_mseconds = request_ptr->default_timeout();
+ RequestInfoPtr request_info_ptr =
+ std::make_shared<MobileRequestInfo>(request_ptr, timeout_in_mseconds);
+
+ if (!request_controller_->waiting_for_response_.Add(request_info_ptr)) {
+ commands::RequestFromMobileImpl* cmd_request =
+ dynamic_cast<commands::RequestFromMobileImpl*>(request_ptr.get());
+ if (cmd_request != NULL) {
+ uint32_t corr_id = cmd_request->correlation_id();
+ request_controller_->duplicate_message_count_lock_.Acquire();
+ auto dup_it =
+ request_controller_->duplicate_message_count_.find(corr_id);
+ if (request_controller_->duplicate_message_count_.end() == dup_it) {
+ request_controller_->duplicate_message_count_[corr_id] = 0;
+ }
+ request_controller_->duplicate_message_count_[corr_id]++;
+ request_controller_->duplicate_message_count_lock_.Release();
+ cmd_request->SendResponse(
+ false, mobile_apis::Result::INVALID_ID, "Duplicate correlation_id");
+ }
+ continue;
+ }
+ SDL_LOG_DEBUG("timeout_in_mseconds " << timeout_in_mseconds);
+
+ if (0 != timeout_in_mseconds) {
+ request_controller_->NotifyTimer();
+ } else {
+ SDL_LOG_DEBUG(
+ "Default timeout was set to 0. "
+ "RequestController will not track timeout "
+ "of this request.");
+ }
+
+ AutoUnlock unlock(auto_lock);
+
+ // execute
+ if ((false == request_controller_->IsLowVoltage()) &&
+ request_ptr->CheckPermissions() && init_res) {
+ SDL_LOG_DEBUG("Execute MobileRequest corr_id = "
+ << request_info_ptr->requestId()
+ << " with timeout: " << timeout_in_mseconds);
+ request_ptr->Run();
+ }
+ }
+}
+
+void RequestControllerImpl::Worker::exitThreadMain() {
+ stop_flag_ = true;
+ // setup stop flag and whit while threadMain will be finished correctly
+ // FIXME (dchmerev@luxoft.com): There is no waiting
+}
+
+RequestControllerImpl::RequestCleanerDelegate::RequestCleanerDelegate(
+ const RequestInfoPtrs& requests)
+ : requests_(requests) {}
+
+RequestControllerImpl::RequestCleanerDelegate::~RequestCleanerDelegate() {
+ SDL_LOG_AUTO_TRACE();
+}
+
+void RequestControllerImpl::RequestCleanerDelegate::exitThreadMain() {
+ sync_primitives::AutoLock lock(state_lock_);
+ state_cond_.NotifyOne();
+}
+
+void RequestControllerImpl::RequestCleanerDelegate::threadMain() {
+ SDL_LOG_DEBUG("Prepare to cleanup of " << requests_.size() << " requests");
+
+ {
+ SDL_LOG_DEBUG("Waiting...");
+ sync_primitives::AutoLock lock(state_lock_);
+ state_cond_.WaitFor(lock, 50);
+ }
+
+ SDL_LOG_DEBUG("Releasing references");
+ requests_.clear();
+}
+
+void RequestControllerImpl::NotifyTimer() {
+ SDL_LOG_AUTO_TRACE();
+ timer_condition_.NotifyOne();
+}
+
+} // namespace request_controller
+} // namespace application_manager