diff --git a/dlib/test/thread_pool.cpp b/dlib/test/thread_pool.cpp index bb1bde2c6..fb7a3d73f 100644 --- a/dlib/test/thread_pool.cpp +++ b/dlib/test/thread_pool.cpp @@ -67,12 +67,13 @@ namespace void perform_test ( ) { + add_functor f; for (int num_threads= 0; num_threads < 4; ++num_threads) { + future a, b, c, res; thread_pool tp(num_threads); print_spinner(); - future a, b, c, res; future obj; @@ -205,12 +206,12 @@ namespace a = 1; b = 2; res = 0; - add_functor f; tp.add_task(f, a, b, res); DLIB_TEST(a == 1); DLIB_TEST(b == 2); DLIB_TEST(res == 3); + global_var = 0; DLIB_TEST(global_var == 0); id = tp.add_task(&set_global_var); @@ -239,6 +240,9 @@ namespace } + + // add this task just to to perterb the thread pool before it goes out of scope + tp.add_task(f, a, b, res); } } diff --git a/dlib/threads/thread_pool_extension.cpp b/dlib/threads/thread_pool_extension.cpp index 90ca13dda..cd664fe7a 100644 --- a/dlib/threads/thread_pool_extension.cpp +++ b/dlib/threads/thread_pool_extension.cpp @@ -10,8 +10,8 @@ namespace dlib // ---------------------------------------------------------------------------------------- - thread_pool:: - thread_pool ( + thread_pool_implementation:: + thread_pool_implementation ( unsigned long num_threads ) : task_done_signaler(m), @@ -21,7 +21,7 @@ namespace dlib tasks.resize(num_threads); for (unsigned long i = 0; i < num_threads; ++i) { - register_thread(*this, &thread_pool::thread); + register_thread(*this, &thread_pool_implementation::thread); } start(); @@ -29,10 +29,33 @@ namespace dlib // ---------------------------------------------------------------------------------------- - thread_pool:: - ~thread_pool() + void thread_pool_implementation:: + shutdown_pool ( + ) { - {auto_mutex M(m); + { + auto_mutex M(m); + + // first wait for all pending tasks to finish + bool found_task = true; + while (found_task) + { + found_task = false; + for (unsigned long i = 0; i < tasks.size(); ++i) + { + // If task bucket i has a task that is currently supposed to be processed + if (tasks[i].is_empty() == false) + { + found_task = true; + break; + } + } + + if (found_task) + task_done_signaler.wait(); + } + + // now tell the threads to kill themselves we_are_destructing = true; task_ready_signaler.broadcast(); } @@ -42,7 +65,15 @@ namespace dlib // ---------------------------------------------------------------------------------------- - unsigned long thread_pool:: + thread_pool_implementation:: + ~thread_pool_implementation() + { + shutdown_pool(); + } + +// ---------------------------------------------------------------------------------------- + + unsigned long thread_pool_implementation:: num_threads_in_pool ( ) const { @@ -52,7 +83,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - void thread_pool:: + void thread_pool_implementation:: wait_for_task ( uint64 task_id ) const @@ -68,7 +99,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - void thread_pool:: + void thread_pool_implementation:: wait_for_all_tasks ( ) const { @@ -97,7 +128,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - bool thread_pool:: + bool thread_pool_implementation:: is_worker_thread ( const thread_id_type id ) const @@ -118,7 +149,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - void thread_pool:: + void thread_pool_implementation:: thread ( ) { @@ -175,7 +206,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - long thread_pool:: + long thread_pool_implementation:: find_empty_task_slot ( ) const { @@ -190,7 +221,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - long thread_pool:: + long thread_pool_implementation:: find_ready_task ( ) const { @@ -205,7 +236,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - uint64 thread_pool:: + uint64 thread_pool_implementation:: make_next_task_id ( long idx ) @@ -217,7 +248,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - unsigned long thread_pool:: + unsigned long thread_pool_implementation:: task_id_to_index ( uint64 id ) const @@ -227,7 +258,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - uint64 thread_pool:: + uint64 thread_pool_implementation:: add_task_internal ( const bfp_type& bfp ) @@ -270,7 +301,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - bool thread_pool:: + bool thread_pool_implementation:: is_task_thread ( ) const { diff --git a/dlib/threads/thread_pool_extension.h b/dlib/threads/thread_pool_extension.h index edbd4661a..5907b9fa8 100644 --- a/dlib/threads/thread_pool_extension.h +++ b/dlib/threads/thread_pool_extension.h @@ -11,13 +11,14 @@ #include "multithreaded_object_extension.h" #include "../uintn.h" #include "../array.h" +#include "../smart_pointers_thread_safe.h" namespace dlib { // ---------------------------------------------------------------------------------------- - class thread_pool; + class thread_pool_implementation; template < typename T @@ -27,29 +28,29 @@ namespace dlib /*! INITIAL VALUE - task_id == 0 - - tp == 0 + - tp.get() == 0 CONVENTION - - is_ready() == (tp == 0) + - is_ready() == (tp.get() == 0) - get() == var - - if (tp != 0) - - tp == a pointer to the thread_pool that is using this future object + - if (tp.get() != 0) + - tp == a pointer to the thread_pool_implementation that is using this future object - task_id == the task id of the task in the thread pool tp that is using this future object. !*/ public: future ( - ) : task_id(0), tp(0) {} + ) : task_id(0) {} future ( const T& item - ) : task_id(0), tp(0), var(item) {} + ) : task_id(0), var(item) {} future ( const future& item - ) :task_id(0), tp(0), var(item.get()) {} + ) :task_id(0), var(item.get()) {} ~future ( ) { wait(); } @@ -75,7 +76,7 @@ namespace dlib ) const { wait(); return var; } bool is_ready ( - ) const { return tp == 0; } + ) const { return tp.get() == 0; } private: @@ -84,7 +85,7 @@ namespace dlib inline void wait () const; mutable uint64 task_id; - mutable thread_pool* tp; + mutable shared_ptr_thread_safe tp; T var; }; @@ -123,7 +124,7 @@ namespace dlib // ---------------------------------------------------------------------------------------- - class thread_pool : private multithreaded_object + class thread_pool_implementation : private multithreaded_object { /*! CONVENTION @@ -141,12 +142,13 @@ namespace dlib !*/ typedef bound_function_pointer::kernel_1a_c bfp_type; - public: - explicit thread_pool ( + friend class thread_pool; + explicit thread_pool_implementation ( unsigned long num_threads ); - ~thread_pool( + public: + ~thread_pool_implementation( ); void wait_for_task ( @@ -294,425 +296,6 @@ namespace dlib return tasks[idx].task_id; } - // -------------------- - - template - uint64 add_task ( - F& function_object - ) - { - COMPILE_TIME_ASSERT(is_function::value == false); - COMPILE_TIME_ASSERT(is_pointer_type::value == false); - - bfp_type temp; - temp.set(function_object); - uint64 id = add_task_internal(temp); - - return id; - } - - template - uint64 add_task ( - const T& obj, - void (T::*funct)() const - ) - { - bfp_type temp; - temp.set(obj,funct); - uint64 id = add_task_internal(temp); - - return id; - } - - uint64 add_task ( - void (*funct)() - ) - { - bfp_type temp; - temp.set(funct); - uint64 id = add_task_internal(temp); - - return id; - } - - // -------------------- - - template - uint64 add_task ( - F& function_object, - future& arg1 - ) - { - COMPILE_TIME_ASSERT(is_function::value == false); - COMPILE_TIME_ASSERT(is_pointer_type::value == false); - - bfp_type temp; - temp.set(function_object,arg1.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - return id; - } - - template - uint64 add_task ( - T& obj, - void (T::*funct)(T1), - future& arg1 - ) - { - bfp_type temp; - temp.set(obj,funct,arg1.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - return id; - } - - template - uint64 add_task ( - const T& obj, - void (T::*funct)(T1) const, - future& arg1 - ) - { - bfp_type temp; - temp.set(obj,funct,arg1.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - return id; - } - - template - uint64 add_task ( - void (*funct)(T1), - future& arg1 - ) - { - bfp_type temp; - temp.set(funct,arg1.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - return id; - } - - // -------------------- - - template - uint64 add_task ( - F& function_object, - future& arg1, - future& arg2 - ) - { - COMPILE_TIME_ASSERT(is_function::value == false); - COMPILE_TIME_ASSERT(is_pointer_type::value == false); - - bfp_type temp; - temp.set(function_object, arg1.get(), arg2.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - return id; - } - - template - uint64 add_task ( - T& obj, - void (T::*funct)(T1,T2), - future& arg1, - future& arg2 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - return id; - } - - template - uint64 add_task ( - const T& obj, - void (T::*funct)(T1,T2) const, - future& arg1, - future& arg2 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - return id; - } - - template - uint64 add_task ( - void (*funct)(T1,T2), - future& arg1, - future& arg2 - ) - { - bfp_type temp; - temp.set(funct, arg1.get(), arg2.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - return id; - } - - // -------------------- - - template - uint64 add_task ( - F& function_object, - future& arg1, - future& arg2, - future& arg3 - ) - { - COMPILE_TIME_ASSERT(is_function::value == false); - COMPILE_TIME_ASSERT(is_pointer_type::value == false); - - bfp_type temp; - temp.set(function_object, arg1.get(), arg2.get(), arg3.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - return id; - } - - template - uint64 add_task ( - T& obj, - void (T::*funct)(T1,T2,T3), - future& arg1, - future& arg2, - future& arg3 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - return id; - } - - template - uint64 add_task ( - const T& obj, - void (T::*funct)(T1,T2,T3) const, - future& arg1, - future& arg2, - future& arg3 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - return id; - } - - template - uint64 add_task ( - void (*funct)(T1,T2,T3), - future& arg1, - future& arg2, - future& arg3 - ) - { - bfp_type temp; - temp.set(funct, arg1.get(), arg2.get(), arg3.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - return id; - } - - // -------------------- - - template - uint64 add_task ( - F& function_object, - future& arg1, - future& arg2, - future& arg3, - future& arg4 - ) - { - COMPILE_TIME_ASSERT(is_function::value == false); - COMPILE_TIME_ASSERT(is_pointer_type::value == false); - - bfp_type temp; - temp.set(function_object, arg1.get(), arg2.get(), arg3.get(), arg4.get()); - uint64 id = add_task_internal(temp); - - // tie the future to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - arg4.task_id = id; - arg4.tp = this; - return id; - } - - template - uint64 add_task ( - T& obj, - void (T::*funct)(T1,T2,T3,T4), - future& arg1, - future& arg2, - future& arg3, - future& arg4 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - arg4.task_id = id; - arg4.tp = this; - return id; - } - - template - uint64 add_task ( - const T& obj, - void (T::*funct)(T1,T2,T3,T4) const, - future& arg1, - future& arg2, - future& arg3, - future& arg4 - ) - { - bfp_type temp; - temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - arg4.task_id = id; - arg4.tp = this; - return id; - } - - template - uint64 add_task ( - void (*funct)(T1,T2,T3,T4), - future& arg1, - future& arg2, - future& arg3, - future& arg4 - ) - { - bfp_type temp; - temp.set(funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); - uint64 id = add_task_internal(temp); - - // tie the futures to this task - arg1.task_id = id; - arg1.tp = this; - arg2.task_id = id; - arg2.tp = this; - arg3.task_id = id; - arg3.tp = this; - arg4.task_id = id; - arg4.tp = this; - return id; - } - - // -------------------- - - private: - uint64 add_task_internal ( const bfp_type& bfp ); @@ -722,6 +305,16 @@ namespace dlib - returns the task id for this new task !*/ + void shutdown_pool ( + ); + /*! + ensures + - causes all threads to terminate and blocks the + caller until this happens. + !*/ + + private: + bool is_worker_thread ( const thread_id_type id ) const; @@ -841,6 +434,502 @@ namespace dlib signaler task_ready_signaler; bool we_are_destructing; + // restricted functions + thread_pool_implementation(thread_pool_implementation&); // copy constructor + thread_pool_implementation& operator=(thread_pool_implementation&); // assignment operator + + }; + + +// ---------------------------------------------------------------------------------------- + + class thread_pool + { + /*! + This object is just a shell that holds a shared_ptr_thread_safe + to the real thread_pool_implementation object. The reason for doing + it this way is so that we can allow any mixture of destruction orders + between thread_pool objects and futures. Whoever gets destroyed + last cleans up the thread_pool_implementation resources. + !*/ + typedef bound_function_pointer::kernel_1a_c bfp_type; + + public: + explicit thread_pool ( + unsigned long num_threads + ) + { + impl.reset(new thread_pool_implementation(num_threads)); + } + + ~thread_pool ( + ) + { + impl->shutdown_pool(); + } + + void wait_for_task ( + uint64 task_id + ) const { impl->wait_for_task(task_id); } + + unsigned long num_threads_in_pool ( + ) const { return impl->num_threads_in_pool(); } + + void wait_for_all_tasks ( + ) const { impl->wait_for_all_tasks(); } + + bool is_task_thread ( + ) const { return impl->is_task_thread(); } + + template + uint64 add_task ( + T& obj, + void (T::*funct)() + ) + { + return impl->add_task(obj, funct); + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(long), + long arg1 + ) + { + return impl->add_task(obj, funct, arg1); + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(long,long), + long arg1, + long arg2 + ) + { + return impl->add_task(obj, funct, arg1, arg2); + } + + // -------------------- + + template + uint64 add_task ( + F& function_object + ) + { + COMPILE_TIME_ASSERT(is_function::value == false); + COMPILE_TIME_ASSERT(is_pointer_type::value == false); + + bfp_type temp; + temp.set(function_object); + uint64 id = impl->add_task_internal(temp); + + return id; + } + + template + uint64 add_task ( + const T& obj, + void (T::*funct)() const + ) + { + bfp_type temp; + temp.set(obj,funct); + uint64 id = impl->add_task_internal(temp); + + return id; + } + + uint64 add_task ( + void (*funct)() + ) + { + bfp_type temp; + temp.set(funct); + uint64 id = impl->add_task_internal(temp); + + return id; + } + + // -------------------- + + template + uint64 add_task ( + F& function_object, + future& arg1 + ) + { + COMPILE_TIME_ASSERT(is_function::value == false); + COMPILE_TIME_ASSERT(is_pointer_type::value == false); + + bfp_type temp; + temp.set(function_object,arg1.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + return id; + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(T1), + future& arg1 + ) + { + bfp_type temp; + temp.set(obj,funct,arg1.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + return id; + } + + template + uint64 add_task ( + const T& obj, + void (T::*funct)(T1) const, + future& arg1 + ) + { + bfp_type temp; + temp.set(obj,funct,arg1.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + return id; + } + + template + uint64 add_task ( + void (*funct)(T1), + future& arg1 + ) + { + bfp_type temp; + temp.set(funct,arg1.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + return id; + } + + // -------------------- + + template + uint64 add_task ( + F& function_object, + future& arg1, + future& arg2 + ) + { + COMPILE_TIME_ASSERT(is_function::value == false); + COMPILE_TIME_ASSERT(is_pointer_type::value == false); + + bfp_type temp; + temp.set(function_object, arg1.get(), arg2.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + return id; + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(T1,T2), + future& arg1, + future& arg2 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + return id; + } + + template + uint64 add_task ( + const T& obj, + void (T::*funct)(T1,T2) const, + future& arg1, + future& arg2 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + return id; + } + + template + uint64 add_task ( + void (*funct)(T1,T2), + future& arg1, + future& arg2 + ) + { + bfp_type temp; + temp.set(funct, arg1.get(), arg2.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + return id; + } + + // -------------------- + + template + uint64 add_task ( + F& function_object, + future& arg1, + future& arg2, + future& arg3 + ) + { + COMPILE_TIME_ASSERT(is_function::value == false); + COMPILE_TIME_ASSERT(is_pointer_type::value == false); + + bfp_type temp; + temp.set(function_object, arg1.get(), arg2.get(), arg3.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + return id; + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(T1,T2,T3), + future& arg1, + future& arg2, + future& arg3 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + return id; + } + + template + uint64 add_task ( + const T& obj, + void (T::*funct)(T1,T2,T3) const, + future& arg1, + future& arg2, + future& arg3 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + return id; + } + + template + uint64 add_task ( + void (*funct)(T1,T2,T3), + future& arg1, + future& arg2, + future& arg3 + ) + { + bfp_type temp; + temp.set(funct, arg1.get(), arg2.get(), arg3.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + return id; + } + + // -------------------- + + template + uint64 add_task ( + F& function_object, + future& arg1, + future& arg2, + future& arg3, + future& arg4 + ) + { + COMPILE_TIME_ASSERT(is_function::value == false); + COMPILE_TIME_ASSERT(is_pointer_type::value == false); + + bfp_type temp; + temp.set(function_object, arg1.get(), arg2.get(), arg3.get(), arg4.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the future to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + arg4.task_id = id; + arg4.tp = impl; + return id; + } + + template + uint64 add_task ( + T& obj, + void (T::*funct)(T1,T2,T3,T4), + future& arg1, + future& arg2, + future& arg3, + future& arg4 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + arg4.task_id = id; + arg4.tp = impl; + return id; + } + + template + uint64 add_task ( + const T& obj, + void (T::*funct)(T1,T2,T3,T4) const, + future& arg1, + future& arg2, + future& arg3, + future& arg4 + ) + { + bfp_type temp; + temp.set(obj, funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + arg4.task_id = id; + arg4.tp = impl; + return id; + } + + template + uint64 add_task ( + void (*funct)(T1,T2,T3,T4), + future& arg1, + future& arg2, + future& arg3, + future& arg4 + ) + { + bfp_type temp; + temp.set(funct, arg1.get(), arg2.get(), arg3.get(), arg4.get()); + uint64 id = impl->add_task_internal(temp); + + // tie the futures to this task + arg1.task_id = id; + arg1.tp = impl; + arg2.task_id = id; + arg2.tp = impl; + arg3.task_id = id; + arg3.tp = impl; + arg4.task_id = id; + arg4.tp = impl; + return id; + } + + private: + + shared_ptr_thread_safe impl; + // restricted functions thread_pool(thread_pool&); // copy constructor thread_pool& operator=(thread_pool&); // assignment operator @@ -858,7 +947,7 @@ namespace dlib if (tp) { tp->wait_for_task(task_id); - tp = 0; + tp.reset(); task_id = 0; } } diff --git a/dlib/threads/thread_pool_extension_abstract.h b/dlib/threads/thread_pool_extension_abstract.h index 1c9ce5457..ff6da1ba6 100644 --- a/dlib/threads/thread_pool_extension_abstract.h +++ b/dlib/threads/thread_pool_extension_abstract.h @@ -76,11 +76,6 @@ namespace dlib ~future ( ); /*! - requires - - if (item.is_ready() == false) then - - The thread_pool that this future was passed to should still exist - (i.e. You can't pass a future to a thread_pool and then destruct the - thread_pool before you destruct the future). ensures - if (item.is_ready() == false) then - the call to this function blocks until the thread processing the task related @@ -223,6 +218,12 @@ namespace dlib mode any thread that calls add_task() is considered to be a thread_pool thread capable of executing tasks. + Also note that all function objects are passed to the tasks + by reference. This means you should ensure that your function + objects are not destroyed while tasks are still using them. + (e.g. Don't let them go out of scope right after a call to + add_task()) + EXCEPTIONS Note that if an exception is thrown inside a task thread and is not caught then the normal rule for uncaught exceptions in @@ -248,7 +249,7 @@ namespace dlib ); /*! ensures - - all resources allocated by *this have been freed. + - blocks until all tasks in the pool have finished. !*/ bool is_task_thread (