olp-cpp-sdk  1.19.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  typename ExecResult = typename std::result_of<
113  Exec(client::CancellationContext)>::type>
122  void SetExecutors(Exec execute_func, Callback callback,
123  client::CancellationContext context) {
124  impl_ = std::make_shared<TaskContextImpl<ExecResult>>(
125  std::move(execute_func), std::move(callback), std::move(context));
126  }
127 
133  class Impl {
134  public:
135  virtual ~Impl() = default;
136 
141  virtual void Execute() = 0;
142 
151  virtual bool BlockingCancel(std::chrono::milliseconds timeout) = 0;
152 
159  };
160 
169  template <typename Response>
170  class TaskContextImpl : public Impl {
171  public:
173  using ExecuteFunc = std::function<Response(client::CancellationContext)>;
175  using UserCallback = std::function<void(Response)>;
176 
185  TaskContextImpl(ExecuteFunc execute_func, UserCallback callback,
187  : execute_func_(std::move(execute_func)),
188  callback_(std::move(callback)),
189  context_(std::move(context)),
190  state_{State::PENDING} {}
191 
192  ~TaskContextImpl() override{};
193 
198  void Execute() override {
199  State expected_state = State::PENDING;
200 
201  if (!state_.compare_exchange_strong(expected_state, State::IN_PROGRESS)) {
202  return;
203  }
204 
205  // Moving the user callback and function guarantee that they are
206  // executed exactly once
207  ExecuteFunc function = nullptr;
208  UserCallback callback = nullptr;
209 
210  {
211  std::lock_guard<std::mutex> lock(mutex_);
212  function = std::move(execute_func_);
213  callback = std::move(callback_);
214  }
215 
216  Response user_response =
218 
219  if (function && !context_.IsCancelled()) {
220  auto response = function(context_);
221  // Cancel could occur during the function execution. In that case,
222  // ignore the response.
223  if (!context_.IsCancelled() ||
224  (!response.IsSuccessful() &&
225  response.GetError().GetErrorCode() == ErrorCode::RequestTimeout)) {
226  user_response = std::move(response);
227  }
228  }
229 
230  // Reset the context after the task is finished.
231  context_.ExecuteOrCancelled([]() { return CancellationToken(); });
232 
233  if (callback) {
234  callback(std::move(user_response));
235  }
236 
237  // Resources need to be released before the notification, else lambas
238  // would have captured resources like network or `TaskScheduler`.
239  function = nullptr;
240  callback = nullptr;
241 
242  condition_.Notify();
243  state_.store(State::COMPLETED);
244  }
245 
254  bool BlockingCancel(std::chrono::milliseconds timeout) override {
255  if (state_.load() == State::COMPLETED) {
256  return true;
257  }
258 
259  // Cancels the operation and waits for the notification.
260  if (!context_.IsCancelled()) {
261  context_.CancelOperation();
262  }
263 
264  {
265  std::lock_guard<std::mutex> lock(mutex_);
266  execute_func_ = nullptr;
267  }
268 
269  return condition_.Wait(timeout);
270  }
271 
278  auto context = context_;
280  [context]() mutable { context.CancelOperation(); });
281  }
282 
286  enum class State {
288  PENDING,
290  IN_PROGRESS,
292  COMPLETED
293  };
294 
297  std::mutex mutex_;
307  std::atomic<State> state_;
308  };
309 
311  std::shared_ptr<Impl> impl_;
312 };
313 
317 struct CORE_API TaskContextHash {
325  size_t operator()(const TaskContext& task_context) const {
326  return std::hash<std::shared_ptr<TaskContext::Impl>>()(task_context.impl_);
327  }
328 };
329 
330 } // namespace client
331 } // 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:133
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:170
void Execute() override
Checks for the cancellation, executes the task, and calls the callback with the result or error.
Definition: TaskContext.h:198
std::function< void(Response)> UserCallback
Consumes the Response instance.
Definition: TaskContext.h:175
std::mutex mutex_
Definition: TaskContext.h:297
TaskContextImpl(ExecuteFunc execute_func, UserCallback callback, client::CancellationContext context)
Creates the TaskContextImpl instance.
Definition: TaskContext.h:185
State
Indicates the state of the request.
Definition: TaskContext.h:286
bool BlockingCancel(std::chrono::milliseconds timeout) override
Cancels the operation and waits for the notification.
Definition: TaskContext.h:254
client::Condition condition_
The Condition instance.
Definition: TaskContext.h:305
ExecuteFunc execute_func_
The ExecuteFunc instance.
Definition: TaskContext.h:299
client::CancellationContext context_
The CancellationContext instance.
Definition: TaskContext.h:303
std::atomic< State > state_
The State enum of the atomic type.
Definition: TaskContext.h:307
UserCallback callback_
The UserCallback instance.
Definition: TaskContext.h:301
client::CancellationToken CancelToken() override
Provides a token to cancel the task.
Definition: TaskContext.h:277
std::function< Response(client::CancellationContext)> ExecuteFunc
The task that produces the Response instance.
Definition: TaskContext.h:173
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:311
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:122
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:317
size_t operator()(const TaskContext &task_context) const
The hash function for the TaskContext instance.
Definition: TaskContext.h:325