1 // Copyright (C) 2003  Davis E. King (davis@dlib.net)
2 // License: Boost Software License   See LICENSE.txt for the full license.
3 #ifndef DLIB_THREADS_KERNEl_SHARED_
4 #define DLIB_THREADS_KERNEl_SHARED_
5 
6 // this file should be included at the bottom of one of the thread kernel headers for a
7 // specific platform.
8 //#include "../threads.h"
9 #include "auto_mutex_extension.h"
10 #include "../binary_search_tree.h"
11 #include "../member_function_pointer.h"
12 #include "../memory_manager.h"
13 #include "../queue.h"
14 #include "../set.h"
15 #include "../test_for_odr_violations.h"
16 
17 
18 
19 
20 
21 namespace dlib
22 {
23 
24 
25 // ----------------------------------------------------------------------------------------
26 
27     namespace threads_kernel_shared
28     {
29         void thread_starter (
30             void*
31         );
32 
33         class threader
34         {
35             /*!
36                 INITIAL VALUE
37                     - pool_count == 0 and
38                     - data_ready is associated with the mutex data_mutex
39                     - data_empty is associated with the mutex data_mutex
40                     - destructed is associated with the mutex data_mutex
41                     - destruct == false
42                     - total_count == 0
43                     - function_pointer == 0
44                     - do_not_ever_destruct == false
45 
46                 CONVENTION
47                     - data_ready is associated with the mutex data_mutex
48                     - data_empty is associated with the mutex data_mutex
49                     - data_ready == a signaler used signal when there is new data waiting
50                       to start a thread with.
51                     - data_empty == a signaler used to signal when the data is now empty
52                     - pool_count == the number of suspended threads in the thread pool
53                     - total_count == the number of threads that are executing anywhere.  i.e.
54                       pool_count + the ones that are currently running some user function.
55                     - if (function_pointer != 0) then
56                         - parameter == a void pointer pointing to the parameter which
57                           should be used to start the next thread
58                         - function_pointer == a pointer to the next function to make a
59                           new thread with
60 
61                     - if (the destructor is running) then
62                         - destruct == true
63                     - else
64                         - destruct == false
65 
66                     - thread_ids is locked by the data_mutex
67                     - thread_ids == a set that contains the thread id for each thread spawned by this
68                       object.
69             !*/
70 
71 
72         public:
73             threader (
74             );
75 
76             ~threader (
77             );
78 
79             void destruct_if_ready (
80             );
81             /*!
82                 ensures
83                     - if (there are no threads currently running and we haven't set do_not_ever_destruct) then
84                         - calls delete this
85                     - else
86                         - does nothing
87             !*/
88 
89             bool create_new_thread (
90                 void (*funct)(void*),
91                 void* param
92             );
93 
94             template <
95                 typename T
96                 >
unregister_thread_end_handler(T & obj,void (T::* handler)())97             void unregister_thread_end_handler (
98                 T& obj,
99                 void (T::*handler)()
100             )
101             {
102                 member_function_pointer<> mfp, junk_mfp;
103                 mfp.set(obj,handler);
104 
105                 thread_id_type junk_id;
106 
107                 // find any member function pointers in the registry that point to the same
108                 // thing as mfp and remove them
109                 auto_mutex M(reg.m);
110                 reg.reg.reset();
111                 while (reg.reg.move_next())
112                 {
113                     while (reg.reg.current_element_valid() && reg.reg.element().value() == mfp)
114                     {
115                         reg.reg.remove_current_element(junk_id, junk_mfp);
116                     }
117                 }
118             }
119 
120             template <
121                 typename T
122                 >
register_thread_end_handler(T & obj,void (T::* handler)())123             void register_thread_end_handler (
124                 T& obj,
125                 void (T::*handler)()
126             )
127             {
128                 thread_id_type id = get_thread_id();
129                 member_function_pointer<> mfp;
130                 mfp.set(obj,handler);
131 
132                 auto_mutex M(reg.m);
133                 reg.reg.add(id,mfp);
134             }
135 
136             bool is_dlib_thread (
137                 thread_id_type id
138             );
139 
140         private:
141 
142             friend void thread_starter (
143                 void*
144             );
145 
146             void call_end_handlers (
147             );
148             /*!
149                 ensures
150                     - calls the registered end handlers for the calling thread and
151                       then removes them from reg.reg
152             !*/
153 
154 
155             // private data
156             set<thread_id_type,memory_manager<char>::kernel_2b>::kernel_1b_c thread_ids;
157             unsigned long total_count;
158             void* parameter;
159             void (*function_pointer)(void*);
160             unsigned long pool_count;
161             mutex data_mutex;           // mutex to protect the above data
162             signaler data_ready;        // signaler to signal when there is new data
163             signaler data_empty;        // signaler to signal when the data is empty
164             bool destruct;
165             signaler destructed;        // signaler to signal when a thread has ended
166             bool do_not_ever_destruct;
167 
168             struct registry_type
169             {
170                 mutex m;
171                 binary_search_tree<
172                     thread_id_type,
173                     member_function_pointer<>,
174                     memory_manager<char>::kernel_2a
175                     >::kernel_2a_c reg;
176             };
177 
178             // stuff for the register_thread_end_handler
179             registry_type reg;
180 
181 
182             // restricted functions
183             threader(threader&);        // copy constructor
184             threader& operator=(threader&);    // assignement opertor
185 
186         };
187 
188     // ------------------------------------------------------------------------------------
189 
190         threader& thread_pool (
191         );
192         /*!
193             ensures
194                 - returns a reference to the global threader object
195         !*/
196 
197     // ------------------------------------------------------------------------------------
198 
199         extern bool thread_pool_has_been_destroyed;
200     }
201 
202     bool is_dlib_thread (
203         thread_id_type id
204     );
205 
206     bool is_dlib_thread (
207     );
208 
209 // ----------------------------------------------------------------------------------------
210 
create_new_thread(void (* funct)(void *),void * param)211     inline bool create_new_thread (
212         void (*funct)(void*),
213         void* param
214     )
215     {
216         try
217         {
218             // now make this thread
219             return threads_kernel_shared::thread_pool().create_new_thread(funct,param);
220         }
221         catch (std::bad_alloc&)
222         {
223             return false;
224         }
225     }
226 
227 // ----------------------------------------------------------------------------------------
228 
229     template <
230         typename T
231         >
register_thread_end_handler(T & obj,void (T::* handler)())232     inline void register_thread_end_handler (
233         T& obj,
234         void (T::*handler)()
235     )
236     {
237         DLIB_ASSERT(is_dlib_thread(),
238                "\tvoid register_thread_end_handler"
239             << "\n\tYou can't register a thread end handler for a thread dlib didn't spawn."
240             );
241 
242         threads_kernel_shared::thread_pool().register_thread_end_handler(obj,handler);
243     }
244 
245 // ----------------------------------------------------------------------------------------
246 
247     template <
248         typename T
249         >
unregister_thread_end_handler(T & obj,void (T::* handler)())250     inline void unregister_thread_end_handler (
251         T& obj,
252         void (T::*handler)()
253     )
254     {
255         // Check if the thread pool has been destroyed and if it has then don't do anything.
256         // This bool here is always true except when the program has started to terminate and
257         // the thread pool object has been destroyed.  This if is here to catch other global
258         // objects that have destructors that try to call unregister_thread_end_handler().
259         // Without this check we get into trouble if the thread pool is destroyed before these
260         // objects.
261         if (threads_kernel_shared::thread_pool_has_been_destroyed == false)
262             threads_kernel_shared::thread_pool().unregister_thread_end_handler(obj,handler);
263     }
264 
265 // ----------------------------------------------------------------------------------------
266 
267 }
268 
269 #ifdef NO_MAKEFILE
270 #include "threads_kernel_shared.cpp"
271 #endif
272 
273 #endif // DLIB_THREADS_KERNEl_SHARED_
274 
275