Teh first one
[mldemos:kalians-mldemos.git] / _AlgorithmsPlugins / KernelMethods / dlib / threads / threads_kernel_shared.cpp
1 // Copyright (C) 2003  Davis E. King (davis@dlib.net)\r
2 // License: Boost Software License   See LICENSE.txt for the full license.\r
3 #ifndef DLIB_THREADS_KERNEL_SHARED_CPp_\r
4 #define DLIB_THREADS_KERNEL_SHARED_CPp_\r
5 \r
6 #include "threads_kernel_shared.h"\r
7 #include "../assert.h"\r
8 #include <iostream>\r
9 \r
10 \r
11 #ifndef DLIB_THREAD_POOL_TIMEOUT\r
12 // default to 30000 milliseconds\r
13 #define DLIB_THREAD_POOL_TIMEOUT 30000\r
14 #endif\r
15 \r
16 namespace dlib\r
17 {\r
18 \r
19 // ----------------------------------------------------------------------------------------\r
20 // ----------------------------------------------------------------------------------------\r
21 // threader functions\r
22 // ----------------------------------------------------------------------------------------\r
23 // ----------------------------------------------------------------------------------------\r
24 \r
25     namespace threads_kernel_shared \r
26     {\r
27 \r
28         bool thread_pool_has_been_destroyed = false;\r
29 \r
30 // ----------------------------------------------------------------------------------------\r
31 \r
32         threader& thread_pool (\r
33         ) \r
34         {\r
35             static threader* thread_pool = new threader;\r
36             return *thread_pool;\r
37         }\r
38 \r
39 // ----------------------------------------------------------------------------------------\r
40 \r
41         struct threader_destruct_helper\r
42         {\r
43             // cause the thread pool to begin its destruction process when \r
44             // global objects start to be destroyed\r
45             ~threader_destruct_helper()\r
46             {\r
47                 thread_pool().destruct_if_ready();\r
48             }\r
49         };\r
50         static threader_destruct_helper a;\r
51 \r
52 // ----------------------------------------------------------------------------------------\r
53 \r
54         bool threader::\r
55         is_dlib_thread (\r
56             thread_id_type id\r
57         )\r
58         {\r
59             auto_mutex M(data_mutex);\r
60             return thread_ids.is_member(id);\r
61         }\r
62 \r
63 // ----------------------------------------------------------------------------------------\r
64 \r
65         threader::\r
66         threader (\r
67         ) :\r
68             total_count(0),\r
69             function_pointer(0),\r
70             pool_count(0),\r
71             data_ready(data_mutex),\r
72             data_empty(data_mutex),\r
73             destruct(false),\r
74             destructed(data_mutex)\r
75         {}\r
76 \r
77 // ----------------------------------------------------------------------------------------\r
78 \r
79         threader::\r
80         ~threader (\r
81         )\r
82         { \r
83             data_mutex.lock();\r
84             destruct = true;\r
85             data_ready.broadcast();\r
86 \r
87             // wait for all the threads to end\r
88             while (total_count > 0)\r
89                 destructed.wait();\r
90 \r
91             thread_pool_has_been_destroyed = true;\r
92             data_mutex.unlock();\r
93         }\r
94 \r
95 // ----------------------------------------------------------------------------------------\r
96 \r
97         void threader::\r
98         destruct_if_ready (\r
99         )\r
100         {\r
101             data_mutex.lock();\r
102 \r
103             // if there aren't any active threads, just maybe some sitting around\r
104             // in the pool then just destroy the threader\r
105             if (total_count == pool_count)\r
106             {\r
107                 destruct = true;\r
108                 data_ready.broadcast();\r
109                 data_mutex.unlock();\r
110                 delete this;\r
111             }\r
112             else\r
113             {\r
114                 // There are still some user threads running so there isn't\r
115                 // much we can really do.  Just let the program end without\r
116                 // cleaning up threading resources.  \r
117                 data_mutex.unlock();\r
118             }\r
119         }\r
120 \r
121 // ----------------------------------------------------------------------------------------\r
122 \r
123         void threader::\r
124         call_end_handlers (\r
125         )\r
126         {\r
127             reg.m.lock();\r
128             const thread_id_type id = get_thread_id();\r
129             thread_id_type id_copy;\r
130             member_function_pointer<>::kernel_1a mfp;\r
131 \r
132             // Remove all the member function pointers for this thread from the tree \r
133             // and call them.\r
134             while (reg.reg[id] != 0)\r
135             {\r
136                 reg.reg.remove(id,id_copy,mfp);\r
137                 reg.m.unlock();\r
138                 mfp();\r
139                 reg.m.lock();\r
140             }\r
141             reg.m.unlock();\r
142         }\r
143 \r
144     // ------------------------------------------------------------------------------------\r
145 \r
146         bool threader::\r
147         create_new_thread (\r
148             void (*funct)(void*),\r
149             void* param\r
150         )\r
151         {\r
152 \r
153             // get a lock on the data mutex\r
154             auto_mutex M(data_mutex);\r
155 \r
156             // loop to ensure that the new function poitner is in the data\r
157             while (true)\r
158             {\r
159                 // if the data is empty then add new data and quit loop\r
160                 if (function_pointer == 0)\r
161                 {\r
162                     parameter = param;\r
163                     function_pointer = funct;\r
164                     break;\r
165                 }\r
166                 else\r
167                 {\r
168                     // wait for data to become empty\r
169                     data_empty.wait();\r
170                 }\r
171             }\r
172 \r
173 \r
174             // get a thread for this new data\r
175             // if a new thread must be crated\r
176             if (pool_count == 0)\r
177             {\r
178                 // make thread and add it to the pool\r
179                 if ( threads_kernel_shared_helpers::spawn_thread(thread_starter, this) == false )\r
180                 {\r
181                     function_pointer = 0;\r
182                     parameter = 0;\r
183                     data_empty.signal();\r
184                     return false;\r
185                 }\r
186                 ++total_count;\r
187             }\r
188             // wake up a thread from the pool\r
189             else\r
190             {\r
191                 data_ready.signal();\r
192             }\r
193 \r
194             return true;\r
195         }\r
196 \r
197     // ------------------------------------------------------------------------------------\r
198 \r
199         void thread_starter (\r
200             void* object\r
201         )\r
202         {\r
203             // get a reference to the calling threader object\r
204             threader& self = *reinterpret_cast<threader*>(object);\r
205 \r
206 \r
207             {\r
208             auto_mutex M(self.data_mutex);\r
209 \r
210             // add this thread id\r
211             thread_id_type thread_id = get_thread_id();\r
212             self.thread_ids.add(thread_id);\r
213 \r
214             // indicate that this thread is now in the thread pool\r
215             ++self.pool_count;\r
216 \r
217             while (self.destruct == false)\r
218             {\r
219                 // if data is ready then process it and launch the thread\r
220                 // if its not ready then go back into the pool\r
221                 while (self.function_pointer != 0)\r
222                 {                \r
223                     // indicate that this thread is now out of the thread pool\r
224                     --self.pool_count;\r
225 \r
226                     // get the data for the function call\r
227                     void (*funct)(void*) = self.function_pointer;\r
228                     void* param = self.parameter;\r
229                     self.function_pointer = 0;\r
230 \r
231                     // signal that the data is now empty\r
232                     self.data_empty.signal();\r
233 \r
234                     self.data_mutex.unlock();\r
235                     // Call funct with its intended parameter.  If this function throws then\r
236                     // we intentionally let the exception escape the thread and result in whatever\r
237                     // happens when it gets caught by the OS (generally the program is terminated).\r
238                     funct(param);\r
239                     self.call_end_handlers();\r
240 \r
241                     self.data_mutex.lock();\r
242 \r
243                     // indicate that this thread is now back in the thread pool\r
244                     ++self.pool_count;\r
245                 }\r
246 \r
247                 if (self.destruct == true)\r
248                     break;\r
249 \r
250                 // if we timed out and there isn't any work to do then\r
251                 // this thread will quit this loop and end.\r
252                 if (self.data_ready.wait_or_timeout(DLIB_THREAD_POOL_TIMEOUT) == false && \r
253                     self.function_pointer == 0)\r
254                     break;\r
255 \r
256             }\r
257 \r
258             // remove this thread id from thread_ids\r
259             thread_id = get_thread_id();\r
260             self.thread_ids.destroy(thread_id);\r
261 \r
262             // indicate that this thread is now out of the thread pool\r
263             --self.pool_count;\r
264             --self.total_count;\r
265 \r
266             self.destructed.signal();\r
267 \r
268             } // end of auto_mutex M(self.data_mutex) block\r
269         }\r
270 \r
271     // ------------------------------------------------------------------------------------\r
272 \r
273     }\r
274 \r
275 // ----------------------------------------------------------------------------------------\r
276 \r
277     bool is_dlib_thread (\r
278         thread_id_type id\r
279     )\r
280     {\r
281         return threads_kernel_shared::thread_pool().is_dlib_thread(id);\r
282     }\r
283 \r
284     bool is_dlib_thread (\r
285     )\r
286     {\r
287         return is_dlib_thread(get_thread_id());\r
288     }\r
289 \r
290 // ----------------------------------------------------------------------------------------\r
291 \r
292 }\r
293 \r
294 #endif // DLIB_THREADS_KERNEL_SHARED_CPp_\r
295 \r