1 #ifndef BMTASK__H__INCLUDED__
2 #define BMTASK__H__INCLUDED__
3 /*
4 Copyright(c) 2020 Anatoliy Kuznetsov(anatoliy_kuznetsov at yahoo.com)
5
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17
18 For more information please visit: http://bitmagic.io
19 */
20
21 /*! \file bmtask.h
22 \brief Task definitions for parallel programming with BitMagic
23
24 The design intent is to make tasks reasonably compatible with
25 different threading and execution models, possibly with different
26 languages and non-C++ runtimes.
27
28 */
29 #include <atomic>
30
31 #include "bmbuffer.h"
32
33 namespace bm
34 {
35
36 /** @defgroup bmtasks Task parallel programming
37 Task parallel programming compatible with different execution models
38 and runtimes
39
40 @ingroup bmagic
41 */
42
43 /** Typedef for a call-back function pointer (pthread conformant signature)
44 @ingroup bmtasks
45 */
46 typedef void* (*task_func_type)(void*);
47
48 /**
49 Descriptor for a task as a callback function, arguments, result.
50
51 Structure contains pointers but being a descriptor it does not own
52 their scope or nature (how it is allocated).
53
54 @ingroup bmtasks
55 */
56 struct task_description
57 {
58 enum task_flags
59 {
60 no_flag = 0, ///< no flag specified
61 barrier_ok = 1u, ///< barrier waits all prev.tasks done without error
62 barrier_any = (1u << 1), ///< barrier waits all prev tasks done (success or not)
63 barrier_ok_delayed = (1u << 2)
64 };
65
66 task_func_type func; ///< pthread-like function callback
67 void* argp; ///< arg pointer
68 void* ret; ///< ret pointer
69
70 void* ctx0; ///< reserved
71 void* ctx1; ///< reserved
72 bm::id64_t param0; ///< reserved
73
74 /// Union to add extra flexible payload to tasks
75 union
76 {
77 int i32;;
78 unsigned u32;
79 unsigned long long u64;
80 float fp32;
81 double fp64;
82 void* void_ptr;
83 } payload0, payload1;
84
85 bm::id64_t flags; ///< task flags to designate barriers
86 unsigned err_code; ///< error code
87 std::atomic_uint done; ///< 0 - pending
88
89 // ----------------------------------------------------
90 // Construction
91 //
task_descriptiontask_description92 task_description() BMNOEXCEPT {}
93
task_descriptiontask_description94 task_description(const task_description& td) BMNOEXCEPT
95 {
96 func = td.func;
97 argp = td.argp;
98 ret = td.ret;
99 ctx0 = td.ctx0;
100 ctx1 = td.ctx1;
101 param0 = td.param0;
102
103 payload0 = td.payload0;
104 payload1 = td.payload1;
105
106 flags = td.flags;
107
108 err_code = td.err_code;
109 done.store(td.done.load()); // atomic operation
110 }
111
112 task_description(task_func_type f, void* argptr = 0) BMNOEXCEPT
113 {
114 init(f, argptr, 0, 0, 0);
115 }
116
inittask_description117 void init(task_func_type f, void* argptr,
118 void* c0, void* c1, bm::id64_t p0) BMNOEXCEPT
119 {
120 func = f; argp = argptr;
121 ret = 0; ctx0 = c0; ctx1 = c1;
122 param0 = p0; flags = 0; err_code = 0;
123 done = 0;
124 payload0.u64 = payload1.u64 = 0;
125 }
126 };
127
128 /**
129 Interface definition (base class) for a group of tasks (batch)
130 @ingroup bmtasks
131 */
132 class task_batch_base
133 {
134 public:
135 typedef unsigned size_type;
136 public:
~task_batch_base()137 virtual ~task_batch_base() {}
138
139 /// Return size of batch
140 virtual size_type size() = 0;
141
142 /// Get task by index in the batch
143 /// @param task_idx - task index in the batch
144 /// @return task description
145 virtual bm::task_description* get_task(size_type task_idx) = 0;
146
147 };
148
149 /**
150 Basic implementation for collection of tasks for parallel execution
151 @ingroup bmtasks
152 */
153 template<typename BVAlloc>
154 class task_batch : public task_batch_base
155 {
156 public:
157 typedef BVAlloc bv_allocator_type;
158 typedef task_batch_base::size_type size_type;
159 typedef
160 bm::heap_vector<bm::task_description, bv_allocator_type, true> task_vector_type;
161
162 public:
163
164 /// task_batch_base intreface implementation
165 //@{
size()166 virtual size_type size() { return (size_type) task_vect_.size(); }
get_task(size_type task_idx)167 virtual bm::task_description* get_task(size_type task_idx)
168 { return &task_vect_.at(task_idx); }
169 //@}
170
171 /// Get access to internal task vector
172 ///
get_task_vector()173 task_vector_type& get_task_vector() BMNOEXCEPT { return task_vect_; }
get_task_vector()174 const task_vector_type& get_task_vector() const BMNOEXCEPT
175 { return task_vect_; }
176
177 protected:
178 task_vector_type task_vect_; ///< list of tasks
179 };
180
181
182 /**
183 Run task batch sequentially
184
185 Function is used for testing and debugging purposes or as a reference
186 to implement custom parallel executors
187
188 @param tasks - collection of tasks to run
189 @ingroup bmtasks
190 */
191 inline
run_task_batch(task_batch_base & tasks)192 void run_task_batch(task_batch_base & tasks)
193 {
194 task_batch_base::size_type batch_size = tasks.size();
195 for (task_batch_base::size_type i = 0; i < batch_size; ++i)
196 {
197 bm::task_description* tdescr = tasks.get_task(i);
198 tdescr->argp = tdescr; // restore the self referenece
199 tdescr->ret = tdescr->func(tdescr->argp);
200 tdescr->done = 1;
201 } // for
202 }
203
204
205 /**
206 "noexcept" traits detection for T::lock()
207 @internal
208 @ingroup bmtasks
209 */
210 template <typename T>
211 struct is_lock_noexcept
212 {
213 #if BM_DONT_WANT_TYPE_TRAITS_HEADER // not used
214 constexpr static bool value = noexcept(((T*)nullptr)->lock());
215 #else
216 constexpr static bool value = noexcept(std::declval<T>().lock());
217 #endif
218 };
219
220 /**
221 Simple scoped lock guard
222 @internal
223 @ingroup bmtasks
224 */
225 template<typename Lock> class lock_guard
226 {
227 public:
lock_guard(Lock & lk)228 lock_guard(Lock& lk) noexcept(bm::is_lock_noexcept<Lock>::value)
229 : lk_(lk) {
230 lk_.lock();
231 }
~lock_guard()232 ~lock_guard() { lk_.unlock(); }
233 private:
234 lock_guard(const lock_guard<Lock>&) = delete;
235 lock_guard<Lock>& operator=(const lock_guard<Lock>&) = delete;
236 private:
237 Lock& lk_;
238 };
239
240
241
242 } // namespace bm
243
244 #endif
245
246