AlexaClientSDK  3.0.0
A cross-platform, modular SDK for interacting with the Alexa Voice Service
Executor.h
Go to the documentation of this file.
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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  * A copy of the License is located at
7  *
8  * http://aws.amazon.com/apache2.0/
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 #ifndef ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTOR_H_
17 #define ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTOR_H_
18 
19 #include <atomic>
20 #include <condition_variable>
21 #include <chrono>
22 #include <deque>
23 #include <functional>
24 #include <future>
25 #include <memory>
26 #include <mutex>
27 #include <utility>
28 
32 
33 namespace alexaClientSDK {
34 namespace avsCommon {
35 namespace utils {
36 namespace threading {
37 
45 class Executor {
46 public:
50  Executor() noexcept;
51 
59  Executor(const std::chrono::milliseconds& unused) noexcept;
60 
64  ~Executor() noexcept;
65 
74  bool execute(std::function<void()>&& function) noexcept;
75 
84  bool execute(const std::function<void()>& function) noexcept;
85 
100  template <typename Task, typename... Args>
101  auto submit(Task task, Args&&... args) noexcept -> std::future<decltype(task(args...))>;
102 
118  template <typename Task, typename... Args>
119  auto submitToFront(Task task, Args&&... args) noexcept -> std::future<decltype(task(args...))>;
120 
124  void waitForSubmittedTasks() noexcept;
125 
127  void shutdown() noexcept;
128 
130  bool isShutdown() noexcept;
131 
137  operator std::shared_ptr<ExecutorInterface>() const noexcept;
138 
139 private:
140  // Friend declaration.
141  friend class SharedExecutor;
142 
146  enum class QueuePosition {
148  Front = 1,
150  Back
151  };
152 
163  bool execute(std::function<void()>&& function, QueuePosition queuePosition) noexcept;
164 
175  template <typename Task, typename... Args>
176  auto pushTo(QueuePosition queuePosition, Task&& task, Args&&... args) noexcept
177  -> std::future<decltype(task(args...))>;
178 
189  template <typename T>
190  std::future<T> pushFunction(QueuePosition queuePosition, std::function<T()>&& function) noexcept;
191 
193  std::shared_ptr<class SharedExecutor> m_executor;
194 };
195 
196 inline Executor::Executor(const std::chrono::milliseconds&) noexcept : Executor() {
197 }
198 
199 template <typename Task, typename... Args>
200 auto Executor::submit(Task task, Args&&... args) noexcept -> std::future<decltype(task(args...))> {
201  return pushTo(QueuePosition::Back, std::forward<Task>(task), std::forward<Args>(args)...);
202 }
203 
204 template <typename Task, typename... Args>
205 auto Executor::submitToFront(Task task, Args&&... args) noexcept -> std::future<decltype(task(args...))> {
206  return pushTo(QueuePosition::Front, std::forward<Task>(task), std::forward<Args>(args)...);
207 }
208 
215 template <typename T>
216 inline static void forwardPromise(std::promise<T>& promise, std::future<T>& future) noexcept {
217 #if __cpp_exceptions || defined(__EXCEPTIONS)
218  try {
219 #endif
220  promise.set_value(future.get());
221 #if __cpp_exceptions || defined(__EXCEPTIONS)
222  } catch (...) {
223  promise.set_exception(std::current_exception());
224  }
225 #endif
226 }
227 
234 template <>
235 inline void forwardPromise<void>(std::promise<void>& promise, std::future<void>& future) noexcept {
236 #if __cpp_exceptions || defined(__EXCEPTIONS)
237  try {
238 #endif
239  future.get();
240  promise.set_value();
241 #if __cpp_exceptions || defined(__EXCEPTIONS)
242  } catch (...) {
243  promise.set_exception(std::current_exception());
244  }
245 #endif
246 }
247 
248 template <typename Task, typename... Args>
249 inline auto Executor::pushTo(QueuePosition queuePosition, Task&& task, Args&&... args) noexcept
250  -> std::future<decltype(task(args...))> {
251  using ValueType = decltype(task(args...));
252  // Remove arguments from the tasks type by binding the arguments to the task.
253  std::function<ValueType()> fn{std::bind(std::forward<Task>(task), std::forward<Args>(args)...)};
254  return pushFunction(queuePosition, std::move(fn));
255 }
256 
257 template <typename T>
258 std::future<T> Executor::pushFunction(QueuePosition queuePosition, std::function<T()>&& function) noexcept {
259  /*
260  * Create a std::packaged_task with the correct return type.
261  *
262  * Note: A std::packaged_task fulfills its future *during* the call to operator(). If the user of a
263  * std::packaged_task hands it off to another thread to execute, and then waits on the future, they will be able to
264  * retrieve the return value from the task and know that the task has executed, but they do not know exactly when
265  * the task object has been deleted. This distinction can be significant if the packaged task is holding onto
266  * resources that need to be freed (through a std::shared_ptr for example). If the user needs to wait for those
267  * resources to be freed they have no way of knowing how long to wait. The translated_task lambda below is a
268  * workaround for this limitation. It executes the packaged task, then disposes of it before passing the task's
269  * return value back to the future that the user is waiting on.
270  */
271 
276  struct CallCtx {
282  inline CallCtx(std::function<T()>&& function) : packagedTask{std::move(function)} {
283  }
284 
286  std::packaged_task<T()> packagedTask;
288  std::promise<T> cleanupPromise;
289  };
290 
291  auto callCtx = std::make_shared<CallCtx>(std::move(function));
292 
293  // Remove the return type from the task by wrapping it in a lambda with no return value.
294  auto translated_task = [callCtx]() mutable {
295  // Execute the task.
296  callCtx->packagedTask();
297  // Note the future for the task's result.
298  auto taskFuture = callCtx->packagedTask.get_future();
299  // Clean up the task.
300  callCtx->packagedTask.reset();
301  auto cleanupPromise = std::move(callCtx->cleanupPromise);
302  // Release parameters.
303  callCtx.reset();
304  // Forward the task's result to our cleanup promise/future.
305  forwardPromise(cleanupPromise, taskFuture);
306  };
307 
308  // Create a promise/future that we will fulfill when we have cleaned up the task.
309  auto cleanupFuture = callCtx->cleanupPromise.get_future();
310 
311  // Release our local reference to packaged task so that the only remaining reference is inside the lambda.
312  callCtx.reset();
313 
314  if (!execute(std::move(translated_task), queuePosition)) {
315  return std::future<T>();
316  }
317 
318  return cleanupFuture;
319 }
320 
323 extern template std::future<void> Executor::pushFunction(
324  QueuePosition queuePosition,
325  std::function<void()>&& function) noexcept;
326 extern template std::future<bool> Executor::pushFunction(
327  QueuePosition queuePosition,
328  std::function<bool()>&& function) noexcept;
329 extern template std::future<std::string> Executor::pushFunction(
330  QueuePosition queuePosition,
331  std::function<std::string()>&& function) noexcept;
333 
334 } // namespace threading
335 } // namespace utils
336 } // namespace avsCommon
337 } // namespace alexaClientSDK
338 
339 #endif // ALEXA_CLIENT_SDK_AVSCOMMON_UTILS_INCLUDE_AVSCOMMON_UTILS_THREADING_EXECUTOR_H_
def args
Definition: android_test.py:111
bool execute(std::function< void()> &&function) noexcept
Schedules a function for execution.
::std::string string
Definition: gtest-port.h:1097
Single-thread executor implementation.
Definition: Executor.h:45
Definition: CapabilityConfiguration.h:190
auto submit(Task task, Args &&... args) noexcept -> std::future< decltype(task(args...))>
Definition: Executor.h:200
bool isShutdown() noexcept
Returns whether or not the executor is shutdown.
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
Definition: gmock-generated-matchers.h:481
auto submitToFront(Task task, Args &&... args) noexcept -> std::future< decltype(task(args...))>
Definition: Executor.h:205
Whether or not curl logs should be emitted.
Definition: AVSConnectionManager.h:36
friend class SharedExecutor
Definition: Executor.h:141
void forwardPromise< void >(std::promise< void > &promise, std::future< void > &future) noexcept
Definition: Executor.h:235
const
Definition: upload.py:398
static void forwardPromise(std::promise< T > &promise, std::future< T > &future) noexcept
Definition: Executor.h:216
const T & move(const T &t)
Definition: gtest-port.h:1317
void shutdown() noexcept
Clears the executor of outstanding tasks and refuses any additional tasks to be submitted.

AlexaClientSDK 3.0.0 - Copyright 2016-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0