libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_dsel_types.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_PRIVATE_THREAD_DSEL_TYPES_HPP
2 #define LIBJMMCG_CORE_PRIVATE_THREAD_DSEL_TYPES_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2010 by J.M.McGuiness, coder@hussar.me.uk
6 **
7 ** This library is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU Lesser General Public
9 ** License as published by the Free Software Foundation; either
10 ** version 2.1 of the License, or (at your option) any later version.
11 **
12 ** This library is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 ** Lesser General Public License for more details.
16 **
17 ** You should have received a copy of the GNU Lesser General Public
18 ** License along with this library; if not, write to the Free Software
19 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 
22 #include "../../core/config.h"
23 
24 #include <boost/bind/bind.hpp>
25 #include <numeric>
26 #include <type_traits>
27 
28 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd {
29 
30  /// A modifier that enables dividing the thread_pool into a number of equal-sized cliques of pool_threads.
31  /**
32  It applies only to parallel algorithms (a compile-time error will result if it is used with an incompatible closure_base-derived closure type).
33 
34  \see cliques_t
35  */
36  struct cliques {
37  typedef std::size_t element_type;
38 
39  /// The number of cliques into which the thread_pool should be divided.
40  /**
41  This is useful for limiting the number of tasks a parallel algorithm may automatically generate, so as to avoid swamping the thread_pool with tasks, and therefore causing excessive horizontal threading. (Horizontal threads are always dynamically generated, whereas vertical pool_threads may be precached in the thread_pool.
42  */
44 
45  explicit constexpr cliques(element_type c) noexcept(true) FORCE_INLINE
46  : number(c) {
47  }
48  };
49 
50  namespace private_ {
51 
52  /**
53  \todo The input process() had two arguments, including the cfg. This closure has only one argument to process...
54  */
55  namespace kernel_priority {
56 
57  /// This library-internal class allows you to temporarily set the kernel-thread priority at which the work is to be run.
58  /**
59  Note that the original priority is always restored, even if exceptions are thrown. No hacks here, as we haven't erased the type like in set_priority_closure.
60 
61  \see priority
62  */
63  template<
64  class TPB,
65  typename TPB::thread_traits::api_params_type::priority_type Pri,
66  class Wk ///< The type of the wrapped work.
67  >
68  class work;
69  template<
70  class TPB,
72  class Wk
73  >
74  class work<TPB, Pri, void (__fastcall Wk::*)() const> : public Wk {
75  public:
76  typedef void result_type;
77  typedef typename TPB::thread_traits thread_traits;
78  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
79 
80  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
81 
83 
84  /// Create the work with the appropriate priority.
85  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
86  : Wk(wk) {
87  }
88 
89  /// Return a reference to the wrapped work.
90  /**
91  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
92  */
93  void get_results() const noexcept(true) FORCE_INLINE {
94  Wk::get_results();
95  }
96  /// Return a reference to the wrapped work.
97  /**
98  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
99  */
100  void __fastcall get_results() noexcept(true) FORCE_INLINE {
101  Wk::get_results();
102  }
103 
104  /// An override of the process() function in the base-class that sets and restores the priority around the work.
105  void __fastcall process() const FORCE_INLINE {
107  Wk::process();
108  }
109  };
110  template<
111  class TPB,
113  class Wk
114  >
115  class work<TPB, Pri, void (__fastcall Wk::*)()> : public Wk {
116  public:
117  typedef void result_type;
118  typedef typename TPB::thread_traits thread_traits;
119  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
120 
121  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
122 
124 
125  /// Create the work with the appropriate priority.
126  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
127  : Wk(wk) {
128  }
129 
130  /// Return a reference to the wrapped work.
131  /**
132  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
133  */
134  void __fastcall get_results() const noexcept(true) FORCE_INLINE {
135  Wk::get_results();
136  }
137  /// Return a reference to the wrapped work.
138  /**
139  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
140  */
141  void __fastcall get_results() noexcept(true) FORCE_INLINE {
142  Wk::get_results();
143  }
144 
145  /// An override of the process() function in the base-class that sets and restores the priority around the work.
146  void __fastcall process() FORCE_INLINE {
148  Wk::process();
149  }
150  };
151  template<
152  class TPB,
154  class Wk,
155  class Res
156  >
157  class work<TPB, Pri, void (__fastcall Wk::*)(Res &) const> : public Wk {
158  public:
159  typedef Res result_type;
160  typedef typename TPB::thread_traits thread_traits;
161  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
162 
163  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
164 
166 
167  /// Create the work with the appropriate priority.
168  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
169  : Wk(wk) {
170  }
171 
172  /// Return a reference to the wrapped work.
173  /**
174  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
175  */
176  const result_type & __fastcall get_results() const noexcept(true) FORCE_INLINE {
177  return Wk::get_results();
178  }
179  /// Return a reference to the wrapped work.
180  /**
181  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
182  */
183  result_type & __fastcall get_results() noexcept(true) FORCE_INLINE {
184  return Wk::get_results();
185  }
186 
187  /// An override of the process() function in the base-class that sets and restores the priority around the work.
188  void __fastcall process(result_type &r) const FORCE_INLINE {
190  Wk::process(r);
191  }
192  };
193  template<
194  class TPB,
196  class Wk,
197  class Res
198  >
199  class work<TPB, Pri, void (__fastcall Wk::*)(Res &)> : public Wk {
200  public:
201  typedef Res result_type;
202  typedef typename TPB::thread_traits thread_traits;
203  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
204 
205  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
206 
208 
209  /// Create the work with the appropriate priority.
210  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
211  : Wk(wk) {
212  }
213 
214  /// Return a reference to the wrapped work.
215  /**
216  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
217  */
218  const result_type & __fastcall get_results() const noexcept(true) {
219  return Wk::get_results();
220  }
221  /// Return a reference to the wrapped work.
222  /**
223  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
224  */
225  result_type & __fastcall get_results() noexcept(true) FORCE_INLINE {
226  return Wk::get_results();
227  }
228 
229  /// An override of the process() function in the base-class that sets and restores the priority around the work.
230  void __fastcall process(result_type &r) FORCE_INLINE {
232  Wk::process(r);
233  }
234  };
235 
236  template<
237  class TPB,
239  class Wk,
240  class Res,
241  class CFGP
242  >
243  class work<TPB, Pri, void (__fastcall Wk::*)(Res &, CFGP const &) const> : public Wk {
244  public:
245  typedef Res result_type;
246  typedef typename TPB::thread_traits thread_traits;
247  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
248 
249  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
250 
252 
253  /// Create the work with the appropriate priority.
254  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
255  : Wk(wk) {
256  }
257 
258  /// Return a reference to the wrapped work.
259  /**
260  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
261  */
262  const result_type & __fastcall get_results() const noexcept(true) FORCE_INLINE {
263  return Wk::get_results();
264  }
265  /// Return a reference to the wrapped work.
266  /**
267  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
268  */
269  result_type & __fastcall get_results() noexcept(true) FORCE_INLINE {
270  return Wk::get_results();
271  }
272 
273  /// An override of the process() function in the base-class that sets and restores the priority around the work.
274  void __fastcall process(result_type &r, CFGP const &cfgp) const FORCE_INLINE {
276  Wk::process(r, cfgp);
277  }
278  };
279  template<
280  class TPB,
282  class Wk,
283  class Res,
284  class CFGP
285  >
286  class work<TPB, Pri, void (__fastcall Wk::*)(Res &, CFGP const &)> : public Wk {
287  public:
288  typedef Res result_type;
289  typedef typename TPB::thread_traits thread_traits;
290  typedef typename thread_traits::api_params_type::priority_type priority_type; ///< A convenience typedef for the thread-trait specific priority type.
291 
292  static constexpr priority_type priority=Pri; ///< The priority at which the wrapped work should run.
293 
295 
296  /// Create the work with the appropriate priority.
297  explicit __stdcall work(Wk &&wk) noexcept(noexcept(Wk(std::declval<Wk>()))) FORCE_INLINE
298  : Wk(wk) {
299  }
300 
301  /// Return a reference to the wrapped work.
302  /**
303  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
304  */
305  const result_type & __fastcall get_results() const noexcept(true) FORCE_INLINE {
306  return Wk::get_results();
307  }
308  /// Return a reference to the wrapped work.
309  /**
310  This function should really be unimplementable, because work wrapped by this class does not return a value. But I need this class to be concrete, because it is used.
311  */
312  result_type & __fastcall get_results() noexcept(true) FORCE_INLINE {
313  return Wk::get_results();
314  }
315 
316  /// An override of the process() function in the base-class that sets and restores the priority around the work.
317  void __fastcall process(result_type &r, CFGP const &cfgp) FORCE_INLINE {
319  Wk::process(r, cfgp);
320  }
321  };
322 
323  }
324 
325  /// The parallel algorithms inherits from this to assist with implementing the cliques_t language element in the DSEL.
326  template<class Alg>
327  struct parallel_algorithm final : public Alg {
328  typedef Alg base_t;
330 
331  explicit parallel_algorithm(operation_type &&op) noexcept(noexcept(base_t(op))) FORCE_INLINE
332  : base_t(op) {}
333  parallel_algorithm(parallel_algorithm const &a) noexcept(noexcept(base_t(std::declval<parallel_algorithm>()))) FORCE_INLINE
334  : base_t(a) {}
335  };
336 
337  /// A wrapper for converting a boost::bind() unspecified-object into an object suitable for transferring into a thread_pool.
338  template<class BindFn>
340  public:
341  typedef BindFn operation_type;
343 
344  /**
345  \param fn An object returned from calling boost::bind. There must be no unbound arguments in the ctor, otherwise this will fail to compile. Also the called function must return an instance of the result_type object, not void.
346  */
347  explicit __stdcall wrap_boost_bind_t(operation_type &&fn) noexcept(noexcept(operation_type(std::declval<operation_type>()))) FORCE_INLINE
348  : boost_fn(fn) {
349  }
350 
351  void __fastcall process(result_type &res) FORCE_INLINE {
352  res=boost_fn();
353  }
354 
355  template<class BindFn1> constexpr bool __fastcall FORCE_INLINE
356  operator<(BindFn1 const &) const noexcept(true) {
357  return true;
358  }
359 
360  private:
361  operation_type boost_fn;
362  };
363 
364  /// A wrapper for converting a boost::bind() unspecified-object into an object suitable for transferring into a thread_pool.
365  template<class BindFn>
367  public:
368  typedef BindFn operation_type;
370 
371  /**
372  \param fn An object returned from calling std::bind. There must be no unbound arguments in the ctor, otherwise this will fail to compile. Also the called function must return an instance of the result_type object, not void.
373  */
374  explicit __stdcall wrap_std_bind_t(operation_type &&fn) noexcept(noexcept(operation_type(std::declval<operation_type>()))) FORCE_INLINE
375  : std_fn(fn) {
376  }
377 
378  void __fastcall process(result_type &res) FORCE_INLINE {
379  res=std_fn();
380  }
381 
382  template<class BindFn1> constexpr bool __fastcall FORCE_INLINE
383  operator<(BindFn1 const &) const noexcept(true) {
384  return true;
385  }
386 
387  private:
388  operation_type std_fn;
389  };
390 
391  /// A modifier to allow joinably transferring the work to the pool.
392  /**
393  If this is used to add work to a pool, and an execution_context does not capture the result, then the result is UB.
394 
395  \todo Clearly the above is undesirable, and it would be better if we could somehow force the compiler to emit an error if the user fails to capture the result.
396  */
397  template<class TPB>
398  class joinable_t;
399  /// A modifier to allow non-joinably transferring the work to the pool.
400  template<class TPB>
401  class nonjoinable_t;
402  /// A modifier to allow setting the kernel-level priority that will be used by the thread whilst processing the work.
403  /**
404  \param Pri The priority at which the work should be executed. Note that this priority is only used whilst the work is being executed, and after the work is completed, or an exception is thrown, the thread resets to the default priority of the thread pool.
405  */
406  template<template<class> class Joinability, class TPB, typename TPB::priority_type Pri>
407  class priority_t;
408  template<class TPB, typename TPB::priority_type Pri>
409  class priority_t<joinable_t, TPB, Pri> : private joinable_t<TPB> {
410  public:
414  typedef typename base_t::os_traits os_traits;
415  typedef typename base_t::priority_type priority_type;
416 
417  static constexpr priority_type priority=Pri;
418 
420  : base_t(p, 1, cfg_p) {
421  }
423  : base_t(p, c, cfg_p) {
424  }
425 
426  /// Transfer the closure_base-derived closure to be process()ed at the specified priority, specified by the template parameter.
427  /**
428  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
429 
430  \param wk The closure_base-derived closure, created from the InpWk, to be asynchronously executed. The result_type of the closure_base-derived closure is taken from a member typedef. The default mutator function is called process(result_type &) or process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of mutator. Note that the process(result_type &) or process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it.
431  \return An opaque execution_context_stack which is captured by "auto const &" or "auto &&", for requesting the results from the asynchronously process()'d closure_base-derived closure.
432 
433  \see create_direct, execution_context_stack, kernel_priority::closure
434  */
435  template<
436  class InpWk,
438  >
440  push_back(InpWk &&wk) noexcept(false) {
441  return base_t::push_back(Ret(std::forward<InpWk>(wk)));
442  }
443 
444  template<
445  class InpWk,
447  >
449  operator<<(InpWk &&wk) noexcept(false) {
450  return push_back(std::forward<InpWk>(wk));
451  }
452 
453  template<
454  class R,
455  class F,
456  class L
457  >
458  typename thread_pool_type::template execution_context_stack<
460  thread_pool_type, Pri, typename thread_pool_type::template create_direct<
462  >
463  >
465  operator<<(boost::_bi::bind_t<R, F, L> &&wk) noexcept(false) {
466  return push_back(wrap_boost_bind_t<boost::_bi::bind_t<R, F, L> >(std::forward<boost::_bi::bind_t<R, F, L>>(wk)));
467  }
468 
469  template<
470  template<class, class, class> class B,
471  class R,
472  class F,
473  class L,
474  class Test=typename std::enable_if<std::is_bind_expression<B<R, F, L>>::value>::type
475  >
476  typename thread_pool_type::template execution_context_stack<
478  thread_pool_type, Pri, typename thread_pool_type::template create_direct<
479  wrap_std_bind_t<B<R, F, L>>
480  >
481  >
483  operator<<(B<R, F, L> &&wk) noexcept(false) {
484  return push_back(wrap_std_bind_t<B<R, F, L> >(std::forward<B<R, F, L>>(wk)));
485  }
486  };
487  template<class TPB, typename TPB::priority_type Pri>
489  public:
493  typedef typename base_t::os_traits os_traits;
494  typedef typename base_t::priority_type priority_type;
495 
496  static constexpr priority_type priority=Pri;
497 
499  : base_t(p, 1, cfg_p) {
500  }
502  : base_t(p, c, cfg_p) {
503  }
504 
505  /// Transfer the closure_base-derived closure to be process()ed at the specified priority, specified by the template parameter.
506  /**
507  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
508 
509  \param wk The closure_base-derived closure to be asynchronously executed, that must be copy-constructible. The result_type is inferred from the process(result_type &) or process() member-functions declared in the Wk type. Note that the process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it. The default mutator function is called process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of create_direct.
510  \return A reference to the pool to allow chaining.
511 
512  \see create_direct, thread_wk_t, kernel_priority::work
513  */
514  template<class InpWk>
515  thread_pool_type & __fastcall FORCE_INLINE
516  push_back(InpWk &&wk) noexcept(false) {
517  typedef typename thread_pool_type::template create_direct<InpWk> creator_t;
519 
521  }
522 
523  template<
524  class R,
525  class F,
526  class L
527  >
529  operator<<(boost::_bi::bind_t<R, F, L> &&wk) noexcept(false) {
530  return push_back(wrap_boost_bind_t<boost::_bi::bind_t<R, F, L> >(std::forward<boost::_bi::bind_t<R, F, L>>(wk)));
531  }
532 
533  template<
534  template<class, class, class> class B,
535  class R,
536  class F,
537  class L,
538  class Test=typename std::enable_if<std::is_bind_expression<B<R, F, L>>::value>::type
539  >
541  operator<<(B<R, F, L> &&wk) noexcept(false) {
542  return push_back(wrap_std_bind_t<B<R, F, L> >(std::forward<B<R, F, L>>(wk)));
543  }
544 
545  template<class InpWk>
547  operator<<(InpWk &&wk) noexcept(false) {
548  return push_back(std::forward<InpWk>(wk));
549  }
550 
551  /// We don't support priorities on closure_base-derived closure that is a parallel_algorithm.
552  template<class Alg>
553  thread_pool_type & __fastcall
555  /// We don't support priorities on closure_base-derived closure that is a parallel_algorithm.
556  template<class Alg>
557  thread_pool_type & __fastcall
558  operator<<(parallel_algorithm<Alg> &&)=delete;
559  };
560 
561  template<class Base>
562  class cliques_t;
563  template<class TPB>
564  class cliques_t<joinable_t<TPB> > : private joinable_t<TPB> {
565  public:
566  typedef joinable_t<TPB> base_t;
569  typedef typename base_t::os_traits os_traits;
570  typedef cliques::element_type element_type;
571 
572  constexpr cliques_t() noexcept(true) FORCE_INLINE {
573  }
574  constexpr cliques_t(thread_pool_type &p, cliques::element_type const &c, typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const &cfg_p) noexcept(true) FORCE_INLINE
575  : base_t(p, c, cfg_p) {
576  }
577 
578  /// Transfer the closure_base-derived closure with the appropriate priority, specified by the template parameter.
579  /**
580  This operation requires no memory allocations, in addition to those required for the operation of the parallel algorithm.
581 
582  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
583 
584  \param wk The closure_base-derived closure to be asynchronously executed, that must be copy-constructible. The result_type is inferred from the process(result_type &) or process() member-functions declared in the Wk type. Note that the process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it. The default mutator function is called process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of create_direct.
585  \return An opaque type, derived from an execution_context, that must be captured using "auto const &" or "auto &&"; for requesting the results from the asynchronously process()'d closure_base-derived closure.
586 
587  \see create_direct, execution_context
588  */
589  template<class Alg>
590  typename Alg::execution_context FORCE_INLINE
591  push_back(parallel_algorithm<Alg> &&wk) noexcept(false) {
592  return base_t::push_back(std::forward<parallel_algorithm<Alg>>(wk));
593  }
594 
595  template<class Alg>
596  typename Alg::execution_context __fastcall FORCE_INLINE
597  operator<<(parallel_algorithm<Alg> &&wk) noexcept(false) {
598  return push_back(std::forward<parallel_algorithm<Alg>>(wk));
599  }
600 
601  /// We don't support cliques on closure_base-derived closure that is not a parallel_algorithm.
602  template<class InpWk>
603  void __fastcall
604  push_back(InpWk &&)=delete;
605  /// We don't support cliques on closure_base-derived closure that is not a parallel_algorithm.
606  template<class InpWk>
607  void __fastcall
608  operator<<(InpWk &&)=delete;
609  };
610  template<class TPB>
611  class cliques_t<nonjoinable_t<TPB> > final : private nonjoinable_t<TPB> {
612  public:
613  typedef nonjoinable_t<TPB> base_t;
616  typedef typename base_t::os_traits os_traits;
617  typedef cliques::element_type element_type;
618 
619  constexpr cliques_t(thread_pool_type &p, cliques::element_type const &c, typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const &cfg_p) noexcept(true) FORCE_INLINE
620  : base_t(p, c, cfg_p) {
621  }
622 
623  /// We don't support cliques on closure_base-derived closure that is not a parallel_algorithm.
624  template<class InpWk>
625  thread_pool_type & __fastcall
626  push_back(InpWk &&)=delete;
627 
628  /// Transfer the closure_base-derived closure with the appropriate priority, specified by the template parameter.
629  /**
630  This operation requires 2 memory allocations, in addition to those required for the operation of the parallel algorithm.
631 
632  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
633 
634  \param wk The closure_base-derived closure to be asynchronously executed, that must be copy-constructible. The result_type is inferred from the process(result_type &) or process() member-functions declared in the Wk type. Note that the process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it. The default mutator function is called process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of create_direct.
635  \return A reference to the pool to allow chaining.
636 
637  \see create_direct
638  */
639  template<class Alg>
640  thread_pool_type & __fastcall FORCE_INLINE
641  push_back(parallel_algorithm<Alg> &&wk) noexcept(false) {
642  return base_t::push_back(std::move(wk));
643  }
644 
645  /// We don't support cliques on closure_base-derived closure that is not a parallel_algorithm.
646  template<class InpWk>
647  thread_pool_type & __fastcall
648  operator<<(InpWk &&)=delete;
649 
650  template<class Alg>
652  operator<<(parallel_algorithm<Alg> &&wk) noexcept(false) {
653  return push_back(std::forward<parallel_algorithm<Alg>>(wk));
654  }
655  };
656 
657  template<class TPB>
658  class joinable_t {
659  public:
660  typedef TPB thread_pool_type;
664 
666 
667  explicit constexpr joinable_t(const cliques::element_type c=1) noexcept(true) FORCE_INLINE
668  : pool(), num_cliques(c), cfg_parms("joinable") {
669  }
670  template<class T> constexpr FORCE_INLINE
671  joinable_t(T const * parent, typename pool_traits_type::thread_wk_elem_type::cfg_type::node_property_t::value_type const details[], const cliques::element_type c=1) noexcept(true)
672  : pool(), num_cliques(c), cfg_parms(parent, details) {
673  }
674  constexpr joinable_t(thread_pool_type &p, const cliques::element_type c, typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const &cfg_p) noexcept(true) FORCE_INLINE
675  : pool(&p), num_cliques(c), cfg_parms(pool->cfg(), cfg_p) {
676  }
677  constexpr joinable_t(joinable_t const &j, thread_pool_type &p) noexcept(true) FORCE_INLINE
678  : pool(&p), num_cliques(j.num_cliques), cfg_parms(pool->cfg(), j.cfg_parms) {
679  }
680 
681  /// Joinably transfer the closure_base-derived closure to the thread_pool.
682  /**
683  Verify that the closure_base-derived closure has not been previously transferred, if it has, throw an exception_type. If the implementation-defined result is not captured, the transferred closure_base-derived closure will not be process()ed.
684 
685  This operation requires no memory allocations, in addition to those required for the operation of the parallel algorithm.
686 
687  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
688 
689  \param wk The closure_base-derived closure, to be asynchronously executed. The result_type of the closure_base-derived closure is taken from a member typedef. The default mutator function is called process(result_type &) or process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of mutator. Note that the process(result_type &) or process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it.
690  \return An opaque type, derived from an execution_context_stack, that must be captured using "auto const &" or "auto &&"; for requesting the results from the asynchronously process()'d closure_base-derived closure.
691 
692  \see create_direct, execution_context_stack
693  */
694  template<class InpWk>
695  typename thread_pool_type::template execution_context_stack<InpWk> __fastcall FORCE_INLINE
696  push_back(InpWk &&wk) noexcept(false) {
697  typedef typename thread_pool_type::template execution_context_stack<InpWk> exec_ctx_t;
698 
699  return exec_ctx_t(*pool, cfg_parms, std::forward<InpWk>(wk));
700  }
701  /**
702  This operation requires no memory allocations, in addition to those required for the operation of the parallel algorithm.
703 
704  \return An opaque type, derived from an execution_context, that must be captured using "auto const &" or "auto &&"; for requesting the results from the asynchronously process()'d closure_base-derived closure.
705  */
706  template<class Alg>
707  typename Alg::execution_context __fastcall FORCE_INLINE
708  push_back(parallel_algorithm<Alg> &&wk) noexcept(false) {
709  return wk.process(num_cliques, cfg_parms);
710  }
711 
712  template<
713  class R,
714  class F,
715  class L
716  >
717  typename thread_pool_type::template execution_context_stack<wrap_boost_bind_t<boost::_bi::bind_t<R, F, L>>> FORCE_INLINE
718  operator<<(boost::_bi::bind_t<R, F, L> &&wk) noexcept(false) {
719  return push_back(wrap_boost_bind_t<boost::_bi::bind_t<R, F, L> >(std::forward<boost::_bi::bind_t<R, F, L>>(wk)));
720  }
721 
722  template<
723  template<class, class, class> class B,
724  class R,
725  class F,
726  class L,
727  class Test=typename std::enable_if<std::is_bind_expression<B<R, F, L>>::value>::type
728  >
729  typename thread_pool_type::template execution_context_stack<wrap_std_bind_t<B<R, F, L>>> FORCE_INLINE
730  operator<<(B<R, F, L> &&wk) noexcept(false) {
731  return push_back(wrap_std_bind_t<B<R, F, L> >(std::forward<B<R, F, L>>(wk)));
732  }
733 
734  template<class InpWk>
735  typename thread_pool_type::template execution_context_stack<InpWk> FORCE_INLINE
736  operator<<(InpWk &&wk) noexcept(false) {
737  typedef typename thread_pool_type::template execution_context_stack<InpWk> exec_ctx_t;
738 
739  return exec_ctx_t(*pool, cfg_parms, std::move(wk));
740  }
741  template<class Alg>
742  typename Alg::execution_context __fastcall FORCE_INLINE
743  operator<<(parallel_algorithm<Alg> &&wk) noexcept(false) {
744  return push_back(std::forward<parallel_algorithm<Alg>>(wk));
745  }
746 
747  /// Joinably transfer the closure_base-derived closure, which will be process()ed at the specified priority.
748  /**
749  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
750 
751  \see priority, priority_t
752  */
753  template<
754  typename thread_pool_type::priority_type Pri ///< The priority at which the work should be executed. Note that this priority is only used whilst the work is being executed, and after the work is completed, or an exception is thrown, the thread resets to the default priority of the thread pool. Note that this parameter is unused: it is the template-parameter that is used to specify the priority.
755  >
756  priority_t<joinable_t, TPB, Pri> __fastcall FORCE_INLINE
757  operator<<(typename thread_pool_type::template priority<Pri>) noexcept(true) {
758  return priority_t<joinable_t, TPB, Pri>(*pool, cfg_parms);
759  }
760 
761  /// Joinably transfer the closure_base-derived closure, using a sub-set of the pool_threads within the thread_pool.
762  /**
763  \param c The number of cliques into which the thread_pool should be divided.
764 
765  \see cliques, cliques_t
766  */
767  cliques_t<joinable_t> __fastcall FORCE_INLINE
768  operator<<(cliques &&c) noexcept(true) {
769  return cliques_t<joinable_t>(*pool, c.number, cfg_parms);
770  }
771 
772  private:
773  thread_pool_type * const pool;
774  const cliques::element_type num_cliques;
775  typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const cfg_parms;
776  };
777 
778  template<class TPB>
780  public:
781  typedef TPB thread_pool_type;
785 
787 
788  constexpr nonjoinable_t(const cliques::element_type c=1) noexcept(true) FORCE_INLINE
789  : pool(), num_cliques(c), cfg_parms("nonjoinable") {
790  }
791  template<class T> constexpr FORCE_INLINE
792  nonjoinable_t(T const * parent, typename pool_traits_type::thread_wk_elem_type::cfg_type::node_property_t::value_type const details[], const cliques::element_type c=1) noexcept(true)
793  : pool(), num_cliques(c), cfg_parms(parent, details) {
794  }
795  constexpr nonjoinable_t(thread_pool_type &p, const cliques::element_type c, typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const &cfg_p) noexcept(true) FORCE_INLINE
796  : pool(&p), num_cliques(c), cfg_parms(pool->cfg(), cfg_p) {
797  }
798  constexpr nonjoinable_t(nonjoinable_t const &nj, thread_pool_type &p) noexcept(true) FORCE_INLINE
799  : pool(&p), num_cliques(nj.num_cliques), cfg_parms(pool->cfg(), nj.cfg_parms) {
800  }
801 
802  /// Transfer the closure_base-derived closure into the thread_pool, non-joinably.
803  /**
804  No need to verify that the closure_base-derived closure has not been previously transferred.
805 
806  This operation requires at most 1 memory allocation.
807 
808  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
809 
810  \param wk The closure_base-derived closure to be asynchronously executed, that must be copy-constructible. The result_type is inferred from the process(result_type &) or process() member-functions declared in the Wk type. Note that the process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it. The default mutator function is called process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of create_direct.
811  \return A reference to the pool to allow chaining.
812 
813  \see create_direct
814  */
815  template<class InpWk>
816  thread_pool_type & __fastcall FORCE_INLINE
817  push_back(InpWk &&wk) noexcept(false) {
818  typedef typename thread_pool_type::template create_direct<InpWk> creator_t;
819  typedef typename pool_traits_type::template thread_wk<result_traits_, typename creator_t::closure_t, typename os_traits::lock_traits::anon_event_type, default_delete, os_traits::lock_traits::template atomic_counter_type> thread_wk_t;
820 
821  assert(dynamic_cast<thread_pool_type *>(pool));
822 
823  pool->add_nonjoinable_work(
824  typename pool_traits_type::template signalled_work_queue_type<typename thread_pool_type::work_distribution_mode::queue_model>::value_type(
825  new thread_wk_t(typename creator_t::closure_t::argument_type(std::forward<InpWk>(wk)), cfg_parms)
826  )
827  );
828  return *pool;
829  }
830  /**
831  This operation requires at most 2 memory allocations, in addition to those required for the operation of the parallel algorithm.
832  */
833  template<class Alg>
834  thread_pool_type & __fastcall FORCE_INLINE
835  push_back(parallel_algorithm<Alg> &&wk) noexcept(false) {
836  return wk.process(num_cliques, cfg_parms);
837  }
838 
839  template<class R, class F, class L>
841  operator<<(boost::_bi::bind_t<R, F, L> &&wk) noexcept(false) {
842  return push_back(wrap_boost_bind_t<boost::_bi::bind_t<R, F, L> >(std::forward<boost::_bi::bind_t<R, F, L>>(wk)));
843  }
844 
845  template<
846  template<class, class, class> class B,
847  class R,
848  class F,
849  class L,
850  class Test=typename std::enable_if<std::is_bind_expression<B<R, F, L>>::value>::type
851  >
853  operator<<(B<R, F, L> &&wk) noexcept(false) {
854  return push_back(wrap_std_bind_t<B<R, F, L> >(std::forward<B<R, F, L>>(wk)));
855  }
856 
857  template<class InpWk>
859  operator<<(InpWk &&wk) noexcept(false) {
860  return push_back(std::forward<InpWk>(wk));
861  }
862  template<class Alg>
864  operator<<(parallel_algorithm<Alg> &&wk) noexcept(false) {
865  return push_back(std::forward<parallel_algorithm<Alg>>(wk));
866  }
867 
868  /// Non-joinably transfer the closure_base-derived closure, which will be process()ed at the specified priority.
869  /**
870  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
871 
872  \see priority, priority_t
873  */
874  template<
875  typename thread_pool_type::priority_type Pri ///< The priority at which the work should be executed. Note that this priority is only used whilst the work is being executed, and after the work is completed, or an exception is thrown, the thread resets to the default priority of the thread pool. Note that this parameter is unused: it is the template-parameter that is used to specify the priority.
876  >
877  priority_t<nonjoinable_t, TPB, Pri> __fastcall FORCE_INLINE
878  operator<<(typename thread_pool_type::template priority<Pri>) noexcept(true) {
879  return priority_t<nonjoinable_t, TPB, Pri>(*pool, cfg_parms);
880  }
881 
882  /// Non-joinably transfer the closure_base-derived closure, using a sub-set of the pool_threads within the thread_pool.
883  /**
884  \param c The number of cliques into which the thread_pool should be divided.
885 
886  \see cliques, cliques_t
887  */
888  cliques_t<nonjoinable_t> __fastcall
889  operator<<(cliques &&c) noexcept(true) FORCE_INLINE {
890  return cliques_t<nonjoinable_t>(*pool, c.number, cfg_parms);
891  }
892 
893  private:
894  thread_pool_type * const pool;
895  const cliques::element_type num_cliques;
896  typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const cfg_parms;
897  };
898 
899  /**
900  This class transfers the closure_base-derived closure generated by the parallel algorithms within subdivide_n_gen_wk::process() into the thread_pool, but the work is allocated within the custom memory-buffer allocated within the algo_thread_wk_buffered that encapsulates the result of the parallel algorithm.
901 
902  \see algo_thread_wk_buffered, subdivide_n_gen_wk::process()
903  */
904  template<class TPB>
905  class nonjoinable_buff_t final {
906  public:
907  typedef TPB thread_pool_type;
911  typedef unsigned char * buffer_type;
912 
914 
915  /**
916  \param b A contiguous array of suitably aligned bytes of appropriate size for allocating the core_work items within it.
917  */
918  explicit constexpr nonjoinable_buff_t(buffer_type const b) noexcept(true) FORCE_INLINE
919  : buffer(b), pool(), cfg_parms("nonjoinable_buff") {
920  }
921  template<class T> constexpr FORCE_INLINE
922  nonjoinable_buff_t(buffer_type const b, T const * parent, typename pool_traits_type::thread_wk_elem_type::cfg_type::node_property_t::value_type const details[]) noexcept(true)
923  : buffer(b), pool(), cfg_parms(parent, details) {
924  }
925  constexpr nonjoinable_buff_t(nonjoinable_buff_t const &njb, thread_pool_type &p) noexcept(true) FORCE_INLINE
926  : buffer(njb.buffer), pool(&p), cfg_parms(pool->cfg(), njb.cfg_parms) {
927  }
928 
929  /// Transfer the closure_base-derived closure into the thread_pool, non-joinably.
930  /**
931  No need to verify that the closure_base-derived closure has not been previously transferred.
932 
933  This operation requires no memory allocations, in addition to those required for the operation of the parallel algorithm.
934 
935  \todo JMG: Hubert Matthews suggested that potentially expression templates could be used here to concatenate the thread_wk_t's that are transferred into the pool; also as an implementation of back_batching, i.e. GSS(k) scheduling.
936 
937  \param wk The closure_base-derived closure to be asynchronously executed, that must be copy-constructible. The result_type is inferred from the process(result_type &) or process() member-functions declared in the Wk type. Note that the process() member-function must not be overloaded, or this will not work, also that it must use the __fastcall calling-convention on those platforms that support it. The default mutator function is called process(), but you could provide an alternative member-function name if desired, as long as the signature is correct via the declaration of create_direct.
938  \return A reference to the pool to allow chaining.
939 
940  \see create_direct
941  */
942  template<class InpWk>
943  thread_pool_type & __fastcall FORCE_INLINE
944  push_back(InpWk &&wk) noexcept(false) {
945  typedef typename thread_pool_type::template create_direct<InpWk> creator_t;
946  typedef typename pool_traits_type::template thread_wk<result_traits_, typename creator_t::closure_t, typename os_traits::lock_traits::anon_event_type, placement_dtor, os_traits::lock_traits::template atomic_counter_type> thread_wk_t;
947 
948  assert(dynamic_cast<thread_pool_type *>(pool));
949  assert(std::accumulate(buffer, buffer+sizeof(thread_wk_t), 0UL)==0UL);
950  // Hurrah! See: we just saved loads of calls to global new & delete by using placement new here!
951  pool->add_nonjoinable_work(
952  typename pool_traits_type::template signalled_work_queue_type<typename thread_pool_type::work_distribution_mode::queue_model>::value_type(
953  new (buffer) thread_wk_t(typename creator_t::closure_t::argument_type(std::forward<InpWk>(wk)), cfg_parms)
954  )
955  );
956  return *pool;
957  }
958 
959  template<class InpWk>
961  operator<<(InpWk &&wk) noexcept(false) {
962  return push_back(std::forward<InpWk>(wk));
963  }
964 
965  private:
966  buffer_type const buffer;
967  thread_pool_type * const pool;
968  typename pool_traits_type::thread_wk_elem_type::cfg_details_type::params const cfg_parms;
969  };
970 
971 }
972 
973 } } }
974 
975 #endif