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