Commit 8a3ff4ff authored by Davis King's avatar Davis King

Added an add_task_by_value() function to the thread_pool.

parent cf6f55c2
...@@ -260,7 +260,8 @@ namespace dlib ...@@ -260,7 +260,8 @@ namespace dlib
uint64 thread_pool_implementation:: uint64 thread_pool_implementation::
add_task_internal ( add_task_internal (
const bfp_type& bfp const bfp_type& bfp,
shared_ptr<function_object_copy>& item
) )
{ {
auto_mutex M(m); auto_mutex M(m);
...@@ -293,6 +294,7 @@ namespace dlib ...@@ -293,6 +294,7 @@ namespace dlib
tasks[idx].thread_id = my_thread_id; tasks[idx].thread_id = my_thread_id;
tasks[idx].task_id = make_next_task_id(idx); tasks[idx].task_id = make_next_task_id(idx);
tasks[idx].bfp = bfp; tasks[idx].bfp = bfp;
tasks[idx].function_copy.swap(item);
task_ready_signaler.signal(); task_ready_signaler.signal();
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "../uintn.h" #include "../uintn.h"
#include "../array.h" #include "../array.h"
#include "../smart_pointers_thread_safe.h" #include "../smart_pointers_thread_safe.h"
#include "../smart_pointers.h"
namespace dlib namespace dlib
{ {
...@@ -296,9 +297,34 @@ namespace dlib ...@@ -296,9 +297,34 @@ namespace dlib
return tasks[idx].task_id; return tasks[idx].task_id;
} }
struct function_object_copy
{
virtual ~function_object_copy(){}
};
template <typename T>
struct function_object_copy_instance : function_object_copy
{
function_object_copy_instance(const T& item_) : item(item_) {}
T item;
virtual ~function_object_copy_instance(){}
};
uint64 add_task_internal ( uint64 add_task_internal (
const bfp_type& bfp const bfp_type& bfp,
shared_ptr<function_object_copy>& item
); );
/*!
ensures
- adds a task to call the given bfp object.
- swaps item into the internal task object which will have a lifetime
at least as long as the running task.
- returns the task id for this new task
!*/
uint64 add_task_internal (
const bfp_type& bfp
) { shared_ptr<function_object_copy> temp; return add_task_internal(bfp, temp); }
/*! /*!
ensures ensures
- adds a task to call the given bfp object. - adds a task to call the given bfp object.
...@@ -424,10 +450,12 @@ namespace dlib ...@@ -424,10 +450,12 @@ namespace dlib
member_function_pointer<long,long>::kernel_1a mfp2; member_function_pointer<long,long>::kernel_1a mfp2;
bfp_type bfp; bfp_type bfp;
shared_ptr<function_object_copy> function_copy;
}; };
array<task_state_type>::expand_1d_c tasks; array<task_state_type>::expand_1d tasks;
array<thread_id_type>::expand_1d_c worker_thread_ids; array<thread_id_type>::expand_1d worker_thread_ids;
mutex m; mutex m;
signaler task_done_signaler; signaler task_done_signaler;
...@@ -528,6 +556,23 @@ namespace dlib ...@@ -528,6 +556,23 @@ namespace dlib
return id; return id;
} }
template <typename F>
uint64 add_task_by_value (
const F& function_object
)
{
thread_pool_implementation::function_object_copy_instance<F>* ptr = 0;
ptr = new thread_pool_implementation::function_object_copy_instance<F>(function_object);
shared_ptr<thread_pool_implementation::function_object_copy> function_copy(ptr);
bfp_type temp;
temp.set(ptr->item);
uint64 id = impl->add_task_internal(temp, function_copy);
return id;
}
template <typename T> template <typename T>
uint64 add_task ( uint64 add_task (
const T& obj, const T& obj,
...@@ -573,6 +618,26 @@ namespace dlib ...@@ -573,6 +618,26 @@ namespace dlib
return id; return id;
} }
template <typename F, typename A1>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1
)
{
thread_pool_implementation::function_object_copy_instance<F>* ptr = 0;
ptr = new thread_pool_implementation::function_object_copy_instance<F>(function_object);
shared_ptr<thread_pool_implementation::function_object_copy> function_copy(ptr);
bfp_type temp;
temp.set(ptr->item, arg1.get());
uint64 id = impl->add_task_internal(temp, function_copy);
// tie the future to this task
arg1.task_id = id;
arg1.tp = impl;
return id;
}
template <typename T, typename T1, typename A1> template <typename T, typename T1, typename A1>
uint64 add_task ( uint64 add_task (
T& obj, T& obj,
...@@ -647,6 +712,29 @@ namespace dlib ...@@ -647,6 +712,29 @@ namespace dlib
return id; return id;
} }
template <typename F, typename A1, typename A2>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2
)
{
thread_pool_implementation::function_object_copy_instance<F>* ptr = 0;
ptr = new thread_pool_implementation::function_object_copy_instance<F>(function_object);
shared_ptr<thread_pool_implementation::function_object_copy> function_copy(ptr);
bfp_type temp;
temp.set(ptr->item, arg1.get(), arg2.get());
uint64 id = impl->add_task_internal(temp, function_copy);
// tie the future to this task
arg1.task_id = id;
arg1.tp = impl;
arg2.task_id = id;
arg2.tp = impl;
return id;
}
template <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2> typename T2, typename A2>
uint64 add_task ( uint64 add_task (
...@@ -736,6 +824,32 @@ namespace dlib ...@@ -736,6 +824,32 @@ namespace dlib
return id; return id;
} }
template <typename F, typename A1, typename A2, typename A3>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
)
{
thread_pool_implementation::function_object_copy_instance<F>* ptr = 0;
ptr = new thread_pool_implementation::function_object_copy_instance<F>(function_object);
shared_ptr<thread_pool_implementation::function_object_copy> function_copy(ptr);
bfp_type temp;
temp.set(ptr->item, arg1.get(), arg2.get(), arg3.get());
uint64 id = impl->add_task_internal(temp, function_copy);
// 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 <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2, typename T2, typename A2,
typename T3, typename A3> typename T3, typename A3>
...@@ -840,6 +954,35 @@ namespace dlib ...@@ -840,6 +954,35 @@ namespace dlib
return id; return id;
} }
template <typename F, typename A1, typename A2, typename A3, typename A4>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
)
{
thread_pool_implementation::function_object_copy_instance<F>* ptr = 0;
ptr = new thread_pool_implementation::function_object_copy_instance<F>(function_object);
shared_ptr<thread_pool_implementation::function_object_copy> function_copy(ptr);
bfp_type temp;
temp.set(ptr->item, arg1.get(), arg2.get(), arg3.get(), arg4.get());
uint64 id = impl->add_task_internal(temp, function_copy);
// 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 <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2, typename T2, typename A2,
typename T3, typename A3, typename T3, typename A3,
......
...@@ -77,9 +77,9 @@ namespace dlib ...@@ -77,9 +77,9 @@ namespace dlib
); );
/*! /*!
ensures ensures
- if (item.is_ready() == false) then - if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related - the call to this function blocks until the thread processing the task related
to the item future has finished. to this future has finished.
!*/ !*/
bool is_ready ( bool is_ready (
...@@ -218,17 +218,16 @@ namespace dlib ...@@ -218,17 +218,16 @@ namespace dlib
mode any thread that calls add_task() is considered to be mode any thread that calls add_task() is considered to be
a thread_pool thread capable of executing tasks. a thread_pool thread capable of executing tasks.
Also note that all function objects are passed to the tasks This object is also implemented such that no memory allocations occur
by reference. This means you should ensure that your function after the thread pool has been constructed (the future object also
objects are not destroyed while tasks are still using them. doesn't perform any memory allocations or contain any system
(e.g. Don't let them go out of scope right after a call to resources such as mutex objects) so long as the user doesn't call
add_task()) any of the add_task_by_value() routines.
EXCEPTIONS EXCEPTIONS
Note that if an exception is thrown inside a task thread and Note that if an exception is thrown inside a task thread and
is not caught then the normal rule for uncaught exceptions in is not caught then the normal rule for uncaught exceptions in
threads applies. That is, the application will be terminated threads applies. That is, the application will be terminated.
and the text of the exception will be printed to standard error.
!*/ !*/
public: public:
...@@ -271,6 +270,25 @@ namespace dlib ...@@ -271,6 +270,25 @@ namespace dlib
the maximum number of tasks that this object will process concurrently. the maximum number of tasks that this object will process concurrently.
!*/ !*/
template <typename F>
uint64 add_task_by_value (
const F& function_object
);
/*!
requires
- function_object() is a valid expression
ensures
- makes a copy of function_object, call it FCOPY.
- if (is_task_thread() == true and there aren't any free threads available) then
- calls FCOPY() within the calling thread and returns when it finishes
- else
- the call to this function blocks until there is a free thread in the pool
to process this new task. Once a free thread is available the task
is handed off to that thread which then calls FCOPY().
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T> template <typename T>
uint64 add_task ( uint64 add_task (
T& obj, T& obj,
...@@ -279,6 +297,9 @@ namespace dlib ...@@ -279,6 +297,9 @@ namespace dlib
/*! /*!
requires requires
- funct == a valid member function pointer for class T - funct == a valid member function pointer for class T
- obj will not go out of scope until after the task has completed (i.e.
this function passes obj to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)() within the calling thread and returns - calls (obj.*funct)() within the calling thread and returns
...@@ -300,6 +321,9 @@ namespace dlib ...@@ -300,6 +321,9 @@ namespace dlib
/*! /*!
requires requires
- funct == a valid member function pointer for class T - funct == a valid member function pointer for class T
- obj will not go out of scope until after the task has completed (i.e.
this function passes obj to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)(arg1) within the calling thread and returns - calls (obj.*funct)(arg1) within the calling thread and returns
...@@ -322,6 +346,9 @@ namespace dlib ...@@ -322,6 +346,9 @@ namespace dlib
/*! /*!
requires requires
- funct == a valid member function pointer for class T - funct == a valid member function pointer for class T
- obj will not go out of scope until after the task has completed (i.e.
this function passes obj to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)(arg1,arg2) within the calling thread and returns - calls (obj.*funct)(arg1,arg2) within the calling thread and returns
...@@ -365,6 +392,9 @@ namespace dlib ...@@ -365,6 +392,9 @@ namespace dlib
requires requires
- function_object(arg1.get()) is a valid expression - function_object(arg1.get()) is a valid expression
(i.e. The A1 type stored in the future must be a type that can be passed into the given function object) (i.e. The A1 type stored in the future must be a type that can be passed into the given function object)
- function_object will not go out of scope until after the task has completed (i.e.
this function passes function_object to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls function_object(arg1.get()) within the calling thread and returns - calls function_object(arg1.get()) within the calling thread and returns
...@@ -378,6 +408,28 @@ namespace dlib ...@@ -378,6 +408,28 @@ namespace dlib
for the submitted task to finish. for the submitted task to finish.
!*/ !*/
template <typename F, typename A1>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1
);
/*!
requires
- function_object(arg1.get()) is a valid expression
(i.e. The A1 type stored in the future must be a type that can be passed into the given function object)
ensures
- makes a copy of function_object, call it FCOPY.
- if (is_task_thread() == true and there aren't any free threads available) then
- calls FCOPY(arg1.get()) within the calling thread and returns when it finishes
- else
- the call to this function blocks until there is a free thread in the pool
to process this new task. Once a free thread is available the task
is handed off to that thread which then calls FCOPY(arg1.get()).
- #arg1.is_ready() == false
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T, typename T1, typename A1> template <typename T, typename T1, typename A1>
uint64 add_task ( uint64 add_task (
T& obj, T& obj,
...@@ -389,6 +441,9 @@ namespace dlib ...@@ -389,6 +441,9 @@ namespace dlib
- funct == a valid member function pointer for class T - funct == a valid member function pointer for class T
- (obj.*funct)(arg1.get()) must be a valid expression. - (obj.*funct)(arg1.get()) must be a valid expression.
(i.e. The A1 type stored in the future must be a type that can be passed into the given function) (i.e. The A1 type stored in the future must be a type that can be passed into the given function)
- obj will not go out of scope until after the task has completed (i.e.
this function passes obj to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)(arg1.get()) within the calling thread and returns - calls (obj.*funct)(arg1.get()) within the calling thread and returns
...@@ -413,6 +468,9 @@ namespace dlib ...@@ -413,6 +468,9 @@ namespace dlib
- funct == a valid member function pointer for class T - funct == a valid member function pointer for class T
- (obj.*funct)(arg1.get()) must be a valid expression. - (obj.*funct)(arg1.get()) must be a valid expression.
(i.e. The A1 type stored in the future must be a type that can be passed into the given function) (i.e. The A1 type stored in the future must be a type that can be passed into the given function)
- obj will not go out of scope until after the task has completed (i.e.
this function passes obj to the task by reference. If you want to avoid
this restriction then use add_task_by_value())
ensures ensures
- if (is_task_thread() == true and there aren't any free threads available) then - if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)(arg1.get()) within the calling thread and returns - calls (obj.*funct)(arg1.get()) within the calling thread and returns
...@@ -450,9 +508,9 @@ namespace dlib ...@@ -450,9 +508,9 @@ namespace dlib
!*/ !*/
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// The remainder of this class just contains overloads for add_task() that take up // The remainder of this class just contains overloads for add_task() and add_task_by_value()
// to 4 futures (as well as 0 futures). Their behavior is identical to the above // that take up to 4 futures (as well as 0 futures). Their behavior is identical to the above
// add_task() functions. // add_task() and add_task_by_value() functions.
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
template <typename F, typename A1, typename A2> template <typename F, typename A1, typename A2>
...@@ -462,6 +520,13 @@ namespace dlib ...@@ -462,6 +520,13 @@ namespace dlib
future<A2>& arg2 future<A2>& arg2
); );
template <typename F, typename A1, typename A2>
uint64 add_task_by_value (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2
);
template <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2> typename T2, typename A2>
uint64 add_task ( uint64 add_task (
...@@ -498,6 +563,14 @@ namespace dlib ...@@ -498,6 +563,14 @@ namespace dlib
future<A3>& arg3 future<A3>& arg3
); );
template <typename F, typename A1, typename A2, typename A3>
uint64 add_task (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
template <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2, typename T2, typename A2,
typename T3, typename A3> typename T3, typename A3>
...@@ -541,6 +614,15 @@ namespace dlib ...@@ -541,6 +614,15 @@ namespace dlib
future<A4>& arg4 future<A4>& arg4
); );
template <typename F, typename A1, typename A2, typename A3, typename A4>
uint64 add_task (
const F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
template <typename T, typename T1, typename A1, template <typename T, typename T1, typename A1,
typename T2, typename A2, typename T2, typename A2,
typename T3, typename A3, typename T3, typename A3,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment