olp-cpp-sdk  1.21.0
TaskContext.h
1 /*
2  * Copyright (C) 2019-2021 HERE Europe B.V.
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  * SPDX-License-Identifier: Apache-2.0
17  * License-Filename: LICENSE
18  */
19 
20 #pragma once
21 
22 #include <atomic>
23 #include <memory>
24 #include <mutex>
25 #include <utility>
26 
27 #include <olp/core/client/ApiError.h>
28 #include <olp/core/client/CancellationContext.h>
29 #include <olp/core/client/CancellationToken.h>
30 #include <olp/core/client/Condition.h>
31 
32 namespace olp {
33 namespace client {
34 
42 class CORE_API TaskContext {
43  public:
56  template <typename Exec, typename Callback>
58  Exec execute_func, Callback callback,
60  TaskContext task;
61  task.SetExecutors(std::move(execute_func), std::move(callback),
62  std::move(context));
63  return task;
64  }
65 
70  void Execute() const { impl_->Execute(); }
71 
81  std::chrono::milliseconds timeout = std::chrono::seconds(60)) const {
82  return impl_->BlockingCancel(timeout);
83  }
84 
90  client::CancellationToken CancelToken() const { return impl_->CancelToken(); }
91 
101  bool operator==(const TaskContext& other) const {
102  return impl_ == other.impl_;
103  }
104 
105  protected:
107  friend struct TaskContextHash;
108 
109  TaskContext() = default;
110 
111  template <typename Exec, typename Callback,
112 #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
113  typename ExecResult =
114  std::invoke_result_t<Exec, client::CancellationContext>
115 #else
116  typename ExecResult =
117  typename std::result_of<Exec(client::CancellationContext)>::type
118 #endif
119  >
128  void SetExecutors(Exec execute_func, Callback callback,
129  client::CancellationContext context) {
130  impl_ = std::make_shared<TaskContextImpl<ExecResult>>(
131  std::move(execute_func), std::move(callback), std::move(context));
132  }
133 
139  class Impl {
140  public:
141  virtual ~Impl() = default;
142 
147  virtual void Execute() = 0;
148 
157  virtual bool BlockingCancel(std::chrono::milliseconds timeout) = 0;
158 
165  };
166 
175  template <typename Response>
176  class TaskContextImpl : public Impl {
177  public:
179  using ExecuteFunc = std::function<Response(client::CancellationContext)>;
181  using UserCallback = std::function<void(Response)>;
182 
191  TaskContextImpl(ExecuteFunc execute_func, UserCallback callback,
193  : execute_func_(std::move(execute_func)),
194  callback_(std::move(callback)),
195  context_(std::move(context)),
196  state_{State::PENDING} {}
197 
198  ~TaskContextImpl() override{};
199 
204  void Execute() override {
205  State expected_state = State::PENDING;
206 
207  if (!state_.compare_exchange_strong(expected_state, State::IN_PROGRESS)) {
208  return;
209  }
210 
211  // Moving the user callback and function guarantee that they are
212  // executed exactly once
213  ExecuteFunc function = nullptr;
214  UserCallback callback = nullptr;
215 
216  {
217  std::lock_guard<std::mutex> lock(mutex_);
218  function = std::move(execute_func_);
219  callback = std::move(callback_);
220  }
221 
222  Response user_response =
224 
225  if (function && !context_.IsCancelled()) {
226  auto response = function(context_);
227  // Cancel could occur during the function execution. In that case,
228  // ignore the response.
229  if (!context_.IsCancelled() ||
230  (!response.IsSuccessful() &&
231  response.GetError().GetErrorCode() == ErrorCode::RequestTimeout)) {
232  user_response = std::move(response);
233  }
234  }
235 
236  // Reset the context after the task is finished.
237  context_.ExecuteOrCancelled([]() { return CancellationToken(); });
238 
239  if (callback) {
240  callback(std::move(user_response));
241  }
242 
243  // Resources need to be released before the notification, else lambas
244  // would have captured resources like network or `TaskScheduler`.
245  function = nullptr;
246  callback = nullptr;
247 
248  condition_.Notify();
249  state_.store(State::COMPLETED);
250  }
251 
260  bool BlockingCancel(std::chrono::milliseconds timeout) override {
261  if (state_.load() == State::COMPLETED) {
262  return true;
263  }
264 
265  // Cancels the operation and waits for the notification.
266  if (!context_.IsCancelled()) {
267  context_.CancelOperation();
268  }
269 
270  {
271  std::lock_guard<std::mutex> lock(mutex_);
272  execute_func_ = nullptr;
273  }
274 
275  return condition_.Wait(timeout);
276  }
277 
284  auto context = context_;
286  [context]() mutable { context.CancelOperation(); });
287  }
288 
292  enum class State {
294  PENDING,
296  IN_PROGRESS,
298  COMPLETED
299  };
300 
303  std::mutex mutex_;
313  std::atomic<State> state_;
314  };
315 
317  std::shared_ptr<Impl> impl_;
318 };
319 
323 struct CORE_API TaskContextHash {
331  size_t operator()(const TaskContext& task_context) const {
332  return std::hash<std::shared_ptr<TaskContext::Impl>>()(task_context.impl_);
333  }
334 };
335 
336 } // namespace client
337 } // namespace olp
A wrapper around an internal error or HTTP status code.
Definition: ApiError.h:37
A wrapper that manages the cancellation state of an asynchronous operation in a thread-safe way.
Definition: CancellationContext.h:40
Cancels service requests.
Definition: CancellationToken.h:33
A helper class that allows one thread to call and wait for a notification in the other thread.
Definition: Condition.h:35
An implementation helper interface used to declare the Execute, BlockingCancel, and CancelToken funct...
Definition: TaskContext.h:139
virtual client::CancellationToken CancelToken()=0
Provides a token to cancel the task.
virtual void Execute()=0
Checks for the cancellation, executes the task, and calls the callback with the result or error.
virtual bool BlockingCancel(std::chrono::milliseconds timeout)=0
Cancels the operation and waits for the notification.
Implements the Impl interface.
Definition: TaskContext.h:176
void Execute() override
Checks for the cancellation, executes the task, and calls the callback with the result or error.
Definition: TaskContext.h:204
std::function< void(Response)> UserCallback
Consumes the Response instance.
Definition: TaskContext.h:181
std::mutex mutex_
Definition: TaskContext.h:303
TaskContextImpl(ExecuteFunc execute_func, UserCallback callback, client::CancellationContext context)
Creates the TaskContextImpl instance.
Definition: TaskContext.h:191
State
Indicates the state of the request.
Definition: TaskContext.h:292
bool BlockingCancel(std::chrono::milliseconds timeout) override
Cancels the operation and waits for the notification.
Definition: TaskContext.h:260
client::Condition condition_
The Condition instance.
Definition: TaskContext.h:311
ExecuteFunc execute_func_
The ExecuteFunc instance.
Definition: TaskContext.h:305
client::CancellationContext context_
The CancellationContext instance.
Definition: TaskContext.h:309
std::atomic< State > state_
The State enum of the atomic type.
Definition: TaskContext.h:313
UserCallback callback_
The UserCallback instance.
Definition: TaskContext.h:307
client::CancellationToken CancelToken() override
Provides a token to cancel the task.
Definition: TaskContext.h:283
std::function< Response(client::CancellationContext)> ExecuteFunc
The task that produces the Response instance.
Definition: TaskContext.h:179
Encapsulates the execution of an asynchronous task and invocation of a callback in a guaranteed manne...
Definition: TaskContext.h:42
std::shared_ptr< Impl > impl_
The Impl instance.
Definition: TaskContext.h:317
bool BlockingCancel(std::chrono::milliseconds timeout=std::chrono::seconds(60)) const
Cancels the operation and waits for the notification.
Definition: TaskContext.h:80
void SetExecutors(Exec execute_func, Callback callback, client::CancellationContext context)
Sets the executors for the request.
Definition: TaskContext.h:128
void Execute() const
Checks for the cancellation, executes the task, and calls the callback with the result or error.
Definition: TaskContext.h:70
static TaskContext Create(Exec execute_func, Callback callback, client::CancellationContext context=client::CancellationContext())
Creates the TaskContext instance with the provided task and callback.
Definition: TaskContext.h:57
client::CancellationToken CancelToken() const
Provides a token to cancel the task.
Definition: TaskContext.h:90
bool operator==(const TaskContext &other) const
Checks whether the values of the TaskContext parameter are the same as the values of the other parame...
Definition: TaskContext.h:101
Rules all the other namespaces.
Definition: AppleSignInProperties.h:24
A helper for unordered containers.
Definition: TaskContext.h:323
size_t operator()(const TaskContext &task_context) const
The hash function for the TaskContext instance.
Definition: TaskContext.h:331