libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_work_closure.hpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2004 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "../../core/intrusive.hpp"
20 #include "../../core/memory_buffer.hpp"
21 #include "../../core/thread_os_traits.hpp"
22 
23 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd { namespace private_ {
24 
25 /// Classes related to implementing work within the thread_pool as a closure.
26 namespace closure {
27 
28  /// The base-class of the closures used in the thread_pool::signalled_work_queue.
29  /**
30  Note that pointers to this class are also used to within the control-flow graph implementation to obtain the vertex() of the node and update the edges into and out of the vertex().
31 
32  \see thread_wk_base, control_flow_graph, no_control_flow_graph
33  */
34  template<
35  class CFG ///< The type of the control-flow graph to be created.
36  >
37  class closure_base {
38  public:
39  typedef CFG cfg_type;
41 
42  /// This function could be overridden in the user's work class to provide an indication of the time it would take to process the transferred closure_base-derived closure. This would only make sense if the pool_aspect was instantiated with a trait of pool_traits::prioritised_queue, and the closure_base-derived closure has the correct operator<() declared for it.
43  /**
44  \todo This is only an idea at the moment, and needs to be implemented with test cases. Obviously we'd need to be able to adjust this to ensure that all work gets eventually processed. It might make sense to provide this as a new pool_trait, that would automatically supply the correct operator<(). Have a look at the theories in "Parallel Algorithms" by H.Casanova.
45  */
46  constexpr int response_time() const noexcept(true) FORCE_INLINE {
47  return 0;
48  }
49 
50  void update_edge(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) FORCE_INLINE {
51  cfg_details.update_edge(e_details);
52  }
53  void delete_edge() noexcept(false) FORCE_INLINE {
54  cfg_details.delete_edge();
55  }
56  void checkpoint_cfg() noexcept(false) FORCE_INLINE {
57  cfg_details.checkpoint_cfg();
58  }
59  typename cfg_type::vertex_t const vertex() const noexcept(true) FORCE_INLINE {
60  return cfg_details.vertex();
61  }
62 
63  protected:
64  explicit closure_base(typename cfg_details_type::params const &p) noexcept(noexcept(cfg_details_type(std::declval<typename cfg_details_type::params>()))) FORCE_INLINE
65  : cfg_details(p) {
66  }
67  /**
68  Not deleted polymorphically, but must be polymorphic to allow input work to be dynamically cast to this type to gain access to the functions for updating the control-flow graph.
69  */
70  virtual ~closure_base() noexcept(true) FORCE_INLINE {}
71 
72  private:
73  cfg_details_type cfg_details;
74  };
75 
76  /// The wrapper for the input work that the user wishes to have mutated.
77  /**
78  The input work to be transferred must be:
79  - copy-constructible or move-constructible,
80  - may have a member typedef of result_type for declaring the result of the mutation and
81  - the mutation function may be specified with either
82  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
83  - a member function "void __fastcall process(result_type &) const noexcept(false)".
84 
85  \see thread_wk_base, control_flow_graph, no_control_flow_graph
86  */
87  template<
88  class InpWk, ///< The type of the input work.
89  bool NoExcept,
90  void (__fastcall InpWk::*Proc)() noexcept(NoExcept), ///< The mutator function, by default InpWk::process().
91  class CFG ///< The type of the control-flow graph to be created.
92  >
93  class closure_void_static final : public InpWk, public closure_base<CFG> {
94  public:
95  typedef closure_base<CFG> base_t;
96  typedef typename base_t::cfg_type cfg_type;
98  typedef void result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
99  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
100 
101  __stdcall closure_void_static(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
102  ~closure_void_static() noexcept(true) FORCE_INLINE {}
103 
104  bool __fastcall operator==(const closure_void_static &) const noexcept(true) FORCE_INLINE;
105  bool __fastcall operator<(const closure_void_static &) const noexcept(true) FORCE_INLINE;
106 
107  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
108  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
109 
110  /// Process the wrapped work.
111  /**
112  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_void_static is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.
113 
114  \todo We really want to compute the noexcept() from the Proc member fn.
115  typedef typename std::remove_pointer<process_ptr_t>::type process_t; - This doesn't seem to remove the pointer.
116  BOOST_MPL_ASSERT((std::is_same<process_t, int>));
117 
118  \see thread_wrapper, thread_pool, thread_pool_type
119  */
120  void __fastcall process() FORCE_INLINE;
121 
122  /**
123  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
124  */
125  friend inline tostream &__fastcall operator<<(tostream &os, closure_void_static const &w) noexcept(false) {
126  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
127 
128  os
129  <<_T("work=0x")<<&w
130  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
131  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
132  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
133 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
134  <<_T(", result_type: void");
135  return os;
136  }
137  };
138 
139  /// The wrapper for the input work that the user wishes to have mutated.
140  /**
141  The input work to be transferred must be:
142  - copy-constructible or move-constructible,
143  - may have a member typedef of result_type for declaring the result of the mutation and
144  - the mutation function may be specified with either
145  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
146  - a member function "void __fastcall process(result_type &) const noexcept(false)".
147 
148  \see thread_wk_base, control_flow_graph, no_control_flow_graph
149  */
150  template<
151  class InpWk, ///< The type of the input work.
152  bool NoExcept,
153  void (__fastcall InpWk::*Proc)() const noexcept(NoExcept), ///< The mutator function, by default InpWk::process().
154  class CFG ///< The type of the control-flow graph to be created.
155  >
156  class closure_void_static_const final : public InpWk, public closure_base<CFG> {
157  public:
158  typedef closure_base<CFG> base_t;
159  typedef typename base_t::cfg_type cfg_type;
161  typedef void result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
162  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
163 
164  __stdcall closure_void_static_const(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
166 
167  bool __fastcall operator==(const closure_void_static_const &) const noexcept(true) FORCE_INLINE;
168  bool __fastcall operator<(const closure_void_static_const &) const noexcept(true) FORCE_INLINE;
169 
170  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
171  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
172 
173  /// Process the wrapped work.
174  /**
175  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_void_static_const is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.
176 
177  \see thread_wrapper, thread_pool, thread_pool_type
178  */
179  void __fastcall process() FORCE_INLINE;
180 
181  /**
182  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
183  */
184  friend inline tostream &__fastcall operator<<(tostream &os, closure_void_static_const const &w) noexcept(false) {
185  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
186 
187  os
188  <<_T("work=0x")<<&w
189  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
190  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
191  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
192 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
193  <<_T(", result_type: void");
194  return os;
195  }
196  };
197 
198  /// The wrapper for the input work that the user wishes to have mutated.
199  /**
200  The input work to be transferred must be:
201  - copy-constructible or move-constructible,
202  - may have a member typedef of result_type for declaring the result of the mutation and
203  - the mutation function may be specified with either
204  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
205  - a member function "void __fastcall process(result_type &) const noexcept(false)".
206 
207  \see thread_wk_base, no_control_flow_graph
208  */
209  template<
210  class InpWk, ///< The type of the input work.
211  class Res, ///< The result type of the mutation.
212  bool NoExcept,
213  void (__fastcall InpWk::*Proc)(Res &) noexcept(NoExcept), ///< The mutator function, by default InpWk::process(Res &).
214  class CFG ///< The type of the control-flow graph to be created.
215  >
216  class closure_static final : public InpWk, public closure_base<CFG> {
217  public:
218  typedef closure_base<CFG> base_t;
219  typedef typename base_t::cfg_type cfg_type;
221  typedef Res result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
222  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
223 
224  explicit __stdcall closure_static(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
225  __stdcall ~closure_static() noexcept(true) FORCE_INLINE {}
226 
227  bool __fastcall operator==(const closure_static &) const noexcept(true) FORCE_INLINE;
228  bool __fastcall operator<(const closure_static &) const noexcept(true) FORCE_INLINE;
229 
230  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
231  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
232 
233  /// Process the wrapped work.
234  /**
235  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_static is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.
236 
237  \see thread_wrapper, thread_pool, thread_pool_type
238  */
239  void __fastcall process() FORCE_INLINE;
240 
241  /// Return a reference to the wrapped work.
242  const result_type & __fastcall get_results() const noexcept(false) FORCE_INLINE;
243  /// Return a reference to the wrapped work.
244  result_type & __fastcall get_results() noexcept(false) FORCE_INLINE;
245 
246  /**
247  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
248  */
249  friend inline tostream &__fastcall operator<<(tostream &os, closure_static const &w) noexcept(false) {
250  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
251 
252  os
253  <<_T("work=0x")<<&w
254  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
255  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
256  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
257 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
258  <<_T(", result_type: ")<<thread_traits::demangle_name(typeid(result_type));
259 // TODO <<_T(", result: ")<<w.result_;
260  return os;
261  }
262 
263  private:
264  result_type result_;
265  };
266 
267  /// The wrapper for the input work that the user wishes to have mutated.
268  /**
269  The input work to be transferred must be:
270  - copy-constructible or move-constructible,
271  - may have a member typedef of result_type for declaring the result of the mutation and
272  - the mutation function may be specified with either
273  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
274  - a member function "void __fastcall process(result_type &) const noexcept(false)".
275 
276  \see thread_wk_base, control_flow_graph
277  */
278  template<
279  class InpWk, ///< The type of the input work.
280  class Res, ///< The result type of the mutation.
281  class CFGP,
282  bool NoExcept,
283  void (__fastcall InpWk::*Proc)(Res &, CFGP const &) noexcept(NoExcept), ///< The mutator function, by default InpWk::process(Res &).
284  class CFG ///< The type of the control-flow graph to be created.
285  >
286  class closure_static_cfg final : public InpWk, public closure_base<CFG> {
287  public:
288  typedef closure_base<CFG> base_t;
289  typedef typename base_t::cfg_type cfg_type;
291  typedef Res result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
292  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
293 
294  explicit __stdcall closure_static_cfg(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
295  __stdcall ~closure_static_cfg() noexcept(true) FORCE_INLINE {}
296 
297  bool __fastcall operator==(const closure_static_cfg &) const noexcept(true) FORCE_INLINE;
298  bool __fastcall operator<(const closure_static_cfg &) const noexcept(true) FORCE_INLINE;
299 
300  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
301  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
302 
303  /// Process the wrapped work.
304  /**
305  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_static is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.
306 
307  \see thread_wrapper, thread_pool, thread_pool_type
308  */
309  void __fastcall process() FORCE_INLINE;
310 
311  /// Return a reference to the wrapped work.
312  const result_type & __fastcall get_results() const noexcept(false) FORCE_INLINE;
313  /// Return a reference to the wrapped work.
314  result_type & __fastcall get_results() noexcept(false) FORCE_INLINE;
315 
316  /**
317  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
318  */
319  friend inline tostream &__fastcall operator<<(tostream &os, closure_static_cfg const &w) noexcept(false) {
320  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
321 
322  os
323  <<_T("work=0x")<<&w
324  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
325  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
326  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
327 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
328  <<_T(", result_type: ")<<thread_traits::demangle_name(typeid(result_type));
329 // TODO <<_T(", result: ")<<w.result_;
330  return os;
331  }
332 
333  private:
334  result_type result_;
335  typename cfg_details_type::params const cfg_params;
336  };
337 
338  /// The wrapper for the input work that the user wishes to have mutated.
339  /**
340  The input work to be transferred must be:
341  - copy-constructible or move-constructible,
342  - may have a member typedef of result_type for declaring the result of the mutation and
343  - the mutation function may be specified with either
344  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
345  - a member function "void __fastcall process(result_type &) const noexcept(false)".
346 
347  \see thread_wk_base, no_control_flow_graph
348  */
349  template<
350  class InpWk, ///< The type of the input work.
351  class Res, ///< The result type of the mutation.
352  bool NoExcept,
353  void (__fastcall InpWk::*Proc)(Res &) const noexcept(NoExcept), ///< The mutator function, by default InpWk::process(Res &).
354  class CFG ///< The type of the control-flow graph to be created.
355  >
356  class closure_static_const final : public InpWk, public closure_base<CFG> {
357  public:
358  typedef closure_base<CFG> base_t;
359  typedef typename base_t::cfg_type cfg_type;
361  typedef Res result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
362  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
363 
364  explicit __stdcall closure_static_const(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
365  __stdcall ~closure_static_const() noexcept(true) FORCE_INLINE {}
366 
367  bool __fastcall operator==(const closure_static_const &) const noexcept(true) FORCE_INLINE;
368  bool __fastcall operator<(const closure_static_const &) const noexcept(true) FORCE_INLINE;
369 
370  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
371  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
372 
373  /// Process the wrapped work.
374  /**
375  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_static_const is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.to allow a thread_pool with un-emitted exceptions to execute it's destructor.
376 
377  \see thread_wrapper, thread_pool, thread_pool_type
378  */
379  void __fastcall process() FORCE_INLINE;
380 
381  /// Return a reference to the wrapped work.
382  const result_type & __fastcall get_results() const noexcept(true) FORCE_INLINE;
383  /// Return a reference to the wrapped work.
384  result_type & __fastcall get_results() noexcept(false) FORCE_INLINE;
385 
386  /**
387  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
388  */
389  friend inline tostream &__fastcall operator<<(tostream &os, closure_static_const const &w) noexcept(false) {
390  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
391 
392  os
393  <<_T("work=0x")<<&w
394  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
395  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
396  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
397 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
398  <<_T(", result_type: ")<<thread_traits::demangle_name(typeid(result_type));
399 // TODO <<_T(", result: ")<<w.result_;
400  return os;
401  }
402 
403  private:
404  result_type result_;
405  };
406 
407  /// The wrapper for the input work that the user wishes to have mutated.
408  /**
409  The input work to be transferred must be:
410  - copy-constructible or move-constructible,
411  - may have a member typedef of result_type for declaring the result of the mutation and
412  - the mutation function may be specified with either
413  - a member function "void __fastcall process(result_type &) noexcept(false)" or,
414  - a member function "void __fastcall process(result_type &) const noexcept(false)".
415 
416  \see thread_wk_base, control_flow_graph
417  */
418  template<
419  class InpWk, ///< The type of the input work.
420  class Res, ///< The result type of the mutation.
421  class CFGP,
422  bool NoExcept,
423  void (__fastcall InpWk::*Proc)(Res &, CFGP const &) const noexcept(NoExcept), ///< The mutator function, by default InpWk::process(Res &).
424  class CFG ///< The type of the control-flow graph to be created.
425  >
426  class closure_static_cfg_const final : public InpWk, public closure_base<CFG> {
427  public:
428  typedef closure_base<CFG> base_t;
429  typedef typename base_t::cfg_type cfg_type;
431  typedef Res result_type; ///< The result-type of the mutation, to assist in making the derived types look like a std::unary_function.
432  typedef InpWk argument_type; ///< The argument-type of the mutation, to assist in making the derived types look like a std::unary_function.
433 
434  explicit __stdcall closure_static_cfg_const(argument_type &&i, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE;
435  __stdcall ~closure_static_cfg_const() noexcept(true) FORCE_INLINE {}
436 
437  bool __fastcall operator==(const closure_static_cfg_const &) const noexcept(true) FORCE_INLINE;
438  bool __fastcall operator<(const closure_static_cfg_const &) const noexcept(true) FORCE_INLINE;
439 
440  argument_type const & __fastcall input() const noexcept(true) FORCE_INLINE;
441  argument_type & __fastcall input() noexcept(true) FORCE_INLINE;
442 
443  /// Process the wrapped work.
444  /**
445  By default, all exceptions derived from std::exception are caught and wrapped, to be potentially transferred to the client. Also a "catch-all" block traps any other unknown exceptions, that are also potentially transferred. It is a grave programmatic error to allow a thread_pool with un-emitted exceptions to execute it's destructor. To avoid this potential for undefined behaviour, if process()ing the closure_static_const is likely to result in an exception being thrown, the user should capture the result of the transfer in an execution_context and dereference it, when the exception will be safely re-thrown, and the user will have an opportunity to correctly catch it and deal with it.to allow a thread_pool with un-emitted exceptions to execute it's destructor.
446 
447  \see thread_wrapper, thread_pool, thread_pool_type
448  */
449  void __fastcall process() FORCE_INLINE;
450 
451  /// Return a reference to the wrapped work.
452  const result_type & __fastcall get_results() const noexcept(true) FORCE_INLINE;
453  /// Return a reference to the wrapped work.
454  result_type & __fastcall get_results() noexcept(false) FORCE_INLINE;
455 
456  /**
457  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
458  */
459  friend inline tostream &__fastcall operator<<(tostream &os, closure_static_cfg_const const &w) noexcept(false) {
460  using thread_traits=api_threading_traits<ppd::platform_api, ppd::sequential_mode>;
461 
462  os
463  <<_T("work=0x")<<&w
464  <<_T(", type: ")<<thread_traits::demangle_name(typeid(w))
465  <<_T(", mutator: ")<<thread_traits::demangle_name(typeid(Proc))
466  <<_T(", argument_type: ")<<thread_traits::demangle_name(typeid(argument_type))
467 // TODO <<_T(", input: ")<<static_cast<argument_type const &>(w)
468  <<_T(", result_type: ")<<thread_traits::demangle_name(typeid(result_type));
469 // TODO <<_T(", result: ")<<w.result_;
470  return os;
471  }
472 
473  private:
474  result_type result_;
475  typename cfg_details_type::params const cfg_params;
476  };
477 
478  /// These two "thread" classes provide the ability for the user to obtain results from that work, or not, as the case may be.
479  /**
480  These are used by the pool_thread class (which are a specialisation of the type of thread_wrapper used by the thread_pool). This is also why these classes are not local to either the thread_pool_type or pool_thread classes, as you might think. (Because they are used by both.)
481  This needs to be thread-safe because copies of the core_work are held by both the pool_thread and the thread that holds the execution_context.
482  The default ordering of core_work in the pool_aspect::work_queue_type is based upon the weak order of the work to be mutated.
483 
484  We don't actually need this to be guaranteed lockfree, as locking is done elsewhere, so we can gain a smidge of performance by using raw pointers.
485  */
486  template<
487  typename OST,
488  class CFG
489  >
491  typename OST::lock_traits
492  > {
493  typedef closure_base<CFG> input_t;
494  public:
495  typedef intrusive::node_details_itf<
496  typename OST::lock_traits
498  typedef OST os_traits;
499  typedef typename os_traits::lock_traits lock_traits;
500  typedef typename input_t::cfg_type cfg_type;
501  typedef typename input_t::cfg_details_type cfg_details_type;
502  typedef lock::lockable_settable<lock_traits> work_complete_t; ///< This atomic object is the object that is used to signal to a waiting future that the work has been completed.
503 
505  virtual ~thread_wk_base() noexcept(true) FORCE_INLINE {}
506 
507  /// The default ordering of core_work in the pool_aspect::work_queue_type is based upon the weak order of the work to be mutated.
508  virtual bool __fastcall operator<(thread_wk_base const &) const noexcept(true)=0;
509  bool __fastcall operator==(thread_wk_base const &ctw) const noexcept(true) FORCE_INLINE {
510  return !(*this<ctw) && !(ctw<*this);
511  }
512 
513  virtual generic_traits::return_data __fastcall result_traits() const noexcept(true)=0;
514  virtual void update_edge(typename cfg_type::node_property_t::value_type const e_details) noexcept(false)=0;
515  virtual void __fastcall process_joinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false)=0;
516  virtual void __fastcall process_nonjoinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false)=0;
517  virtual work_complete_t *__fastcall work_complete() noexcept(true) FORCE_INLINE {return nullptr;}
518 
519  virtual tstring __fastcall to_string() const noexcept(false) override=0;
520 
521  /**
522  \todo Implement using the advice given in "Standard C++ IOStreams and Locales" by A.Langer & K.Kreft, page 170.
523  */
524  friend tostream & __fastcall FORCE_INLINE
525  operator<<(tostream &os, thread_wk_base const &wk) noexcept(false) {
526  os<<wk.to_string();
527  return os;
528  }
529 
530  protected:
531  constexpr thread_wk_base() noexcept(true) FORCE_INLINE
532  : base_t() {}
533  };
534  template<
536  typename OST,
537  template<class> class Del,
538  template<class> class AtCtr,
539  class CFG
540  >
541  class thread_wk_async_t;
542  /**
543  nonjoinable types have to be allocated on the heap (or a custom allocator), because there is no execution_context available to hold these objects.
544  */
545  template<typename OST, template<class> class Del, template<class> class AtCtr, class CFG>
547  public:
549  typedef typename base_t::os_traits os_traits;
550  typedef typename base_t::lock_traits lock_traits;
553  typedef typename counter_type::deleter_t deleter_t;
554  typedef typename lock_traits::template noop_atomic_ctr<typename atomic_ctr_t::value_type> no_ref_counting;
557 
558  /**
559  To assist in allowing compile-time computation of the algorithmic order of the threading model.
560  */
562 
563  static_assert(!std::is_same<atomic_ctr_t, no_ref_counting>::value || std::is_same<deleter_t, no_deletion>::value, "If not refcounted, then the object must not have a defined deleter, as it should be stack-based.");
564 
565  constexpr generic_traits::return_data __fastcall result_traits() const noexcept(true) override FORCE_INLINE {
567  }
568 
569  tstring __fastcall to_string() const noexcept(false) override FORCE_INLINE {
570  return _T("result_traits=nonjoinable");
571  }
572 
573  protected:
574  ~thread_wk_async_t() noexcept(true) FORCE_INLINE {}
575  };
576  /**
577  \todo joinable types do not have to be allocated on the heap, because there is an execution_context available to hold these objects.
578  */
579  template<typename OST, template<class> class Del, template<class> class AtCtr, class CFG>
581  public:
583  typedef typename base_t::os_traits os_traits;
584  typedef typename base_t::lock_traits lock_traits;
588  typedef typename counter_type::deleter_t deleter_t;
589  typedef typename lock_traits::template noop_atomic_ctr<typename atomic_ctr_t::value_type> no_ref_counting;
592 
593  /**
594  To assist in allowing compile-time computation of the algorithmic order of the threading model.
595  */
601  );
602 
603  static_assert(!std::is_same<atomic_ctr_t, no_ref_counting>::value || std::is_same<deleter_t, no_deletion>::value, "If not refcounted, then the object must not have a defined deleter, as it should be stack-based.");
604 
605  constexpr generic_traits::return_data __fastcall result_traits() const noexcept(true) override FORCE_INLINE {
607  }
608  virtual typename os_traits::thread_exception const &__fastcall exception_thrown_in_thread() const noexcept(true)=0;
609  virtual void throw_any_exception() const noexcept(false)=0;
610 
611  tstring __fastcall to_string() const noexcept(false) override FORCE_INLINE {
612  return _T("result_traits=joinable");
613  }
614 
615  protected:
616  ~thread_wk_async_t() noexcept(true) FORCE_INLINE {}
617  };
618 
619  template<generic_traits::return_data RD, typename OST, class ThrW, class WFlg, template<class> class Del, template<class> class AtCtr, class CFG>
620  class thread_wk;
621 
622  /// With this class, the user cannot join with the transferred closure_base-derived closure.
623  template<typename OST, class ThrW, class WFlg, template<class> class Del, template<class> class AtCtr, class CFG>
625  public:
627  typedef typename base_t::os_traits os_traits;
628  typedef typename base_t::lock_traits lock_traits;
629  typedef typename base_t::cfg_type cfg_type;
631  typedef WFlg work_complete_t; ///< This atomic object is the object that is used to signal to a waiting future that the work has been completed.
632  /**
633  We don't directly contain the argument_type in closure_t because we need to be able to perform operator<() on potentially different objects for supporting pool_traits::prioritised_queue, so we need a hierarchy to allow this.
634  */
635  typedef ThrW closure_t; ///< This is the binding of the input data to the mutator, with a representation of the output type. i.e. a closure.
636 
637  /**
638  Note the value transfer semantics....
639  */
641  : base_t(), closure_wk_(std::forward<typename closure_t::argument_type>(tw), p) {
642  }
643 
644  bool operator<(typename base_t::base_t const &cw) const noexcept(true) override FORCE_INLINE {
645  return dynamic_cast<thread_wk const *>(&cw) ? static_cast<thread_wk const &>(cw)<*this : false;
646  }
647  bool __fastcall operator<(thread_wk const &ctw) const noexcept(true) FORCE_INLINE {
648  return closure_wk_<ctw.closure_wk_;
649  }
650 
651  closure_t &__fastcall closure() noexcept(true) FORCE_INLINE {
652  return closure_wk_;
653  }
654  closure_t const &__fastcall closure() const noexcept(true) FORCE_INLINE {
655  return closure_wk_;
656  }
659  }
660  void __fastcall process_joinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
661 // TODO handle the exception on the thread, as no execution_context, but what if threads already contains an exception...?
664  }
665  void __fastcall process_nonjoinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
668  }
669 
670  tstring __fastcall to_string() const noexcept(false) override {
672  os
673  <<base_t::to_string()
674  <<_T(", enqueued work=0x")<<this
675  <<_T(", enqueued type: ")<<os_traits::thread_traits::demangle_name(typeid(*this))
676  <<_T(", work details: ")<<closure_wk_;
677  return os.str();
678  }
679 
680  protected:
681  ~thread_wk() noexcept(true) FORCE_INLINE {}
682 
683  private:
685  };
686 
687  /// With this class, the user can explicitly join with the transferred closure_base-derived closure, but only via the execution_context, created via the appropriate thread_pool derived from thread_pool_type.
688  template<typename OST, class ThrW, class WFlg, template<class> class Del, template<class> class AtCtr, class CFG>
690  public:
692  typedef typename base_t::os_traits os_traits;
693  typedef typename base_t::lock_traits lock_traits;
694  typedef typename base_t::cfg_type cfg_type;
696  typedef WFlg work_complete_t; ///< This atomic object is the object that is used to signal to a waiting future that the work has been completed.
697  /**
698  We don't directly contain the argument_type in closure_t because we need to be able to perform operator<() on potentially different objects for supporting pool_traits::prioritised_queue, so we need a hierarchy to allow this.
699  */
700  typedef ThrW closure_t; ///< This is the binding of the input data to the mutator, with a representation of the output type. i.e. a closure.
701 
704  }
705 
706  bool operator<(typename base_t::base_t const &cw) const noexcept(true) override FORCE_INLINE {
707  return dynamic_cast<thread_wk const *>(&cw) ? static_cast<thread_wk const &>(cw)<*this : false;
708  }
709  bool __fastcall operator<(thread_wk const &ctw) const noexcept(true) FORCE_INLINE {
710  return closure_wk_<ctw.closure_wk_;
711  }
712 
713  closure_t &__fastcall closure() noexcept(true) FORCE_INLINE {
714  return closure_wk_;
715  }
716  closure_t const &__fastcall closure() const noexcept(true) FORCE_INLINE {
717  return closure_wk_;
718  }
719 
720  typename os_traits::thread_exception const &__fastcall exception_thrown_in_thread() const noexcept(true) override FORCE_INLINE {
722  }
723  void throw_any_exception() const noexcept(false) override FORCE_INLINE {
725  }
726  work_complete_t *__fastcall work_complete() noexcept(true) final override FORCE_INLINE {
727  return &work_complete_;
728  }
731  }
732  void __fastcall process_joinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
733  /* Signal (& release) anyone waiting for the work to be completed.
734  It is really important that this gets done, despite any exceptions that may be thrown, this is to release any thread(s) waiting on the result, in case of exceptions being thrown. Any of these exceptions that are uncaught by the user, will be eventually caught in the first registered. Then in the get_results() function, that exception will be re-thrown, to pass it to the user. Hence the need to ensure that the "work_complete" signal is set in all cases, but allow exceptions to propagate transparently (as I will handle them later, in base-class code).
735  */
736  try {
738 #if defined(__GNUC__) && !defined(__clang__)
739  } catch (abi::__forced_unwind &) {
740  throw;
741 #endif
742  } catch (...) {
744  }
746  }
747  void __fastcall process_nonjoinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
750  }
751 
752  tstring __fastcall to_string() const noexcept(false) override {
754  os
755  <<base_t::to_string()
756  <<_T(", enqueued work=0x")<<this
757  <<_T(", enqueued type: ")<<os_traits::thread_traits::demangle_name(typeid(*this))
758  <<_T(", work complete event=0x")<<&work_complete_
759  <<_T(", work details: ")<<closure_wk_
760  <<_T(", exception details: ")<<exception_thrown_in_thread_;
761  return os.str();
762  }
763 
764  protected:
765  ~thread_wk() noexcept(true) FORCE_INLINE {}
766 
767  private:
771  };
772 
773  /// With this class, the user can explicity join with the transferred closure_base-derived closure returned from a parallel algorithm, but only via the execution_context, created via the appropriate thread_pool derived from thread_pool_type.
774  template<typename OST, class ThrW, class WFlg, template<class> class Del, template<class> class AtCtr, class CFG>
776  public:
778  typedef typename base_t::os_traits os_traits;
779  typedef typename base_t::lock_traits lock_traits;
780  typedef typename base_t::cfg_type cfg_type;
782  /// This atomic object is the object that is used to signal to a waiting future that the work has been completed.
783  typedef WFlg work_complete_t;
784  /// This is the binding of the input data to the mutator, with a representation of the output type. i.e. a closure.
785  /**
786  We don't directly contain the argument_type in closure_t because we need to be able to perform operator<() on potentially different objects for supporting pool_traits::prioritised_queue, so we need a hierarchy to allow this.
787  */
788  typedef ThrW closure_t;
789 
790  __stdcall algo_thread_wk(work_complete_t &w, typename closure_t::argument_type &&tw, typename cfg_details_type::params const &p) FORCE_INLINE
791  : closure_wk_(std::forward<typename closure_t::argument_type>(tw), p), work_complete_(w) {
792  work_complete_.lock_containers();
793  }
794  ~algo_thread_wk() noexcept(true) FORCE_INLINE {}
795 
796  bool operator<(typename base_t::base_t const &cw) const noexcept(true) override FORCE_INLINE {
797  return dynamic_cast<algo_thread_wk const *>(&cw) ? static_cast<algo_thread_wk const &>(cw)<*this : false;
798  }
799  bool __fastcall operator<(algo_thread_wk const &ctw) const noexcept(true) FORCE_INLINE {
800  return closure_wk_<ctw.closure_wk_;
801  }
802 
803  closure_t &__fastcall closure() noexcept(true) FORCE_INLINE {
804  return closure_wk_;
805  }
806  closure_t const &__fastcall closure() const noexcept(true) FORCE_INLINE {
807  return closure_wk_;
808  }
809 
810  typename os_traits::thread_exception const &__fastcall exception_thrown_in_thread() const noexcept(true) override FORCE_INLINE {
811  return exception_thrown_in_thread_;
812  }
813  void throw_any_exception() const noexcept(false) override FORCE_INLINE {
814  exception_thrown_in_thread_.throw_if_set();
815  }
816  work_complete_t *__fastcall work_complete() noexcept(true) final override FORCE_INLINE {
817  return &work_complete_;
818  }
819  void update_edge(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
820  closure_wk_.update_edge(e_details);
821  }
822  void __fastcall process_joinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
823  /* Signal (& release) anyone waiting for the work to be completed.
824  It is really important that this gets done, despite any exceptions that may be thrown, this is to release any thread(s) waiting on the result, in case of exceptions being thrown. Any of these exceptions that are uncaught by the user, will be eventually caught in the first registered. Then in the get_results() function, that exception will be re-thrown, to pass it to the user. Hence the need to ensure that the "work_complete" signal is set in all cases, but allow exceptions to propagate transparently (as I will handle them later, in base-class code).
825  */
826  try {
827  closure_wk_.process();
828 #if defined(__GNUC__) && !defined(__clang__)
829  } catch (abi::__forced_unwind &) {
830  throw;
831 #endif
832  } catch (...) {
833  exception_thrown_in_thread_.set(std::current_exception());
834  }
835  closure_wk_.update_edge(e_details);
836  }
837  void __fastcall process_nonjoinable(typename cfg_type::node_property_t::value_type const e_details) noexcept(false) override FORCE_INLINE {
838  closure_wk_.process();
839  closure_wk_.update_edge(e_details);
840  }
841 
842  void resize_output(typename work_complete_t::containers_type::size_type const out_colln_size) noexcept(false) FORCE_INLINE {
843  work_complete_.resize_output(out_colln_size);
844  }
845 
846  tstring __fastcall to_string() const noexcept(false) override {
847  tostringstream os;
848  os
849  <<base_t::to_string()
850  <<_T(", enqueued work=0x")<<this
851  <<_T(", enqueued type: ")<<os_traits::thread_traits::demangle_name(typeid(*this))
852  <<_T(", work complete event=0x")<<&work_complete_
853  <<_T(", work details: ")<<closure_wk_
854  <<_T(", exception details: ")<<exception_thrown_in_thread_;
855  return os.str();
856  }
857 
858  private:
859  closure_t closure_wk_;
860  typename os_traits::thread_exception exception_thrown_in_thread_;
861  work_complete_t &work_complete_;
862  };
863 
864  /// With this class, the user can explicitly join with the transferred closure_base-derived closure returned from a parallel algorithm, but only via the execution_context, created via the appropriate thread_pool derived from thread_pool_type.
865  /**
866  This class also allocates a custom memory-buffer on the heap for containing the closure_base-derived closure generated by the parallel algorithms within subdivide_n_gen_wk::process(), to reduce the number of calls to the global operator new(). Note that the dtors of these placement-new'd closures must be called before the dtor of the buufedr, otherwise heap corruption may occur. This requirement is guaranteed because within the base class the work_complete_ object is only signalled once allmutations on the collection is complete, and this can occur only after all of the subdivide_n_gen_wk::process() functions have finished recursing, i.e. the work has been distributed, so all of the closures created to do that have been mutated.
867 
868  \see algo_thread_wk, nonjoinable_buff_t, subdivide_n_gen_wk::process()
869  */
870  template<typename OST, class ThrW, class WFlg, class SubDivAlgWk, template<class> class Del, template<class> class AtCtr, class CFG>
872  public:
874  typedef typename base_t::os_traits os_traits;
876  typedef typename base_t::cfg_type cfg_type;
879  typedef typename base_t::closure_t closure_t;
880 
882 
883  __stdcall algo_thread_wk_buffered(work_complete_t &w, typename closure_t::argument_type &&tw, typename algo_work_heap_type::size_type const num_objs, typename cfg_details_type::params const &p) noexcept(false) FORCE_INLINE
884  : base_t(w, std::forward<typename closure_t::argument_type>(tw), p), algo_work_heap_(num_objs) {
885  }
887 
888  /// Access the pre-allocated buffer for the thread_wk_types that subdivide_n_gen_wk::process() creates.
889  /**
890  This is so that these thread_wk_types can be allocated via placement new into this buffer to reduce load on operator new() in the transfer of nonjoinable_buff() work in subdivide_n_gen_wk::process().
891  */
892  algo_work_heap_type &__fastcall algo_work_heap() noexcept(true) FORCE_INLINE {
893  return algo_work_heap_;
894  }
895 
896  tstring __fastcall to_string() const noexcept(false) override {
897  tostringstream os;
898  os
899  <<base_t::to_string()
900  <<_T(", algo_work_heap: ")<<algo_work_heap_;
901  return os.str();
902  }
903 
904  private:
905  algo_work_heap_type algo_work_heap_;
906  };
907 
908 } } } } }
909