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_CPp_
4 #define DLIB_THREADS_KERNEL_SHARED_CPp_
5 
6 #include "threads_kernel_shared.h"
7 #include "../assert.h"
8 #include "../platform.h"
9 #include <iostream>
10 
11 
12 #ifndef DLIB_THREAD_POOL_TIMEOUT
13 // default to 30000 milliseconds
14 #define DLIB_THREAD_POOL_TIMEOUT 30000
15 #endif
16 
17 namespace dlib
18 {
19 
20 // ----------------------------------------------------------------------------------------
21 // ----------------------------------------------------------------------------------------
22 // threader functions
23 // ----------------------------------------------------------------------------------------
24 // ----------------------------------------------------------------------------------------
25 
26     namespace threads_kernel_shared
27     {
28 
29         bool thread_pool_has_been_destroyed = false;
30 
31 // ----------------------------------------------------------------------------------------
32 
33         struct threader_destruct_helper
34         {
35             // cause the thread pool to begin its destruction process when
36             // global objects start to be destroyed
~threader_destruct_helperdlib::threads_kernel_shared::threader_destruct_helper37             ~threader_destruct_helper()
38             {
39                 thread_pool().destruct_if_ready();
40             }
41         };
42 
43 // ----------------------------------------------------------------------------------------
44 
thread_pool()45         threader& thread_pool (
46         )
47         {
48             static threader* thread_pool = new threader;
49             static threader_destruct_helper a;
50             return *thread_pool;
51         }
52 
53 // ----------------------------------------------------------------------------------------
54 
55         bool threader::
is_dlib_thread(thread_id_type id)56         is_dlib_thread (
57             thread_id_type id
58         )
59         {
60             auto_mutex M(data_mutex);
61             return thread_ids.is_member(id);
62         }
63 
64 // ----------------------------------------------------------------------------------------
65 
66         threader::
threader()67         threader (
68         ) :
69             total_count(0),
70             function_pointer(0),
71             pool_count(0),
72             data_ready(data_mutex),
73             data_empty(data_mutex),
74             destruct(false),
75             destructed(data_mutex),
76             do_not_ever_destruct(false)
77         {
78 #ifdef WIN32
79             // Trying to destroy the global thread pool when we are part of a DLL and the
80             // DLL is being unloaded can sometimes lead to weird behavior.  For example, in
81             // the python interpreter you will get the interpreter to hang.  Or if we are
82             // part of a MATLAB mex file and the file is being unloaded there can also be
83             // similar weird issues.  So when we are using dlib on windows we just disable
84             // the destruction of the global thread pool since it doesn't matter anyway.
85             // It's resources will just get freed by the OS.  This is even the recommended
86             // thing to do by Microsoft (http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx).
87             //
88             // As an aside, it's worth pointing out that the reason we try and free
89             // resources on program shutdown on other operating systems is so we can have
90             // clean reports from tools like valgrind which check for memory leaks.  But
91             // trying to do this on windows is a lost cause so we give up in this case and
92             // follow the Microsoft recommendation.
93             do_not_ever_destruct = true;
94 #endif // WIN32
95         }
96 
97 // ----------------------------------------------------------------------------------------
98 
99         threader::
~threader()100         ~threader (
101         )
102         {
103             data_mutex.lock();
104             destruct = true;
105             data_ready.broadcast();
106 
107             // wait for all the threads to end
108             while (total_count > 0)
109                 destructed.wait();
110 
111             thread_pool_has_been_destroyed = true;
112             data_mutex.unlock();
113         }
114 
115 // ----------------------------------------------------------------------------------------
116 
117         void threader::
destruct_if_ready()118         destruct_if_ready (
119         )
120         {
121             if (do_not_ever_destruct)
122                 return;
123 
124             data_mutex.lock();
125 
126             // if there aren't any active threads, just maybe some sitting around
127             // in the pool then just destroy the threader
128             if (total_count == pool_count)
129             {
130                 destruct = true;
131                 data_ready.broadcast();
132                 data_mutex.unlock();
133                 delete this;
134             }
135             else
136             {
137                 // There are still some user threads running so there isn't
138                 // much we can really do.  Just let the program end without
139                 // cleaning up threading resources.
140                 data_mutex.unlock();
141             }
142         }
143 
144 // ----------------------------------------------------------------------------------------
145 
146         void threader::
call_end_handlers()147         call_end_handlers (
148         )
149         {
150             reg.m.lock();
151             const thread_id_type id = get_thread_id();
152             thread_id_type id_copy;
153             member_function_pointer<> mfp;
154 
155             // Remove all the member function pointers for this thread from the tree
156             // and call them.
157             while (reg.reg[id] != 0)
158             {
159                 reg.reg.remove(id,id_copy,mfp);
160                 reg.m.unlock();
161                 mfp();
162                 reg.m.lock();
163             }
164             reg.m.unlock();
165         }
166 
167     // ------------------------------------------------------------------------------------
168 
169         bool threader::
create_new_thread(void (* funct)(void *),void * param)170         create_new_thread (
171             void (*funct)(void*),
172             void* param
173         )
174         {
175 
176             // get a lock on the data mutex
177             auto_mutex M(data_mutex);
178 
179             // loop to ensure that the new function pointer is in the data
180             while (true)
181             {
182                 // if the data is empty then add new data and quit loop
183                 if (function_pointer == 0)
184                 {
185                     parameter = param;
186                     function_pointer = funct;
187                     break;
188                 }
189                 else
190                 {
191                     // wait for data to become empty
192                     data_empty.wait();
193                 }
194             }
195 
196 
197             // get a thread for this new data
198             // if a new thread must be created
199             if (pool_count == 0)
200             {
201                 // make thread and add it to the pool
202                 if ( threads_kernel_shared_helpers::spawn_thread(thread_starter, this) == false )
203                 {
204                     function_pointer = 0;
205                     parameter = 0;
206                     data_empty.signal();
207                     return false;
208                 }
209                 ++total_count;
210             }
211             // wake up a thread from the pool
212             else
213             {
214                 data_ready.signal();
215             }
216 
217             return true;
218         }
219 
220     // ------------------------------------------------------------------------------------
221 
thread_starter(void * object)222         void thread_starter (
223             void* object
224         )
225         {
226             // get a reference to the calling threader object
227             threader& self = *static_cast<threader*>(object);
228 
229 
230             {
231             auto_mutex M(self.data_mutex);
232 
233             // add this thread id
234             thread_id_type thread_id = get_thread_id();
235             self.thread_ids.add(thread_id);
236 
237             // indicate that this thread is now in the thread pool
238             ++self.pool_count;
239 
240             while (self.destruct == false)
241             {
242                 // if data is ready then process it and launch the thread
243                 // if its not ready then go back into the pool
244                 while (self.function_pointer != 0)
245                 {
246                     // indicate that this thread is now out of the thread pool
247                     --self.pool_count;
248 
249                     // get the data for the function call
250                     void (*funct)(void*) = self.function_pointer;
251                     void* param = self.parameter;
252                     self.function_pointer = 0;
253 
254                     // signal that the data is now empty
255                     self.data_empty.signal();
256 
257                     self.data_mutex.unlock();
258                     // Call funct with its intended parameter.  If this function throws then
259                     // we intentionally let the exception escape the thread and result in whatever
260                     // happens when it gets caught by the OS (generally the program is terminated).
261                     funct(param);
262                     self.call_end_handlers();
263 
264                     self.data_mutex.lock();
265 
266                     // indicate that this thread is now back in the thread pool
267                     ++self.pool_count;
268                 }
269 
270                 if (self.destruct == true)
271                     break;
272 
273                 // if we timed out and there isn't any work to do then
274                 // this thread will quit this loop and end.
275                 if (self.data_ready.wait_or_timeout(DLIB_THREAD_POOL_TIMEOUT) == false &&
276                     self.function_pointer == 0)
277                     break;
278 
279             }
280 
281             // remove this thread id from thread_ids
282             thread_id = get_thread_id();
283             self.thread_ids.destroy(thread_id);
284 
285             // indicate that this thread is now out of the thread pool
286             --self.pool_count;
287             --self.total_count;
288 
289             self.destructed.signal();
290 
291             } // end of auto_mutex M(self.data_mutex) block
292         }
293 
294     // ------------------------------------------------------------------------------------
295 
296     }
297 
298 // ----------------------------------------------------------------------------------------
299 
is_dlib_thread(thread_id_type id)300     bool is_dlib_thread (
301         thread_id_type id
302     )
303     {
304         return threads_kernel_shared::thread_pool().is_dlib_thread(id);
305     }
306 
is_dlib_thread()307     bool is_dlib_thread (
308     )
309     {
310         return is_dlib_thread(get_thread_id());
311     }
312 
313 // ----------------------------------------------------------------------------------------
314 
315 }
316 
317 #endif // DLIB_THREADS_KERNEL_SHARED_CPp_
318 
319