libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
thread_wrapper.hpp
Go to the documentation of this file.
1 #ifndef LIBJMMCG_CORE_THREAD_WRAPPER_HPP
2 #define LIBJMMCG_CORE_THREAD_WRAPPER_HPP
3 
4 /******************************************************************************
5 ** Copyright © 2004 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 /**
23  \file
24  Note that this file should be included after any MFC header files, or you'll get an error about WINDOWS.H being included.
25 */
26 
27 #include "private_/thread_base.hpp"
28 
29 namespace jmmcg { namespace LIBJMMCG_VER_NAMESPACE { namespace ppd {
30 
31  /// A handy utility class for the "thread_wrapper" class, that is supposed to do nothing.
32  /**
33  The idea is that you could have an automatic instance of this object that surrounds the worker_fn() method, is automatically instantiated in the process() method and released upon exit of the process() method.
34  */
36  // Does nothing! No context for the worker thread.
37 
38  /**
39  To assist in allowing compile-time computation of the algorithmic order of the threading model.
40  */
42  };
43 
44  /// This class is used to implement a class that wraps using a thread, it is an alternative to using a thread pool to manage your threads.
45  /**
46  Note that the most derived classes' dtor must call private_::thread_base::wait_thread_exit() in it's dtor to ensure that the thread goes out of scope before the object upon which it relies.
47  */
48  template<
50  typename Mdl,
51  typename TWC=null_thread_worker_context ///< This now allows for the worker thread to implement worker-thread specific-context, e.g. COM initialisation for apartment threading! (As the object here is constructed on the stack and within the context of the worker thread in the process() method, but before the worker_fn() method is called. This object must have a default constructor. Thanks to George Velimachitis (he suggested the context template idea)!
52  >
53  class wrapper : public private_::thread_base<API, Mdl> {
54  public:
55  using base_t=private_::thread_base<API, Mdl>;
56  using thread_context_t=TWC;
57  using lock_traits=typename base_t::lock_traits;
58  using thread_traits=typename base_t::thread_traits;
59  using os_traits=typename base_t::os_traits;
60  using exception_type=typename os_traits::exception_type;
61 
62  /**
63  To assist in allowing compile-time computation of the algorithmic order of the threading model.
64  */
67  && lock_traits::anon_event_type::memory_access_mode==ppd::generic_traits::memory_access_modes::crew_memory_access
70  );
71 
72  /// Create the wrapper object. Note that the underlying kernel thread will not have been created.
73  /**
74  \see base_t::create_running()
75  */
76  __stdcall wrapper(const wrapper &) noexcept(true) FORCE_INLINE;
77 
78  __stdcall ~wrapper() noexcept(false) override FORCE_INLINE;
79 
80  wrapper(wrapper &&)=delete;
81  void operator=(wrapper const &)=delete;
82  void operator=(wrapper &&)=delete;
83 
84  protected:
85  /**
86  \todo Consider moving this event into the stack of process() as an automatic variable in there. This may be useful in implementing detached threads. Detached threads may be useful in improving performance of horizontal threading, because the work_complete() lock in them won't have to wait for non-dependent work to complete before it returns.
87  */
88  mutable std::atomic<bool> exit_requested;
89 
90  explicit __stdcall wrapper(const typename thread_traits::api_params_type::suspend_period_ms ew_=50) noexcept(false) FORCE_INLINE;
91 
92  explicit __stdcall wrapper(thread_context_t &&thread_context, const typename thread_traits::api_params_type::suspend_period_ms ew_=50) noexcept(false) FORCE_INLINE;
93 
94  /// The method used to determine if there has been a request for the thread to exit, and therefore finish processing work, called before attempting to process() a work item.
95  /**
96  \return true if an exit from the thread has been requested, otherwise false to continue processing.
97  */
98  virtual bool __fastcall pre_exit() noexcept(false) FORCE_INLINE;
99 
100  /// This is the main "work" function. Implement it to do some work in the thread.
101  /**
102  \param context The context is non-const, to allow the client to modify the state of the context, if they need to. (Although this generalisation may be really required....)
103 
104  \return "false" to continue working. Otherwise return "true" to exit. (Or throw an exception - but that's a messy way to exit...)
105  */
106  virtual bool __fastcall worker_fn(thread_context_t &context) noexcept(false)=0;
107  /// Override this function is you want to do some other work to signal the exit of the thread.
108  /**
109  This is primarily used by the destructor to politely tell the thread function to quit. Note that if you override this function you must still implement the exit functionality, otherwise the destructor will time-out and terminate the worker thread.
110  */
111  typename thread_traits::api_params_type::states __fastcall process() noexcept(false) override;
112 
113  private:
114  thread_context_t thread_context_;
115  };
116 
117  /// This class is used to implement a class that wraps using a thread, it is an alternative to using a thread pool to manage your threads, in a C++ std::thread-style manner.
118  /**
119  \see wrapper, std::thread
120  */
121  template<
123  typename Mdl
124  >
125  class thread final : public wrapper<API, Mdl, std::function<void()>> {
126  public:
127  using base_t=wrapper<API, Mdl, std::function<void()>>;
128  using thread_context_t=typename base_t::thread_context_t;
129  using lock_traits=typename base_t::lock_traits;
130  using thread_traits=typename base_t::thread_traits;
131  using os_traits=typename base_t::os_traits;
132  using exception_type=typename base_t::exception_type;
133 
134  /**
135  To assist in allowing compile-time computation of the algorithmic order of the threading model.
136  */
139  && lock_traits::anon_event_type::memory_access_mode==ppd::generic_traits::memory_access_modes::crew_memory_access
142  );
143 
144  explicit __stdcall thread(const typename thread_traits::api_params_type::suspend_period_ms ew_=50) noexcept(false) FORCE_INLINE;
145  /// Create the wrapper object. Note that the underlying kernel thread will have been created, so the proc_fn will execute in the underlying thread (if any).
146  /**
147  This ctor requires a memory allocation for the functor.
148 
149  \param proc_fn The functor that will executed by the underlying kernel thread (if any).
150 
151  \see create_running()
152  */
153  template<class ProcFn>
154  explicit __stdcall thread(ProcFn &&proc_fn, const typename thread_traits::api_params_type::suspend_period_ms ew_=50) noexcept(false) FORCE_INLINE;
155 
156  __stdcall ~thread() noexcept(false) override FORCE_INLINE;
157 
158  private:
159  bool __fastcall worker_fn(thread_context_t &context) noexcept(false) override;
160  };
161 
162 } } }
163 
165 
166 #endif