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