Btk
async.hpp
1 #if !defined(BTK_ASYNC_HPP_)
2 #define BTK_ASYNC_HPP_
3 #include <tuple>
4 #include <memory>
5 #include "../signal/signal.hpp"
6 #include "../defs.hpp"
7 namespace Btk{
8  BTKAPI void DeferCall(void(* fn)(void*),void *data);
9  namespace Impl{
10  template<bool has_signal,class RetT>
11  struct AsyncSignal;
12 
13  template<class RetT>
14  struct AsyncSignal<true,RetT>{
15  using type = Signal<void(RetT &)>;
16  type signal;//< The Signals
17  };
18  template<>
19  struct AsyncSignal<true,void>{
20  using type = Signal<void()>;
21  type signal;
22  };
23  //Empty signal
24  template<class RetT>
25  struct AsyncSignal<false,RetT>{
26  using type = void;
27  };
28 
29  //A struct to store task value
30  template<class RetT>
32  union Data{
33  //erase the destructor
34  RetT data;
35  bool data__unused;
36  Data(){
37  data__unused = false;
38  }
39  ~Data(){}
40  }data;
41  void cleanup(){
42  if constexpr(not std::is_destructible<RetT>()){
43  data.data.~RetT();
44  }
45  };
46  template<class T>
47  void store(T &&t){
48  new (&data.data)T(std::forward<T>(t));
49  };
50  };
51  //A specialization for ref
52  template<class RetT>
53  struct AsyncResultHolder<RetT&>{
54  union Data{
55  //erase the destructor
56  std::remove_reference_t<RetT> data;
57  bool data__unused;
58 
59  Data(){
60  data__unused = false;
61  }
62  ~Data(){}
63  }data;
64  void cleanup(){};
65 
66  template<class T>
67  void store(T &&t){
68  data.data = &t;
69  };
70  };
71  template<>
72  struct AsyncResultHolder<void>{
73  void cleanup(){
74 
75  };
76  };
84  template<bool HasSignal,class Callable,class ...Args>
85  struct AsyncBase:
86  public AsyncSignal<HasSignal,std::invoke_result_t<Callable,Args...>>,
87  AsyncResultHolder<std::invoke_result_t<Callable,Args...>>,
88  std::tuple<Args...>{
89  public:
90  //< The async task has signal
91  static constexpr int has_signal = HasSignal;
97  AsyncBase(Args &&...args):
98  std::tuple<Args...>(std::forward<Args>(args)...){};
99  };
106  template<bool HasSiganlArg,class T>
107  struct AsyncGuard;
108 
109  template<class T>
110  struct AsyncGuard<true,T>{
111  //The invoker
112  T *invoker;
113  bool need_cleanup = true;
114  bool need_delete = true;
115  //< need We cleanup for the invoker
116  AsyncGuard(T *i){
117  invoker = i;
118  }
119  AsyncGuard(const AsyncGuard &) = delete;
120  ~AsyncGuard(){
121  if(need_cleanup){
122  invoker->cleanup();
123  }
124  if(need_delete){
125  delete invoker;
126  }
127  }
132  void release(){
133  need_cleanup = false;
134  need_delete = false;
135  };
136  };
142  template<class T>
143  struct AsyncGuard<false,T>{
144  T *invoker;
145  bool need_delete = true;
146  //< need We cleanup for the invoker
147  AsyncGuard(T *i){
148  invoker = i;
149  }
150  AsyncGuard(const AsyncGuard &) = delete;
151  ~AsyncGuard(){
152  if(need_delete){
153  delete invoker;
154  }
155  }
156  void release(){
157  need_delete = false;
158  };
159  };
160  template<class T>
162  T *invoker;
163  AsyncScopePtr(T *ptr){
164  invoker = ptr;
165  };
166  AsyncScopePtr(const AsyncScopePtr &) = delete;
167  ~AsyncScopePtr(){
168  invoker->cleanup();
169  delete invoker;
170  };
171  };
178  template<bool HasSignal,class Callable,class ...Args>
179  struct AsyncInvoker:public AsyncBase<HasSignal,Callable,Args...>{
185  Callable callable;
186 
187  //Create a invoker
188  AsyncInvoker(Callable &&_callable,Args &&..._args):
189  AsyncBase<HasSignal,Callable,Args...>(std::forward<Args>(_args)...),
190  callable(std::forward<Callable>(_callable)){
191 
192  };
197  using result_type = std::invoke_result_t<Callable,Args...>;
202  static constexpr bool signal_has_args = not std::is_same<result_type,void>();
209  return std::apply(callable,
210  static_cast<std::tuple<Args...>&&>(*this));
211  }
216  void run(){
217  if constexpr(not HasSignal){
218  //It doesnnot have signal ,just Invoke it self
219  std::unique_ptr<AsyncInvoker> ptr(this);
220  invoke();
221  }
222  else if constexpr(std::is_same<result_type,void>()){
224  invoke();
225  if(not this->signal.empty()){
226  //Emit this signal on main thread
227  DeferCall(EmitSignal,this);
228  //relase the guard
229  guard.release();
230  }
231  }
232  else{
234  //< We didnot have the value
235  //< So we set the flags to false
236  guard.need_cleanup = false;
237  this->store(invoke());
238  //< Ok,we store the value
239  guard.need_cleanup = true;
240  if(not this->signal.empty()){
241  //Emit this signal on main thread
242  DeferCall(EmitSignal,this);
243  guard.release();
244  }
245  }
246  };
251  template<class T = std::enable_if<HasSignal>>
252  void emit(){
253  if constexpr(std::is_same<result_type,void>()){
254  std::unique_ptr<AsyncInvoker> ptr(this);
255  this->signal.emit();
256  }
257  else{
258  //Add Guard
259  AsyncScopePtr<AsyncInvoker> guard(this);
260 
261  if constexpr(std::is_reference<result_type>()){
262  //Is ref
263  //So the Holder's value is a point
264  this->signal.emit(*(this->data.data));
265  }
266  else{
267  this->signal.emit(this->data.data);
268  }
269  }
270  };
276  static void Run(void *__self){
277  static_cast<AsyncInvoker*>(__self)->run();
278  };
284  template<class T = std::enable_if<HasSignal>>
285  static void EmitSignal(void *__self){
286  static_cast<AsyncInvoker*>(__self)->emit();
287  };
288  };
295  BTKAPI void RtLauch(void *invoker,void(*run)(void*invoker));
296  BTKAPI void DeferLauch(void *invoker,void(*run)(void*invoker));
297  };
298  enum class Lauch{
299  Async = 0,
300  Defered = 1
301  };
307  struct _NoSignal{
308 
309  };
310  constexpr _NoSignal NoSignal{};
318  template<bool HasSignal,class Callable,class ...Args>
319  class AsyncTask{
320  public:
321  using result_type = typename std::invoke_result_t<Callable,Args...>;
322  using signal_type = typename Impl::AsyncSignal<HasSignal,result_type>::type;
323  using invoker_type = typename Impl::AsyncInvoker<HasSignal,Callable,Args...>;
324  //< The type of invoker
325  AsyncTask(Callable &&callable,Args&& ...args){
326  invoker = new invoker_type(
327  std::forward<Callable>(callable),
328  std::forward<Args>(args)...
329  );
330  };
331  AsyncTask(AsyncTask &&task){
332  invoker = task.invoker;
333  task.invoker = nullptr;
334  };
335  AsyncTask(const AsyncTask &) = delete;
336  ~AsyncTask(){
337  lauch();
338  };
344  void lauch(Lauch lauch = Lauch::Async){
345  if(invoker != nullptr){
346  if(lauch == Lauch::Async){
347  Impl::RtLauch(invoker,invoker_type::Run);
348  }
349  else{
350  Impl::DeferLauch(invoker,invoker_type::Run);
351  }
352  invoker = nullptr;
353  }
354  };
360  signal_type *operator ->() const noexcept{
361  if constexpr(HasSignal){
362  return &(invoker->signal);
363  }
364  else{
365  return nullptr;
366  }
367  };
368  private:
369  invoker_type *invoker;
370  };
380  template<class T,class ...Args>
381  AsyncTask<true,T,Args...> Async(T &&callable,Args ...args){
382  return AsyncTask<true,T,Args...>(
383  std::forward<T>(callable),
384  std::forward<Args>(args)...
385  );
386  };
397  template<class T,class ...Args>
398  AsyncTask<false,T,Args...> Async(_NoSignal,T &&callable,Args ...args){
399  return AsyncTask<false,T,Args...>(
400  std::forward<T>(callable),
401  std::forward<Args>(args)...
402  );
403  };
404  void AsyncInit();
405  void AsyncQuit();
406 };
407 
408 
409 #endif // BTK_ASYNC_HPP_
result_type invoke()
Invoke the callable.
Definition: async.hpp:208
AsyncTask< true, T, Args... > Async(T &&callable, Args ...args)
Create a Async Task.
Definition: async.hpp:381
AsyncBase(Args &&...args)
Construct a new Async Base object.
Definition: async.hpp:97
This header include many useful containers.
Definition: async.hpp:7
void release()
Relase both.
Definition: async.hpp:132
BTKAPI void DeferCall(void(*fn)(void *), void *data)
This function will be called in main EventLoop.
Definition: core.cpp:421
void emit()
Emit the signal impl.
Definition: async.hpp:252
std::invoke_result_t< Callable, Args... > result_type
The result type.
Definition: async.hpp:197
The AsyncInvoker.
Definition: async.hpp:179
A RAII class for impl Invoker.
Definition: async.hpp:107
A type to tell the async don&#39;t emit the signal in main thread.
Definition: async.hpp:307
Definition: async.hpp:32
Callable callable
The signal.
Definition: async.hpp:185
Definition: async.hpp:161
The AsyncTask.
Definition: async.hpp:319
Definition: async.hpp:31
void run()
Run the invoker.
Definition: async.hpp:216
static void Run(void *__self)
The main entry for invoker.
Definition: async.hpp:276
BTKAPI int run()
Enter the EventLoop.
Definition: core.cpp:58
The async base,It store the return value and the signal.
Definition: async.hpp:85
static void EmitSignal(void *__self)
Emit Signal in main thread.
Definition: async.hpp:285
Definition: async.hpp:11
void lauch(Lauch lauch=Lauch::Async)
Lauch the asyn task.
Definition: async.hpp:344