Lesson 35 - Get Compute Auth Token Working

This commit is contained in:
Norman Lansing
2026-02-28 12:32:28 -05:00
parent 1d477ee42a
commit 4fde462bce
7743 changed files with 1397833 additions and 18 deletions

View File

@@ -0,0 +1,118 @@
// Copyright (C) 2016 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_AsYNC_Hh_
#define DLIB_AsYNC_Hh_
// C++11 things don't work in old versions of visual studio
#if !defined( _MSC_VER) || _MSC_VER >= 1900
#include "async_abstract.h"
#include "thread_pool_extension.h"
#include <future>
#include <functional>
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace impl
{
template <typename T> struct selector {};
template <typename T, typename U, typename V>
void call_prom_set_value(
T& prom,
U& fun,
selector<V>
)
{
prom.set_value(fun());
}
template <typename T, typename U>
void call_prom_set_value(
T& prom,
U& fun,
selector<void>
)
{
fun();
prom.set_value();
}
template <typename> struct result_of;
#if (__cplusplus >= 201703L || \
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) && \
__cpp_lib_is_invocable >= 201703L
template <typename F, typename... Args>
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
#else
template <typename F, typename... Args>
struct result_of<F(Args...)>
: std::result_of<F&&(Args&&...)> {};
#endif
}
// ----------------------------------------------------------------------------------------
thread_pool& default_thread_pool();
// ----------------------------------------------------------------------------------------
template <
typename Function,
typename ...Args
>
std::future<typename impl::result_of<Function(Args...)>::type> async(
thread_pool& tp,
Function&& f,
Args&&... args
)
{
auto prom = std::make_shared<std::promise<typename impl::result_of<Function(Args...)>::type>>();
std::future<typename impl::result_of<Function(Args...)>::type> ret = prom->get_future();
using bind_t = decltype(std::bind(std::forward<Function>(f), std::forward<Args>(args)...));
auto fun = std::make_shared<bind_t>(std::bind(std::forward<Function>(f), std::forward<Args>(args)...));
tp.add_task_by_value([fun, prom]()
{
try
{
impl::call_prom_set_value(*prom, *fun, impl::selector<typename impl::result_of<Function(Args...)>::type>());
}
catch(...)
{
prom->set_exception(std::current_exception());
}
});
return ret;
}
// ----------------------------------------------------------------------------------------
template <
typename Function,
typename ...Args
>
std::future<typename impl::result_of<Function(Args...)>::type> async(
Function&& f,
Args&&... args
)
{
return async(default_thread_pool(), std::forward<Function>(f), std::forward<Args>(args)...);
}
}
// ----------------------------------------------------------------------------------------
#ifdef NO_MAKEFILE
#include "async.cpp"
#endif
#endif
#endif // DLIB_AsYNC_Hh_

View File

@@ -0,0 +1,67 @@
// Copyright (C) 2016 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_AsYNC_ABSTRACT_Hh_
#ifdef DLIB_AsYNC_ABSTRACT_Hh_
#include "thread_pool_extension_abstract.h"
#include <future>
#include <functional>
namespace dlib
{
// ----------------------------------------------------------------------------------------
thread_pool& default_thread_pool(
);
/*!
ensures
- returns a reference to a global thread_pool. If the DLIB_NUM_THREADS
environment variable is set to an integer then the thread pool will contain
DLIB_NUM_THREADS threads, otherwise it will contain
std::thread::hardware_concurrency() threads.
!*/
// ----------------------------------------------------------------------------------------
template <
typename Function,
typename ...Args
>
std::future<typename std::result_of<Function(Args...)>::type> async(
thread_pool& tp,
Function&& f,
Args&&... args
);
/*!
requires
- f must be a function and f(args...) must be a valid expression.
ensures
- This function behaves just like std::async(std::launch::async, f, args)
except that instead of spawning a new thread to process each task it submits
the task to the provided dlib::thread_pool. Therefore, dlib::async() is
guaranteed to use a bounded number of threads unlike std::async(). This also
means that calls to dlib::async() will block if there aren't any free threads
in the thread pool.
!*/
// ----------------------------------------------------------------------------------------
template <
typename Function,
typename ...Args
>
std::future<typename std::result_of<Function(Args...)>::type> async(
Function&& f,
Args&&... args
);
/*!
ensures
- Calling this function is equivalent to directly calling async(default_thread_pool(), f, args...)
!*/
}
// ----------------------------------------------------------------------------------------
#endif // DLIB_AsYNC_ABSTRACT_Hh_

View File

@@ -0,0 +1,180 @@
// Copyright (C) 2005 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_AUTO_MUTEX_EXTENSIOn_
#define DLIB_AUTO_MUTEX_EXTENSIOn_
#include "threads_kernel.h"
#include "rmutex_extension.h"
#include "read_write_mutex_extension.h"
#include "auto_mutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class auto_mutex
{
/*!
INITIAL VALUE
- if (m != 0) then
- the mutex pointed to by m is locked
- if (r != 0) then
- the mutex pointed to by r is locked
- if (rw != 0) then
- the mutex pointed to by rw is locked
- exactly one of r, m, or rw is not 0.
CONVENTION
- if (m != 0) then
- the mutex pointed to by m is locked
- if (r != 0) then
- the mutex pointed to by r is locked
- if (rw != 0) then
- the mutex pointed to by rw is locked
- exactly one of r, m, or rw is not 0.
!*/
public:
explicit auto_mutex (
const mutex& m_
) : m(&m_),
r(0),
rw(0)
{
m->lock();
}
explicit auto_mutex (
const rmutex& r_
) : m(0),
r(&r_),
rw(0)
{
r->lock();
}
explicit auto_mutex (
const read_write_mutex& rw_
) : m(0),
r(0),
rw(&rw_)
{
rw->lock();
}
void unlock()
{
if (m != 0)
{
m->unlock();
m = 0;
}
else if (r != 0)
{
r->unlock();
r = 0;
}
else if (rw != 0)
{
rw->unlock();
rw = 0;
}
}
~auto_mutex (
)
{
unlock();
}
private:
const mutex* m;
const rmutex* r;
const read_write_mutex* rw;
// restricted functions
auto_mutex(auto_mutex&); // copy constructor
auto_mutex& operator=(auto_mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class auto_mutex_readonly
{
public:
explicit auto_mutex_readonly (
const read_write_mutex& rw_
) : rw(rw_), _has_write_lock(false), _has_read_lock(true)
{
rw.lock_readonly();
}
~auto_mutex_readonly (
)
{
unlock();
}
void lock_readonly (
)
{
if (!_has_read_lock)
{
unlock();
rw.lock_readonly();
_has_read_lock = true;
}
}
void lock_write (
)
{
if (!_has_write_lock)
{
unlock();
rw.lock();
_has_write_lock = true;
}
}
void unlock (
)
{
if (_has_write_lock)
{
rw.unlock();
_has_write_lock = false;
}
else if (_has_read_lock)
{
rw.unlock_readonly();
_has_read_lock = false;
}
}
bool has_read_lock (
) { return _has_read_lock; }
bool has_write_lock (
) { return _has_write_lock; }
private:
const read_write_mutex& rw;
bool _has_write_lock;
bool _has_read_lock;
// restricted functions
auto_mutex_readonly(auto_mutex_readonly&); // copy constructor
auto_mutex_readonly& operator=(auto_mutex_readonly&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_AUTO_MUTEX_EXTENSIOn_

View File

@@ -0,0 +1,185 @@
// Copyright (C) 2005 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_AUTO_MUTEX_EXTENSIOn_ABSTRACT_
#ifdef DLIB_AUTO_MUTEX_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
#include "rmutex_extension_abstract.h"
#include "read_write_mutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class auto_mutex
{
/*!
INITIAL VALUE
The mutex given in the constructor is locked and associated with this
object.
WHAT THIS OBJECT REPRESENTS
This object represents a mechanism for automatically locking and unlocking
a mutex object.
!*/
public:
explicit auto_mutex (
const mutex& m
);
/*!
ensures
- #*this is properly initialized
- m will be locked
!*/
explicit auto_mutex (
const rmutex& m
);
/*!
ensures
- #*this is properly initialized
- m will be locked
!*/
explicit auto_mutex (
const read_write_mutex& m
);
/*!
ensures
- #*this is properly initialized
- m will be locked via m.lock() (i.e. a write lock will be obtained)
!*/
void unlock(
);
/*!
ensures
- if (unlock() has not already been called) then
- The mutex associated with *this has been unlocked. This is useful if
you want to unlock a mutex before the auto_mutex destructor executes.
!*/
~auto_mutex (
);
/*!
ensures
- all resources allocated by *this have been freed
- calls unlock()
!*/
private:
// restricted functions
auto_mutex(auto_mutex&); // copy constructor
auto_mutex& operator=(auto_mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class auto_mutex_readonly
{
/*!
INITIAL VALUE
The mutex given in the constructor is locked using a read-only lock and
associated with this object.
WHAT THIS OBJECT REPRESENTS
This object represents a mechanism for automatically locking and unlocking
a read_write_mutex object. In particular, a readonly lock is used.
!*/
public:
explicit auto_mutex_readonly (
const read_write_mutex& m
);
/*!
ensures
- #*this is properly initialized
- a readonly lock will be obtained on m using m.lock_readonly()
- #has_read_lock() == true
!*/
~auto_mutex_readonly (
);
/*!
ensures
- all resources allocated by *this have been freed
- the mutex associated with *this has been unlocked
!*/
bool has_read_lock (
);
/*!
ensures
- returns true if this object has called read_write_mutex::lock_readonly()
on its associated mutex and has yet to release that lock.
!*/
bool has_write_lock (
);
/*!
ensures
- returns true if this object has called read_write_mutex::lock() on its
associated mutex and has yet to release that lock.
!*/
void lock_readonly (
);
/*!
ensures
- This function converts the lock on the associated mutex into a readonly lock.
Specifically:
if (!has_read_lock()) then
- if (has_write_lock()) then
- unlocks the associated mutex and then relocks it by calling
read_write_mutex::lock_readonly()
- else
- locks the associated mutex by calling read_write_mutex::lock_readonly()
- #has_read_lock() == true
- Note that the lock switch is not atomic. This means that whatever
resource is protected by the mutex might have been modified during the
call to lock_readonly().
!*/
void lock_write (
);
/*!
ensures
- This function converts the lock on the associated mutex into a write lock.
Specifically:
if (!has_write_lock()) then
- if (has_read_lock()) then
- unlocks the associated mutex and then relocks it by calling
read_write_mutex::lock()
- else
- locks the associated mutex by calling read_write_mutex::lock()
- #has_write_lock() == true
- Note that the lock switch is not atomic. This means that whatever
resource is protected by the mutex might have been modified during the
call to lock_write().
!*/
void unlock (
);
/*!
ensures
- if (has_read_lock() || has_write_lock()) then
- unlocks the associated mutex. This is useful if you want to unlock a
mutex before the auto_mutex_readonly destructor executes.
- #has_read_lock() == false
- #has_write_lock() == false
!*/
private:
// restricted functions
auto_mutex_readonly(auto_mutex_readonly&); // copy constructor
auto_mutex_readonly& operator=(auto_mutex_readonly&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_AUTO_MUTEX_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,116 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_AUTO_UNLOCK_EXTENSIOn_
#define DLIB_AUTO_UNLOCK_EXTENSIOn_
#include "threads_kernel.h"
#include "rmutex_extension.h"
#include "read_write_mutex_extension.h"
#include "auto_unlock_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class auto_unlock
{
/*!
INITIAL VALUE
- if (m != 0) then
- the mutex pointed to by m is locked
- if (r != 0) then
- the mutex pointed to by r is locked
- if (rw != 0) then
- the mutex pointed to by rw is locked
- exactly one of r, m, or rw is not 0.
CONVENTION
- if (m != 0) then
- the mutex pointed to by m is locked
- if (r != 0) then
- the mutex pointed to by r is locked
- if (rw != 0) then
- the mutex pointed to by rw is locked
- exactly one of r, m, or rw is not 0.
!*/
public:
explicit auto_unlock (
const mutex& m_
) : m(&m_),
r(0),
rw(0)
{}
explicit auto_unlock (
const rmutex& r_
) : m(0),
r(&r_),
rw(0)
{}
explicit auto_unlock (
const read_write_mutex& rw_
) : m(0),
r(0),
rw(&rw_)
{}
~auto_unlock (
)
{
if (m != 0)
m->unlock();
else if (r != 0)
r->unlock();
else
rw->unlock();
}
private:
const mutex* m;
const rmutex* r;
const read_write_mutex* rw;
// restricted functions
auto_unlock(auto_unlock&); // copy constructor
auto_unlock& operator=(auto_unlock&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class auto_unlock_readonly
{
public:
explicit auto_unlock_readonly (
const read_write_mutex& rw_
) :
rw(rw_)
{}
~auto_unlock_readonly (
)
{
rw.unlock_readonly();
}
private:
const read_write_mutex& rw;
// restricted functions
auto_unlock_readonly(auto_unlock_readonly&); // copy constructor
auto_unlock_readonly& operator=(auto_unlock_readonly&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_AUTO_UNLOCK_EXTENSIOn_

View File

@@ -0,0 +1,116 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_AUTO_UNLOCK_EXTENSIOn_ABSTRACT_
#ifdef DLIB_AUTO_UNLOCK_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
#include "rmutex_extension_abstract.h"
#include "read_write_mutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class auto_unlock
{
/*!
INITIAL VALUE
The mutex given in the constructor is associated with this object.
WHAT THIS OBJECT REPRESENTS
This object represents a mechanism for automatically unlocking
a mutex object. It is useful when you already have a locked mutex
and want to make sure it gets unlocked even if an exception is thrown
or you quit the function at a weird spot.
!*/
public:
explicit auto_unlock (
const mutex& m
);
/*!
ensures
- #*this is properly initialized
- does not modify m in any way
!*/
explicit auto_unlock (
const rmutex& m
);
/*!
ensures
- #*this is properly initialized
- does not modify m in any way
!*/
explicit auto_unlock (
const read_write_mutex& m
);
/*!
ensures
- #*this is properly initialized
- does not modify m in any way
!*/
~auto_unlock (
);
/*!
ensures
- all resources allocated by *this have been freed
- calls unlock() on the mutex associated with *this
!*/
private:
// restricted functions
auto_unlock(auto_unlock&); // copy constructor
auto_unlock& operator=(auto_unlock&); // assignment operator
};
// ----------------------------------------------------------------------------------------
class auto_unlock_readonly
{
/*!
INITIAL VALUE
The mutex given in the constructor is associated with this object.
WHAT THIS OBJECT REPRESENTS
This object represents a mechanism for automatically unlocking
a read_write_mutex object. It is useful when you already have a locked mutex
and want to make sure it gets unlocked even if an exception is thrown
or you quit the function at a weird spot. Note that the mutex
is unlocked by calling unlock_readonly() on it.
!*/
public:
explicit auto_unlock_readonly (
const read_write_mutex& m
);
/*!
ensures
- #*this is properly initialized
- does not modify m in any way
!*/
~auto_unlock_readonly (
);
/*!
ensures
- all resources allocated by *this have been freed
- calls unlock_readonly() on the mutex associated with *this
!*/
private:
// restricted functions
auto_unlock_readonly(auto_unlock_readonly&); // copy constructor
auto_unlock_readonly& operator=(auto_unlock_readonly&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_AUTO_UNLOCK_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_CREATE_NEW_THREAD_EXTENSIOn_
#define DLIB_CREATE_NEW_THREAD_EXTENSIOn_
#include "threads_kernel_abstract.h"
#include "create_new_thread_extension_abstract.h"
#include "../threads.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T,
void (T::*funct)()
>
inline void dlib_create_new_thread_helper (
void* obj
)
{
T* o = static_cast<T*>(obj);
(o->*funct)();
}
// ----------------------------------------------------------------------------------------
template <
typename T,
void (T::*funct)()
>
inline bool create_new_thread (
T& obj
)
{
return create_new_thread(dlib_create_new_thread_helper<T,funct>,&obj);
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_CREATE_NEW_THREAD_EXTENSIOn_

View File

@@ -0,0 +1,33 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_CREATE_NEW_THREAD_EXTENSIOn_ABSTRACT_
#ifdef DLIB_CREATE_NEW_THREAD_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T,
void (T::*funct)()
>
bool create_new_thread (
T& obj
);
/*!
ensures
- creates a new thread and calls obj.*funct() from it.
- returns true upon success and false upon failure to create the new thread.
!*/
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_CREATE_NEW_THREAD_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,153 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_MULTITHREADED_OBJECT_EXTENSIOn_
#define DLIB_MULTITHREADED_OBJECT_EXTENSIOn_
#include "multithreaded_object_extension_abstract.h"
#include "threads_kernel.h"
#include "auto_mutex_extension.h"
#include "rmutex_extension.h"
#include "rsignaler_extension.h"
#include "../algs.h"
#include "../assert.h"
#include "../map.h"
#include "../member_function_pointer.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class multithreaded_object
{
/*!
INITIAL VALUE
- is_running_ == false
- should_stop_ == false
- thread_ids.size() == 0
- dead_threads.size() == 0
- threads_started == 0
CONVENTION
- number_of_threads_registered() == thread_ids.size() + dead_threads.size()
- number_of_threads_alive() == threads_started
- is_running() == is_running_
- should_stop() == should_stop_
- thread_ids == a map of current thread ids to the member function
pointers that that thread runs.
- threads_started == the number of threads that have been spawned to run
thread_helper but haven't ended yet.
- dead_threads == a queue that contains all the member function pointers
for threads that are currently registered but not running
- m_ == the mutex used to protect all our variables
- s == the signaler for m_
!*/
public:
multithreaded_object (
);
virtual ~multithreaded_object (
) = 0;
void clear (
);
bool is_running (
) const;
unsigned long number_of_threads_alive (
) const;
unsigned long number_of_threads_registered (
) const;
void wait (
) const;
void start (
);
void pause (
);
void stop (
);
protected:
bool should_stop (
) const;
template <
typename T
>
void register_thread (
T& object,
void (T::*thread)()
)
{
auto_mutex M(m_);
try
{
mfp mf;
mf.set(object,thread);
dead_threads.enqueue(mf);
if (is_running_)
start();
}
catch (...)
{
is_running_ = false;
should_stop_ = true;
s.broadcast();
throw;
}
}
private:
class raii_thread_helper
{
public:
raii_thread_helper(multithreaded_object& self_, thread_id_type id_);
~raii_thread_helper();
multithreaded_object& self;
thread_id_type id;
};
void thread_helper(
);
typedef member_function_pointer<> mfp;
rmutex m_;
rsignaler s;
map<thread_id_type,mfp,memory_manager<char>::kernel_2a>::kernel_1a thread_ids;
queue<mfp,memory_manager<char>::kernel_2a>::kernel_1a dead_threads;
bool is_running_;
bool should_stop_;
unsigned long threads_started;
// restricted functions
multithreaded_object(multithreaded_object&); // copy constructor
multithreaded_object& operator=(multithreaded_object&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "multithreaded_object_extension.cpp"
#endif
#endif // DLIB_MULTITHREADED_OBJECT_EXTENSIOn_

View File

@@ -0,0 +1,186 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_MULTITHREADED_OBJECT_EXTENSIOn_ABSTRACT_
#ifdef DLIB_MULTITHREADED_OBJECT_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class multithreaded_object
{
/*!
INITIAL VALUE
- is_running() == false
- number_of_threads_alive() == 0
- number_of_threads_registered() == 0
WHAT THIS OBJECT REPRESENTS
This object represents a multithreaded object. It is similar to
the threaded_object except it allows you to have many threads in a
single object rather than just one. To use it you inherit from it
and register the member functions in your new class that you want
to run in their own threads by calling register_thread(). Then when
you call start() it will spawn all the registered functions
in their own threads.
!*/
public:
multithreaded_object (
);
/*!
ensures
- #*this is properly initialized
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
virtual ~multithreaded_object (
) = 0;
/*!
requires
- number_of_threads_alive() == 0
(i.e. in the destructor for the object you derive from this one you
must wait for all the threads to end.)
ensures
- all resources allocated by *this have been freed.
!*/
void clear(
);
/*!
ensures
- #*this has its initial value
- blocks until all threads have terminated
throws
- std::bad_alloc or dlib::thread_error
if an exception is thrown then *this is unusable
until clear() is called and succeeds
!*/
bool is_running (
) const;
/*!
ensures
- if (number_of_threads_alive() > 0 && the threads are currently supposed to be executing) then
- returns true
- else
- returns false
!*/
unsigned long number_of_threads_alive (
) const;
/*!
ensures
- returns the number of threads that are currently alive (i.e.
the number of threads that have started but not yet terminated)
!*/
unsigned long number_of_threads_registered (
) const;
/*!
ensures
- returns the number of threads that have been registered by
calls to register_thread()
!*/
void wait (
) const;
/*!
requires
- is not called from one of this object's threads
ensures
- if (number_of_threads_alive() > 0) then
- blocks until all the threads in this object have terminated
(i.e. blocks until number_of_threads_alive() == 0)
!*/
void start (
);
/*!
ensures
- #number_of_threads_alive() == number_of_threads_registered()
- #is_running() == true
- #should_stop() == false
- all the threads registered are up and running.
throws
- std::bad_alloc or dlib::thread_error
If either of these exceptions are thrown then
#is_running() == false and should_stop() == true
!*/
void pause (
);
/*!
ensures
- #is_running() == false
!*/
void stop (
);
/*!
ensures
- #should_stop() == true
- #is_running() == false
!*/
protected:
template <
typename T
>
void register_thread (
T& object,
void (T::*thread)()
);
/*!
requires
- (object.*thread)() forms a valid function call
- the thread function does not throw
ensures
- registers the member function pointed to by thread as one of the threads
that runs when is_running() == true
- #number_of_threads_registered() == number_of_threads_registered() + 1
- if (is_running() == true)
- spawns this new member function in its own thread
- #number_of_threads_alive() += number_of_threads_alive() + 1
throws
- std::bad_alloc or dlib::thread_error
If either of these exceptions are thrown then
#is_running() == false and should_stop() == true
!*/
bool should_stop (
) const;
/*!
requires
- is only called from one of the registered threads in this object
ensures
- if (is_running() == false && should_stop() == false) then
- blocks until (#is_running() == true || #should_stop() == true)
- if (this thread is supposed to terminate) then
- returns true
- else
- returns false
!*/
private:
// restricted functions
multithreaded_object(multithreaded_object&); // copy constructor
multithreaded_object& operator=(multithreaded_object&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_MULTITHREADED_OBJECT_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,676 @@
// Copyright (C) 2013 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_PARALLEL_FoR_Hh_
#define DLIB_PARALLEL_FoR_Hh_
#include "parallel_for_extension_abstract.h"
#include "thread_pool_extension.h"
#include "../console_progress_indicator.h"
#include "async.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace impl
{
template <typename T>
class helper_parallel_for
{
public:
helper_parallel_for (
T& obj_,
void (T::*funct_)(long)
) :
obj(obj_),
funct(funct_)
{}
T& obj;
void (T::*funct)(long);
void process_block (long begin, long end)
{
for (long i = begin; i < end; ++i)
(obj.*funct)(i);
}
};
template <typename T>
class helper_parallel_for_funct
{
public:
helper_parallel_for_funct (
const T& funct_
) : funct(funct_) {}
const T& funct;
void run(long i)
{
funct(i);
}
};
template <typename T>
class helper_parallel_for_funct2
{
public:
helper_parallel_for_funct2 (
const T& funct_
) : funct(funct_) {}
const T& funct;
void run(long begin, long end)
{
funct(begin, end);
}
};
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long, long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
if (tp.num_threads_in_pool() != 0)
{
const long num = end-begin;
const long num_workers = static_cast<long>(tp.num_threads_in_pool());
// How many samples to process in a single task (aim for chunks_per_thread jobs per worker)
const long block_size = std::max(1L, num/(num_workers*chunks_per_thread));
for (long i = 0; i < num; i+=block_size)
{
tp.add_task(obj, funct, begin+i, begin+std::min(i+block_size, num));
}
tp.wait_for_all_tasks();
}
else
{
// Since there aren't any threads in the pool we might as well just invoke
// the function directly since that's all the thread_pool object would do.
// But doing it ourselves skips a mutex lock.
(obj.*funct)(begin, end);
}
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long, long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
thread_pool tp(num_threads);
parallel_for_blocked(tp, begin, end, obj, funct, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::helper_parallel_for_funct2<T> helper(funct);
parallel_for_blocked(tp, begin, end, helper, &impl::helper_parallel_for_funct2<T>::run, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
thread_pool tp(num_threads);
parallel_for_blocked(tp, begin, end, funct, chunks_per_thread);
}
template <typename T>
void parallel_for_blocked (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
parallel_for_blocked(default_thread_pool(), begin, end, funct, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::helper_parallel_for<T> helper(obj, funct);
parallel_for_blocked(tp, begin, end, helper, &impl::helper_parallel_for<T>::process_block, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
thread_pool tp(num_threads);
parallel_for(tp, begin, end, obj, funct, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::helper_parallel_for_funct<T> helper(funct);
parallel_for(tp, begin, end, helper, &impl::helper_parallel_for_funct<T>::run, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
thread_pool tp(num_threads);
parallel_for(tp, begin, end, funct, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
parallel_for(default_thread_pool(), begin, end, funct, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
namespace impl
{
template <typename T>
class parfor_verbose_helper
{
public:
parfor_verbose_helper(T& obj_, void (T::*funct_)(long), long begin, long end) :
obj(obj_), funct(funct_), pbar(end-begin)
{
count = 0;
wrote_to_screen = pbar.print_status(0);
}
~parfor_verbose_helper()
{
if (wrote_to_screen)
std::cout << std::endl;
}
mutable long count;
T& obj;
void (T::*funct)(long);
mutable console_progress_indicator pbar;
mutable bool wrote_to_screen;
mutex m;
void operator()(long i) const
{
(obj.*funct)(i);
{
auto_mutex lock(m);
wrote_to_screen = pbar.print_status(++count) || wrote_to_screen;
}
}
};
template <typename T>
class parfor_verbose_helper3
{
public:
parfor_verbose_helper3(T& obj_, void (T::*funct_)(long,long), long begin, long end) :
obj(obj_), funct(funct_), pbar(end-begin)
{
count = 0;
wrote_to_screen = pbar.print_status(0);
}
~parfor_verbose_helper3()
{
if (wrote_to_screen)
std::cout << std::endl;
}
mutable long count;
T& obj;
void (T::*funct)(long,long);
mutable console_progress_indicator pbar;
mutable bool wrote_to_screen;
mutex m;
void operator()(long begin, long end) const
{
(obj.*funct)(begin, end);
{
auto_mutex lock(m);
count += end-begin;
wrote_to_screen = pbar.print_status(count) || wrote_to_screen;
}
}
};
template <typename T>
class parfor_verbose_helper2
{
public:
parfor_verbose_helper2(const T& obj_, long begin, long end) : obj(obj_), pbar(end-begin)
{
count = 0;
wrote_to_screen = pbar.print_status(0);
}
~parfor_verbose_helper2()
{
if (wrote_to_screen)
std::cout << std::endl;
}
mutable long count;
const T& obj;
mutable console_progress_indicator pbar;
mutable bool wrote_to_screen;
mutex m;
void operator()(long i) const
{
obj(i);
{
auto_mutex lock(m);
wrote_to_screen = pbar.print_status(++count) || wrote_to_screen;
}
}
void operator()(long begin, long end) const
{
obj(begin, end);
{
auto_mutex lock(m);
count += end-begin;
wrote_to_screen = pbar.print_status(count) || wrote_to_screen;
}
}
};
}
template <typename T>
void parallel_for_verbose (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper<T> helper(obj, funct, begin, end);
parallel_for(tp, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper<T> helper(obj, funct, begin, end);
parallel_for(num_threads, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for(tp, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for(num_threads, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for(begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long,long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper3<T> helper(obj, funct, begin, end);
parallel_for_blocked(tp, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long,long),
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper3<T> helper(obj, funct, begin, end);
parallel_for_blocked(num_threads, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for_blocked(tp, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for_blocked(num_threads, begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
)
{
// make sure requires clause is not broken
DLIB_ASSERT(begin <= end && chunks_per_thread > 0,
"\t void parallel_for_blocked_verbose()"
<< "\n\t Invalid inputs were given to this function"
<< "\n\t begin: " << begin
<< "\n\t end: " << end
<< "\n\t chunks_per_thread: " << chunks_per_thread
);
impl::parfor_verbose_helper2<T> helper(funct, begin, end);
parallel_for_blocked(begin, end, helper, chunks_per_thread);
}
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_PARALLEL_FoR_Hh_

View File

@@ -0,0 +1,469 @@
// Copyright (C) 2013 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_PARALLEL_FoR_ABSTRACT_Hh_
#ifdef DLIB_PARALLEL_FoR_ABSTRACT_Hh_
#include "thread_pool_extension_abstract.h"
#include "async_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long, long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This is a convenience function for submitting a block of jobs to a thread_pool.
In particular, given the half open range [begin, end), this function will
split the range into approximately tp.num_threads_in_pool()*chunks_per_thread
blocks, which it will then submit to the thread_pool. The given thread_pool
will then call (obj.*funct)() on each of the subranges.
- To be precise, suppose we have broken the range [begin, end) into the
following subranges:
- [begin[0], end[0])
- [begin[1], end[1])
- [begin[2], end[2])
...
- [begin[n], end[n])
Then parallel_for_blocked() submits each of these subranges to tp for
processing such that (obj.*funct)(begin[i], end[i]) is invoked for all valid
values of i. Moreover, the subranges are non-overlapping and completely
cover the total range of [begin, end).
- This function will not perform any memory allocations or create any system
resources such as mutex objects.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long, long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
thread_pool tp(num_threads);
parallel_for_blocked(tp, begin, end, obj, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- chunks_per_thread > 0
- begin <= end
ensures
- This is a convenience function for submitting a block of jobs to a
thread_pool. In particular, given the range [begin, end), this function will
split the range into approximately tp.num_threads_in_pool()*chunks_per_thread
blocks, which it will then submit to the thread_pool. The given thread_pool
will then call funct() on each of the subranges.
- To be precise, suppose we have broken the range [begin, end) into the
following subranges:
- [begin[0], end[0])
- [begin[1], end[1])
- [begin[2], end[2])
...
- [begin[n], end[n])
Then parallel_for_blocked() submits each of these subranges to tp for
processing such that funct(begin[i], end[i]) is invoked for all valid values
of i.
- This function will not perform any memory allocations or create any system
resources such as mutex objects.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
thread_pool tp(num_threads);
parallel_for_blocked(tp, begin, end, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
parallel_for_blocked(default_thread_pool(), begin, end, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following function call:
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
{
for (long i = begin_sub; i < end_sub; ++i)
(obj.*funct)(i);
}, chunks_per_thread);
- Therefore, this routine invokes (obj.*funct)(i) for all i in the range
[begin, end). However, it does so using tp.num_threads_in_pool() parallel
threads.
- This function will not perform any memory allocations or create any system
resources such as mutex objects.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
thread_pool tp(num_threads);
parallel_for(tp, begin, end, obj, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following function call:
parallel_for_blocked(tp, begin, end, [&](long begin_sub, long end_sub)
{
for (long i = begin_sub; i < end_sub; ++i)
funct(i);
}, chunks_per_thread);
- Therefore, this routine invokes funct(i) for all i in the range [begin, end).
However, it does so using tp.num_threads_in_pool() parallel threads.
- This function will not perform any memory allocations or create any system
resources such as mutex objects.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
thread_pool tp(num_threads);
parallel_for(tp, begin, end, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is equivalent to the following block of code:
parallel_for(default_thread_pool(), begin, end, funct, chunks_per_thread);
!*/
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for() routine defined above except
that it will print messages to cout showing the progress in executing the
parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for() routine defined above except
that it will print messages to cout showing the progress in executing the
parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for() routine defined above except
that it will print messages to cout showing the progress in executing the
parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for() routine defined above except
that it will print messages to cout showing the progress in executing the
parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_verbose (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for() routine defined above except
that it will print messages to cout showing the progress in executing the
parallel for loop.
- It will also use the default_thread_pool().
!*/
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
thread_pool& tp,
long begin,
long end,
T& obj,
void (T::*funct)(long,long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for_blocked() routine defined
above except that it will print messages to cout showing the progress in
executing the parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
unsigned long num_threads,
long begin,
long end,
T& obj,
void (T::*funct)(long,long),
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for_blocked() routine defined
above except that it will print messages to cout showing the progress in
executing the parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
thread_pool& tp,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for_blocked() routine defined
above except that it will print messages to cout showing the progress in
executing the parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
unsigned long num_threads,
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for_blocked() routine defined
above except that it will print messages to cout showing the progress in
executing the parallel for loop.
!*/
// ----------------------------------------------------------------------------------------
template <typename T>
void parallel_for_blocked_verbose (
long begin,
long end,
const T& funct,
long chunks_per_thread = 8
);
/*!
requires
- begin <= end
- chunks_per_thread > 0
ensures
- This function is identical to the parallel_for_blocked() routine defined
above except that it will print messages to cout showing the progress in
executing the parallel for loop.
- It will also use the default_thread_pool()
!*/
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_PARALLEL_FoR_ABSTRACT_Hh_

View File

@@ -0,0 +1,6 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_1_
#include "threads_kernel_2.h"
#endif

View File

@@ -0,0 +1,177 @@
// Copyright (C) 2010 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_READ_WRITE_MUTEX_EXTENSIOn_
#define DLIB_READ_WRITE_MUTEX_EXTENSIOn_
#include "threads_kernel.h"
#include "read_write_mutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class read_write_mutex
{
/*!
INITIAL VALUE
- max_locks == defined by constructor
- available_locks == max_locks
- write_lock_in_progress == false
- write_lock_active == false
CONVENTION
- Each time someone gets a read only lock they take one of the "available locks"
and each write lock takes all possible locks (i.e. max_locks). The number of
available locks is recorded in available_locks. Any time you try to lock this
object and there aren't available locks you have to wait.
- max_locks == max_readonly_locks()
- if (some thread is on the process of obtaining a write lock) then
- write_lock_in_progress == true
- else
- write_lock_in_progress == false
- if (some thread currently has a write lock on this mutex) then
- write_lock_active == true
- else
- write_lock_active == false
!*/
public:
read_write_mutex (
) : s(m),
max_locks(0xFFFFFFFF),
available_locks(max_locks),
write_lock_in_progress(false),
write_lock_active(false)
{}
explicit read_write_mutex (
unsigned long max_locks_
) : s(m),
max_locks(max_locks_),
available_locks(max_locks_),
write_lock_in_progress(false),
write_lock_active(false)
{
// make sure requires clause is not broken
DLIB_ASSERT(max_locks > 0,
"\t read_write_mutex::read_write_mutex(max_locks)"
<< "\n\t You must give a non-zero value for max_locks"
<< "\n\t this: " << this
);
}
~read_write_mutex (
)
{}
void lock (
) const
{
m.lock();
// If another write lock is already in progress then wait for it to finish
// before we start trying to grab all the available locks. This way we
// don't end up fighting over the locks.
while (write_lock_in_progress)
s.wait();
// grab the right to perform a write lock
write_lock_in_progress = true;
// now start grabbing all the locks
unsigned long locks_obtained = available_locks;
available_locks = 0;
while (locks_obtained != max_locks)
{
s.wait();
locks_obtained += available_locks;
available_locks = 0;
}
write_lock_in_progress = false;
write_lock_active = true;
m.unlock();
}
void unlock (
) const
{
m.lock();
// only do something if there really was a lock in place
if (write_lock_active)
{
available_locks = max_locks;
write_lock_active = false;
s.broadcast();
}
m.unlock();
}
void lock_readonly (
) const
{
m.lock();
while (available_locks == 0)
s.wait();
--available_locks;
m.unlock();
}
void unlock_readonly (
) const
{
m.lock();
// If this condition is false then it means there are no more readonly locks
// to free. So we don't do anything.
if (available_locks != max_locks && !write_lock_active)
{
++available_locks;
// only perform broadcast when there is another thread that might be listening
if (available_locks == 1 || write_lock_in_progress)
{
s.broadcast();
}
}
m.unlock();
}
unsigned long max_readonly_locks (
) const
{
return max_locks;
}
private:
mutex m;
signaler s;
const unsigned long max_locks;
mutable unsigned long available_locks;
mutable bool write_lock_in_progress;
mutable bool write_lock_active;
// restricted functions
read_write_mutex(read_write_mutex&); // copy constructor
read_write_mutex& operator=(read_write_mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_READ_WRITE_MUTEX_EXTENSIOn_

View File

@@ -0,0 +1,146 @@
// Copyright (C) 2010 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_READWRITE_MUTEX_EXTENSIOn_ABSTRACT_
#ifdef DLIB_READWRITE_MUTEX_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class read_write_mutex
{
/*!
INITIAL VALUE
read_write_mutex is in the fully unlocked state
WHAT THIS OBJECT REPRESENTS
This object represents a mutex intended to be used for synchronous
thread control of shared data. When a thread wants to access some
shared data it locks out other threads by calling lock() and calls
unlock() when it is finished.
This mutex also has the additional ability to distinguish between
a lock for the purposes of modifying some shared data, a write lock,
and a lock for the purposes of only reading shared data, a readonly
lock. The lock() and unlock() functions are used for write locks while
the lock_readonly() and unlock_readonly() are for readonly locks.
The difference between a readonly and write lock can be understood as
follows. The read_write_mutex will allow many threads to obtain simultaneous
readonly locks but will only allow a single thread to obtain a write lock.
Moreover, while the write lock is obtained no other threads are allowed
to have readonly locks.
!*/
public:
read_write_mutex (
);
/*!
ensures
- #*this is properly initialized
- max_readonly_locks() == 0xFFFFFFFF
(i.e. about 4 billion)
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the read_write_mutex.
!*/
explicit read_write_mutex (
unsigned long max_locks
);
/*!
requires
- max_locks > 0
ensures
- #*this is properly initialized
- max_readonly_locks() == max_locks
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the read_write_mutex.
!*/
~read_write_mutex (
);
/*!
requires
- *this is not locked
ensures
- all resources allocated by *this have been freed
!*/
void lock (
) const;
/*!
requires
- The thread calling this function does not have any kind of lock on this
object
ensures
- if (there is any kind of lock on *this) then
- the calling thread is put to sleep until a write lock becomes available.
Once available, a write lock is obtained on this mutex and this function
terminates.
- else
- a write lock is obtained on this mutex and the calling thread is not put to sleep
!*/
void unlock (
) const;
/*!
ensures
- if (there is a write lock on *this) then
- #*this is unlocked (i.e. other threads may now lock this object)
- else
- the call to unlock() has no effect
!*/
unsigned long max_readonly_locks (
) const;
/*!
ensures
- returns the maximum number of concurrent readonly locks this object will allow.
!*/
void lock_readonly (
) const;
/*!
requires
- The thread calling this function does not already have a write
lock on this object
ensures
- if (there is a write lock on *this or there are no free readonly locks) then
- the calling thread is put to sleep until there is no longer a write lock
and a free readonly lock is available. Once this is the case, a readonly
lock is obtained and this function terminates.
- else
- a readonly lock is obtained on *this and the calling thread is not put
to sleep. Note that multiple readonly locks can be obtained at once.
!*/
void unlock_readonly (
) const;
/*!
ensures
- if (there is a readonly lock on *this) then
- one readonly lock is removed from *this.
- else
- the call to unlock_readonly() has no effect.
!*/
private:
// restricted functions
read_write_mutex(read_write_mutex&); // copy constructor
read_write_mutex& operator=(read_write_mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_READWRITE_MUTEX_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,109 @@
// Copyright (C) 2005 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_RMUTEX_EXTENSIOn_
#define DLIB_RMUTEX_EXTENSIOn_
#include "threads_kernel.h"
#include "rmutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class rmutex
{
/*!
INITIAL VALUE
count == 0
thread_id == 0
CONVENTION
- count == lock_count()
- if (no thread currently has a lock on this mutex) then
- count == 0
- else
- count == the number of times the thread that owns this mutex has
called lock()
- thread_id == the id of this thread.
!*/
public:
rmutex (
) : s(m),
thread_id(0),
count(0)
{}
~rmutex (
)
{}
unsigned long lock_count (
) const
{
return count;
}
void lock (
unsigned long times = 1
) const
{
const thread_id_type current_thread_id = get_thread_id();
m.lock();
if (thread_id == current_thread_id)
{
// we already own this mutex in this case
count += times;
}
else
{
// wait for our turn to claim this rmutex
while (count != 0)
s.wait();
count = times;
thread_id = current_thread_id;
}
m.unlock();
}
void unlock (
unsigned long times = 1
) const
{
const thread_id_type current_thread_id = get_thread_id();
m.lock();
if (thread_id == current_thread_id)
{
if (count <= times)
{
count = 0;
s.signal();
}
else
{
count -= times;
}
}
m.unlock();
}
private:
mutex m;
signaler s;
mutable thread_id_type thread_id;
mutable unsigned long count;
// restricted functions
rmutex(rmutex&); // copy constructor
rmutex& operator=(rmutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_RMUTEX_EXTENSIOn_

View File

@@ -0,0 +1,107 @@
// Copyright (C) 2005 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_RMUTEX_EXTENSIOn_ABSTRACT_
#ifdef DLIB_RMUTEX_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class rmutex
{
/*!
INITIAL VALUE
rmutex is in the unlocked state
WHAT THIS OBJECT REPRESENTS
This object represents a recursive mutex intended to be used for synchronous
thread control of shared data. When a thread wants to access some
shared data it locks out other threads by calling lock() and calls
unlock() when it is finished.
The difference between this and the normal mutex object is that it is safe to
call lock() from a thread that already has a lock on this mutex. Doing
so just increments a counter but otherwise has no effect on the mutex.
Note that unlock() must be called for each call to lock() to release the
mutex.
!*/
public:
rmutex (
);
/*!
ensures
- #*this is properly initialized
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the rmutex.
!*/
~rmutex (
);
/*!
requires
- *this is not locked
ensures
- all resources allocated by *this have been freed
!*/
unsigned long lock_count (
) const;
/*!
requires
- the calling thread has a lock on this mutex
ensures
- returns the number of times the thread has called lock()
!*/
void lock (
unsigned long times = 1
) const;
/*!
ensures
- if (*this is currently locked by another thread) then
- the thread that called lock() on *this is put to sleep until
it becomes available.
- #lock_count() == times
- if (*this is currently unlocked) then
- #*this becomes locked and the current thread is NOT put to sleep
but now "owns" #*this
- #lock_count() == times
- if (*this is locked and owned by the current thread) then
- the calling thread retains its lock on *this and isn't put to sleep.
- #lock_count() == lock_count() + times
!*/
void unlock (
unsigned long times = 1
) const;
/*!
ensures
- if (*this is currently locked and owned by the thread calling unlock) then
- if (lock_count() <= times ) then
- #*this is unlocked (i.e. other threads may now lock this object)
- else
- #*this will remain locked
- #lock_count() == lock_count() - times
- else
- the call to unlock() has no effect
!*/
private:
// restricted functions
rmutex(rmutex&); // copy constructor
rmutex& operator=(rmutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_RMUTEX_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,90 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_RSIGNALER_EXTENSIOn_
#define DLIB_RSIGNALER_EXTENSIOn_
#include "rsignaler_extension_abstract.h"
#include "rmutex_extension.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class rsignaler
{
public:
rsignaler (
const rmutex& associated_mutex
) :
assoc_mutex(associated_mutex),
s(m)
{}
~rsignaler (
)
{}
void wait (
) const
{
m.lock();
const unsigned long lock_count = assoc_mutex.lock_count();
assoc_mutex.unlock(lock_count);
s.wait();
m.unlock();
assoc_mutex.lock(lock_count);
}
bool wait_or_timeout (
unsigned long milliseconds
) const
{
m.lock();
const unsigned long lock_count = assoc_mutex.lock_count();
assoc_mutex.unlock(lock_count);
bool res = s.wait_or_timeout(milliseconds);
m.unlock();
assoc_mutex.lock(lock_count);
return res;
}
void signal (
) const
{
m.lock();
s.signal();
m.unlock();
}
void broadcast (
) const
{
m.lock();
s.broadcast();
m.unlock();
}
const rmutex& get_mutex (
) const { return assoc_mutex; }
private:
const rmutex& assoc_mutex;
mutex m;
signaler s;
// restricted functions
rsignaler(rsignaler&); // copy constructor
rsignaler& operator=(rsignaler&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_RSIGNALER_EXTENSIOn_

View File

@@ -0,0 +1,123 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_RSIGNALER_EXTENSIOn_ABSTRACT_
#ifdef DLIB_RSIGNALER_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
#include "rmutex_extension_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class rsignaler
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents an event signaling system for threads. It gives
a thread the ability to wake up other threads that are waiting for a
particular signal.
Each rsignaler object is associated with one and only one rmutex object.
More than one rsignaler object may be associated with a single rmutex
but a signaler object may only be associated with a single rmutex.
NOTE:
You must guard against spurious wakeups. This means that a thread
might return from a call to wait even if no other thread called
signal. This is rare but must be guarded against.
Also note that this object is identical to the signaler object
except that it works with rmutex objects rather than mutex objects.
!*/
public:
rsignaler (
const rmutex& associated_mutex
);
/*!
ensures
- #*this is properly initialized
- #get_mutex() == associated_mutex
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the signaler.
!*/
~rsignaler (
);
/*!
ensures
- all resources allocated by *this have been freed
!*/
void wait (
) const;
/*!
requires
- get_mutex() is locked and owned by the calling thread
ensures
- atomically unlocks get_mutex() and blocks the calling thread
- calling thread may wake if another thread calls signal() or broadcast()
on *this
- when wait() returns the calling thread again has a lock on get_mutex()
!*/
bool wait_or_timeout (
unsigned long milliseconds
) const;
/*!
requires
- get_mutex() is locked and owned by the calling thread
ensures
- atomically unlocks get_mutex() and blocks the calling thread
- calling thread may wake if another thread calls signal() or broadcast()
on *this
- after the specified number of milliseconds has elapsed the calling thread
will wake once get_mutex() is free
- when wait returns the calling thread again has a lock on get_mutex()
- returns false if the call to wait_or_timeout timed out
- returns true if the call did not time out
!*/
void signal (
) const;
/*!
ensures
- if (at least one thread is waiting on *this) then
- at least one of the waiting threads will wake
!*/
void broadcast (
) const;
/*!
ensures
- any and all threads waiting on *this will wake
!*/
const rmutex& get_mutex (
) const;
/*!
ensures
- returns a const reference to the rmutex associated with *this
!*/
private:
// restricted functions
rsignaler(rsignaler&); // copy constructor
rsignaler& operator=(rsignaler&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_RSIGNALER_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,215 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREAD_FUNCTIOn_
#define DLIB_THREAD_FUNCTIOn_
#include <memory>
#include "thread_function_extension_abstract.h"
#include "threads_kernel.h"
#include "auto_mutex_extension.h"
#include "threaded_object_extension.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class thread_function : private threaded_object
{
class base_funct
{
public:
virtual void go() = 0;
virtual ~base_funct() {}
};
template <typename F, typename T1, typename T2, typename T3, typename T4>
class super_funct_4 : public base_funct
{
public:
super_funct_4 ( F funct, T1 arg1, T2 arg2, T3 arg3, T4 arg4) :
f(funct),
a1(arg1),
a2(arg2),
a3(arg3),
a4(arg4)
{
}
void go() { f(a1, a2, a3, a4); }
F f;
T1 a1;
T2 a2;
T3 a3;
T4 a4;
};
template <typename F, typename T1, typename T2, typename T3>
class super_funct_3 : public base_funct
{
public:
super_funct_3 ( F funct, T1 arg1, T2 arg2, T3 arg3):
f(funct),
a1(arg1),
a2(arg2),
a3(arg3)
{
}
void go() { f(a1, a2, a3); }
F f;
T1 a1;
T2 a2;
T3 a3;
};
template <typename F, typename T1, typename T2>
class super_funct_2 : public base_funct
{
public:
super_funct_2 ( F funct, T1 arg1, T2 arg2) :
f(funct),
a1(arg1),
a2(arg2)
{
}
void go() { f(a1, a2); }
F f;
T1 a1;
T2 a2;
};
template <typename F, typename T>
class super_funct_1 : public base_funct
{
public:
super_funct_1 ( F funct, T arg) : f(funct), a(arg)
{
}
void go() { f(a); }
F f;
T a;
};
template <typename F>
class super_funct_0 : public base_funct
{
public:
super_funct_0 ( F funct) : f(funct)
{
}
void go() { f(); }
F f;
};
public:
template <typename F>
thread_function (
F funct
)
{
f.reset(new super_funct_0<F>(funct));
start();
}
template <typename F, typename T>
thread_function (
F funct,
T arg
)
{
f.reset(new super_funct_1<F,T>(funct,arg));
start();
}
template <typename F, typename T1, typename T2>
thread_function (
F funct,
T1 arg1,
T2 arg2
)
{
f.reset(new super_funct_2<F,T1,T2>(funct, arg1, arg2));
start();
}
template <typename F, typename T1, typename T2, typename T3>
thread_function (
F funct,
T1 arg1,
T2 arg2,
T3 arg3
)
{
f.reset(new super_funct_3<F,T1,T2,T3>(funct, arg1, arg2, arg3));
start();
}
template <typename F, typename T1, typename T2, typename T3, typename T4>
thread_function (
F funct,
T1 arg1,
T2 arg2,
T3 arg3,
T4 arg4
)
{
f.reset(new super_funct_4<F,T1,T2,T3,T4>(funct, arg1, arg2, arg3, arg4));
start();
}
~thread_function (
)
{
threaded_object::wait();
}
bool is_alive (
) const
{
return threaded_object::is_alive();
}
void wait (
) const
{
threaded_object::wait();
}
private:
void thread ()
{
f->go();
}
std::unique_ptr<base_funct> f;
// restricted functions
thread_function(thread_function&); // copy constructor
thread_function& operator=(thread_function&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREAD_FUNCTIOn_

View File

@@ -0,0 +1,146 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_THREAD_FUNCTIOn_ABSTRACT_
#ifdef DLIB_THREAD_FUNCTIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class thread_function
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents a thread on a global C++ function or function
object. That is, it allows you to run a function in its own thread.
!*/
public:
template <typename F>
thread_function (
F funct
);
/*!
ensures
- #*this is properly initialized
- the function funct has been started in its own thread
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
template <typename F, typename T1>
thread_function (
F funct,
T1 arg1
);
/*!
ensures
- #*this is properly initialized
- A thread has been created and it will call funct(arg1)
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
template <typename F, typename T1, typename T2>
thread_function (
F funct,
T1 arg1,
T2 arg2
);
/*!
ensures
- #*this is properly initialized
- A thread has been created and it will call funct(arg1, arg2)
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
template <typename F, typename T1, typename T2, typename T3>
thread_function (
F funct,
T1 arg1,
T2 arg2,
T3 arg3
);
/*!
ensures
- #*this is properly initialized
- A thread has been created and it will call funct(arg1, arg2, arg3)
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
template <typename F, typename T1, typename T2, typename T3, typename T4>
thread_function (
F funct,
T1 arg1,
T2 arg2,
T3 arg3,
T4 arg4
);
/*!
ensures
- #*this is properly initialized
- A thread has been created and it will call funct(arg1, arg2, arg3, arg4)
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
~thread_function (
);
/*!
ensures
- all resources allocated by *this have been freed.
- blocks until is_alive() == false
!*/
bool is_alive (
) const;
/*!
ensures
- if (this object's thread has yet to terminate) then
- returns true
- else
- returns false
!*/
void wait (
) const;
/*!
ensures
- if (is_alive() == true) then
- blocks until this object's thread terminates
!*/
private:
// restricted functions
thread_function(thread_function&); // copy constructor
thread_function& operator=(thread_function&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREAD_FUNCTIOn_ABSTRACT_

View File

@@ -0,0 +1,842 @@
// Copyright (C) 2008 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_THREAD_POOl_ABSTRACT_Hh_
#ifdef DLIB_THREAD_POOl_ABSTRACT_Hh_
#include "threads_kernel_abstract.h"
#include "../uintn.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
class future
{
/*!
INITIAL VALUE
- is_ready() == true
WHAT THIS OBJECT REPRESENTS
This object represents a container that allows you to safely pass objects
into the tasks performed by the thread_pool object defined below. An
example will make it clear:
// Suppose you have a global function defined as follows
void add (int a, int b, int& result) { result = a + b; }
// Also suppose you have a thread_pool named tp defined somewhere.
// Then you could do the following.
future<int> a, b, result;
a = 3;
b = 4;
// this function call causes another thread to execute a call to the add() function
// and passes in the int objects contained in a, b, and result
tp.add_task(add,a,b,result);
// This line will wait for the task in the thread pool to finish and then print the
// value in the result integer. So it will print a 7.
cout << result << endl;
!*/
public:
future (
);
/*!
ensures
- The object of type T contained in this future has
an initial value for its type.
- #is_ready() == true
!*/
future (
const T& item
);
/*!
ensures
- #get() == item
- #is_ready() == true
!*/
future (
const future& item
);
/*!
ensures
- if (item.is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to the item future has finished.
- #is_ready() == true
- #item.is_ready() == true
- #get() == item.get()
!*/
~future (
);
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
!*/
bool is_ready (
) const;
/*!
ensures
- if (the value of this future may not yet be ready to be accessed because it
is in use by a task in a thread_pool) then
- returns false
- else
- returns true
!*/
future& operator=(
const T& item
);
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
- #is_ready() == true
- #get() == item
- returns *this
!*/
future& operator=(
const future& item
);
/*!
ensures
- if (is_ready() == false || item.is_ready() == false) then
- the call to this function blocks until the threads processing the tasks related
to this future and the item future have finished.
- #is_ready() == true
- #item.is_ready() == true
- #get() == item.get()
- returns *this
!*/
operator T& (
);
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
- #is_ready() == true
- returns get()
!*/
operator const T& (
) const;
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
- #is_ready() == true
- returns get()
!*/
T& get (
);
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
- #is_ready() == true
- returns a non-const reference to the object of type T contained inside this future
!*/
const T& get (
) const;
/*!
ensures
- if (is_ready() == false) then
- the call to this function blocks until the thread processing the task related
to this future has finished.
- #is_ready() == true
- returns a const reference to the object of type T contained inside this future
!*/
};
// ----------------------------------------------------------------------------------------
template <typename T>
inline void swap (
future<T>& a,
future<T>& b
) { std::swap(a.get(), b.get()); }
/*!
provides a global swap function
!*/
// ----------------------------------------------------------------------------------------
// The future object comes with overloads for all the usual comparison operators.
template <typename T> bool operator== (const future<T>& a, const future<T>& b) { return a.get() == b.get(); }
template <typename T> bool operator!= (const future<T>& a, const future<T>& b) { return a.get() != b.get(); }
template <typename T> bool operator<= (const future<T>& a, const future<T>& b) { return a.get() <= b.get(); }
template <typename T> bool operator>= (const future<T>& a, const future<T>& b) { return a.get() >= b.get(); }
template <typename T> bool operator< (const future<T>& a, const future<T>& b) { return a.get() < b.get(); }
template <typename T> bool operator> (const future<T>& a, const future<T>& b) { return a.get() > b.get(); }
template <typename T> bool operator== (const future<T>& a, const T& b) { return a.get() == b; }
template <typename T> bool operator== (const T& a, const future<T>& b) { return a == b.get(); }
template <typename T> bool operator!= (const future<T>& a, const T& b) { return a.get() != b; }
template <typename T> bool operator!= (const T& a, const future<T>& b) { return a != b.get(); }
template <typename T> bool operator<= (const future<T>& a, const T& b) { return a.get() <= b; }
template <typename T> bool operator<= (const T& a, const future<T>& b) { return a <= b.get(); }
template <typename T> bool operator>= (const future<T>& a, const T& b) { return a.get() >= b; }
template <typename T> bool operator>= (const T& a, const future<T>& b) { return a >= b.get(); }
template <typename T> bool operator< (const future<T>& a, const T& b) { return a.get() < b; }
template <typename T> bool operator< (const T& a, const future<T>& b) { return a < b.get(); }
template <typename T> bool operator> (const future<T>& a, const T& b) { return a.get() > b; }
template <typename T> bool operator> (const T& a, const future<T>& b) { return a > b.get(); }
// ----------------------------------------------------------------------------------------
class thread_pool
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents a fixed size group of threads which you can
submit tasks to and then wait for those tasks to be completed.
Note that setting the number of threads to 0 is a valid way to
use this object. It causes it to not contain any threads
at all. When tasks are submitted to the object in this mode
the tasks are processed within the calling thread. So in this
mode any thread that calls add_task() is considered to be
a thread_pool thread capable of executing tasks.
This object is also implemented such that no memory allocations occur
after the thread_pool has been constructed so long as the user doesn't
call any of the add_task_by_value() routines. The future object also
doesn't perform any memory allocations or contain any system resources
such as mutex objects.
EXCEPTIONS
Note that if an exception is thrown inside a task thread and is not caught
then the exception will be trapped inside the thread pool and rethrown at a
later time when someone calls one of the add task or wait member functions
of the thread pool. This allows exceptions to propagate out of task threads
and into the calling code where they can be handled.
!*/
public:
explicit thread_pool (
unsigned long num_threads
);
/*!
ensures
- #num_threads_in_pool() == num_threads
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
~thread_pool(
);
/*!
ensures
- blocks until all tasks in the pool have finished.
- If one of the threads has generated an exception but it hasn't yet been
rethrown to the caller (e.g. by calling wait_for_all_tasks()) then the
program will be terminated. So make sure you handle all the possible
exceptions from your tasks.
!*/
bool is_task_thread (
) const;
/*!
ensures
- if (the thread calling this function is one of the threads in this
thread pool or num_threads_in_pool() == 0) then
- returns true
- else
- returns false
!*/
unsigned long num_threads_in_pool (
) const;
/*!
ensures
- returns the number of threads contained in this thread pool. That is, returns
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>
uint64 add_task (
T& obj,
void (T::*funct)()
);
/*!
requires
- 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
- if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)() 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 (obj.*funct)()
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)()
);
/*!
requires
- funct == a valid member function pointer for class T
ensures
- makes a copy of obj, call it OBJ_COPY.
- if (is_task_thread() == true and there aren't any free threads available) then
- calls (OBJ_COPY.*funct)() 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 (OBJ_COPY.*funct)().
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T>
uint64 add_task (
T& obj,
void (T::*funct)(long),
long arg1
);
/*!
requires
- 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
- if (is_task_thread() == true and there aren't any free threads available) then
- calls (obj.*funct)(arg1) 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 (obj.*funct)(arg1)
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T>
uint64 add_task (
T& obj,
void (T::*funct)(long,long),
long arg1,
long arg2
);
/*!
requires
- 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
- 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
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 (obj.*funct)(arg1,arg2)
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
void wait_for_task (
uint64 task_id
) const;
/*!
ensures
- if (there is currently a task with the given id being executed in the thread pool) then
- the call to this function blocks until the task with the given id is complete
- else
- the call to this function returns immediately
!*/
void wait_for_all_tasks (
) const;
/*!
ensures
- the call to this function blocks until all tasks which were submitted
to the thread pool by the thread that is calling this function have
finished.
!*/
// --------------------
template <typename F, typename A1>
uint64 add_task (
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)
- 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
- 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
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 function_object(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 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>
uint64 add_task (
T& obj,
void (T::*funct)(T1),
future<A1>& arg1
);
/*!
requires
- funct == a valid member function pointer for class T
- (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)
- 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
- 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
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 (obj.*funct)(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>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1),
future<A1>& arg1
);
/*!
requires
- funct == a valid member function pointer for class T
- (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)
ensures
- makes a copy of obj, call it OBJ_COPY.
- if (is_task_thread() == true and there aren't any free threads available) then
- calls (OBJ_COPY.*funct)(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 (OBJ_COPY.*funct)(arg1.get()).
- 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>
uint64 add_task (
const T& obj,
void (T::*funct)(T1) const,
future<A1>& arg1
);
/*!
requires
- funct == a valid member function pointer for class T
- (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)
- 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
- 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
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 (obj.*funct)(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>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1) const,
future<A1>& arg1
);
/*!
requires
- funct == a valid member function pointer for class T
- (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)
ensures
- makes a copy of obj, call it OBJ_COPY.
- if (is_task_thread() == true and there aren't any free threads available) then
- calls (OBJ_COPY.*funct)(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 (OBJ_COPY.*funct)(arg1.get()).
- returns a task id that can be used by this->wait_for_task() to wait
for the submitted task to finish.
!*/
template <typename T1, typename A1>
uint64 add_task (
void (*funct)(T1),
future<A1>& arg1
);
/*!
requires
- funct == a valid function pointer
- (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)
ensures
- if (is_task_thread() == true and there aren't any free threads available) then
- calls funct(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 funct(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.
!*/
// --------------------------------------------------------------------------------
// The remainder of this class just contains overloads for add_task() and add_task_by_value()
// that take up to 4 futures (as well as 0 futures). Their behavior is identical to the above
// add_task() and add_task_by_value() functions.
// --------------------------------------------------------------------------------
template <typename F, typename A1, typename A2>
uint64 add_task (
F& function_object,
future<A1>& arg1,
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,
typename T2, typename A2>
uint64 add_task (
T& obj,
void (T::*funct)(T1,T2),
future<A1>& arg1,
future<A2>& arg2
);
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2),
future<A1>& arg1,
future<A2>& arg2
);
template <typename T, typename T1, typename A1,
typename T2, typename A2>
uint64 add_task (
const T& obj,
void (T::*funct)(T1,T2) const,
future<A1>& arg1,
future<A2>& arg2
);
template <typename T, typename T1, typename A1,
typename T2, typename A2>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2) const,
future<A1>& arg1,
future<A2>& arg2
);
template <typename T1, typename A1,
typename T2, typename A2>
uint64 add_task (
void (*funct)(T1,T2),
future<A1>& arg1,
future<A2>& arg2
);
// --------------------
template <typename F, typename A1, typename A2, typename A3>
uint64 add_task (
F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
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
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3>
uint64 add_task (
T& obj,
void (T::*funct)(T1,T2,T3),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2,T3),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3>
uint64 add_task (
const T& obj,
void (T::*funct)(T1,T2,T3) const,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2,T3) const,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
template <typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3>
uint64 add_task (
void (*funct)(T1,T2,T3),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3
);
// --------------------
template <typename F, typename A1, typename A2, typename A3, typename A4>
uint64 add_task (
F& function_object,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
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
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3,
typename T4, typename A4>
uint64 add_task (
T& obj,
void (T::*funct)(T1,T2,T3,T4),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3,
typename T4, typename A4>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2,T3,T4),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3,
typename T4, typename A4>
uint64 add_task (
const T& obj,
void (T::*funct)(T1,T2,T3,T4) const,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
template <typename T, typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3,
typename T4, typename A4>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)(T1,T2,T3,T4) const,
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
template <typename T1, typename A1,
typename T2, typename A2,
typename T3, typename A3,
typename T4, typename A4>
uint64 add_task (
void (*funct)(T1,T2,T3,T4),
future<A1>& arg1,
future<A2>& arg2,
future<A3>& arg3,
future<A4>& arg4
);
// --------------------
template <typename F>
uint64 add_task (
F& function_object
);
template <typename T>
uint64 add_task (
const T& obj,
void (T::*funct)() const,
);
template <typename T>
uint64 add_task_by_value (
const T& obj,
void (T::*funct)() const
);
uint64 add_task (
void (*funct)()
);
// --------------------
private:
// restricted functions
thread_pool(thread_pool&); // copy constructor
thread_pool& operator=(thread_pool&); // assignment operator
};
}
// ----------------------------------------------------------------------------------------
#endif // DLIB_THREAD_POOl_ABSTRACT_Hh_

View File

@@ -0,0 +1,141 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_
#define DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_
#include "thread_specific_data_extension_abstract.h"
#include "threads_kernel_abstract.h"
#include "../binary_search_tree.h"
#include "auto_mutex_extension.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
class thread_specific_data
{
/*!
CONVENTION
- for all valid ID:
(*items[ID]) == pointer to the data for thread with id ID
!*/
public:
thread_specific_data (
)
{
thread_end_handler_calls_left = 0;
}
~thread_specific_data (
)
{
// We should only call the unregister_thread_end_handler function if there are
// some outstanding callbacks we expect to get. Otherwise lets avoid calling it
// since the dlib state that maintains the registered thread end handlers may have
// been destructed already (since the program might be in the process of terminating).
bool call_unregister = false;
m.lock();
if (thread_end_handler_calls_left > 0)
call_unregister = true;
m.unlock();
if (call_unregister)
unregister_thread_end_handler(const_cast<thread_specific_data&>(*this),&thread_specific_data::thread_end_handler);
auto_mutex M(m);
items.reset();
while (items.move_next())
{
delete items.element().value();
}
}
inline T& data (
) { return get_data(); }
inline const T& data (
) const { return get_data(); }
private:
T& get_data (
) const
{
thread_id_type id = get_thread_id();
auto_mutex M(m);
T** item = items[id];
if (item)
{
return **item;
}
else
{
// register an end handler for this thread so long as it is a dlib created thread.
T* new_item = new T;
bool in_tree = false;
try
{
T* temp_item = new_item;
thread_id_type temp_id = id;
items.add(temp_id,temp_item);
in_tree = true;
if (is_dlib_thread(id))
{
register_thread_end_handler(const_cast<thread_specific_data&>(*this),&thread_specific_data::thread_end_handler);
++thread_end_handler_calls_left;
}
}
catch (...)
{
if (in_tree)
{
items.destroy(id);
}
delete new_item;
throw;
}
return *new_item;
}
}
void thread_end_handler (
)
{
const thread_id_type id = get_thread_id();
thread_id_type junk = 0;
T* item = 0;
auto_mutex M(m);
--thread_end_handler_calls_left;
if (items[id])
{
items.remove(id,junk,item);
delete item;
}
}
mutable typename binary_search_tree<thread_id_type,T*>::kernel_2a items;
mutex m;
mutable long thread_end_handler_calls_left;
// restricted functions
thread_specific_data(thread_specific_data&); // copy constructor
thread_specific_data& operator=(thread_specific_data&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_

View File

@@ -0,0 +1,87 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_ABSTRACT_
#ifdef DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
template <
typename T
>
class thread_specific_data
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents a container of thread specific data. When
a thread calls the data() member function it gets a reference to a T object
that is specific to its own thread. Each subsequent call to data() from that
thread returns the same instance. Also note that when a thread ends
the instance of its data() object gets destroyed and freed (if the thread
was created by the dlib library). So any pointers or references to the object
will be invalid after the thread has ended.
!*/
public:
thread_specific_data (
);
/*!
ensures
- #*this is properly initialized
!*/
~thread_specific_data (
);
/*!
ensures
- all resources allocated by *this have been freed. This includes
all the thread specific data returned by the data() functions.
!*/
T& data (
);
/*!
ensures
- if (the calling thread has NOT called this->data() before) then
- constructs an instance of T that is specific to the calling
thread.
- returns a reference to the T instance that was constructed for
the calling thread.
throws
- std::bad_alloc or any exception thrown by T's constructor
If an exception is thrown then the call to data() will have
no effect on *this.
!*/
const T& data (
) const;
/*!
ensures
- if (the calling thread has NOT called this->data() before) then
- constructs an instance of T that is specific to the calling
thread.
- returns a const reference to the T instance that was constructed for
the calling thread.
throws
- std::bad_alloc or any exception thrown by T's constructor
If an exception is thrown then the call to data() will have
no effect on *this.
!*/
private:
// restricted functions
thread_specific_data(thread_specific_data&); // copy constructor
thread_specific_data& operator=(thread_specific_data&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREAD_SPECIFIC_DATA_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,123 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADED_OBJECT_EXTENSIOn_
#define DLIB_THREADED_OBJECT_EXTENSIOn_
#include "threaded_object_extension_abstract.h"
#include "threads_kernel.h"
#include "auto_mutex_extension.h"
#include "../algs.h"
#include "../assert.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class threaded_object
{
/*!
INITIAL VALUE
- is_running_ == false
- is_alive_ == false
- should_stop_ == false
- should_respawn_ == false
#ifdef ENABLE_ASSERTS
- id_valid == false
- id1 == get_main_thread_id()
#endif
CONVENTION
- is_running() == is_running_
- is_alive() == is_alive_
- should_stop() == should_stop_
- should_respawn() == should_respawn_
#ifdef ENABLE_ASSERTS
- if (when thread() is executing) then
- id1 == the id of the running thread
- id_valid == true
- else
- id1 == an undefined value
- id_valid == false
#endif
- m_ == the mutex used to protect all our variables
- s == the signaler for m_
!*/
public:
threaded_object (
);
virtual ~threaded_object (
);
bool is_running (
) const;
bool is_alive (
) const;
void wait (
) const;
void start (
);
void restart (
);
void set_respawn (
);
bool should_respawn (
) const;
void pause (
);
void stop (
);
protected:
bool should_stop (
) const;
private:
void thread_helper(
);
virtual void thread (
) = 0;
mutex m_;
signaler s;
thread_id_type id1;
bool is_running_;
bool is_alive_;
bool should_stop_;
bool should_respawn_;
bool id_valid;
// restricted functions
threaded_object(threaded_object&); // copy constructor
threaded_object& operator=(threaded_object&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "threaded_object_extension.cpp"
#endif
#endif // DLIB_THREADED_OBJECT_EXTENSIOn_

View File

@@ -0,0 +1,199 @@
// Copyright (C) 2007 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_THREADED_OBJECT_EXTENSIOn_ABSTRACT_
#ifdef DLIB_THREADED_OBJECT_EXTENSIOn_ABSTRACT_
#include "threads_kernel_abstract.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
class threaded_object
{
/*!
INITIAL VALUE
- is_running() == false
- is_alive() == false
- should_respawn() == false
WHAT THIS OBJECT REPRESENTS
This object represents a simple threaded object. To use it you inherit
from it and define the thread() function. Then when you call start()
it will spawn a thread that calls this->thread().
!*/
public:
threaded_object (
);
/*!
ensures
- #*this is properly initialized
throws
- std::bad_alloc
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create threading objects.
!*/
virtual ~threaded_object (
);
/*!
requires
- is_alive() == false
(i.e. in the destructor for the object you derive from this one you
must wait for this->thread() to end.)
ensures
- all resources allocated by *this have been freed.
!*/
bool is_running (
) const;
/*!
requires
- is not called from this->thread()
ensures
- if (is_alive() && this->thread() is currently supposed to be executing) then
- returns true
- else
- returns false
!*/
bool is_alive (
) const;
/*!
requires
- is not called from this->thread()
ensures
- if (this->thread() has been called by some thread and has yet to terminate) then
- returns true
- else
- returns false
!*/
void wait (
) const;
/*!
requires
- is not called from this->thread()
ensures
- if (is_alive() == true) then
- blocks until this->thread() terminates
!*/
void start (
);
/*!
requires
- is not called from this->thread()
ensures
- #is_alive() == true
- #is_running() == true
- #should_stop() == false
throws
- std::bad_alloc or dlib::thread_error
If either of these exceptions are thrown then
#is_alive() == false and #is_running() == false
!*/
void set_respawn (
);
/*!
requires
- is not called from this->thread()
ensures
- #should_respawn() == true
!*/
bool should_respawn (
) const;
/*!
requires
- is not called from this->thread()
ensures
- returns true if the thread will automatically restart upon termination and
false otherwise. Note that every time a thread starts it sets should_respawn()
back to false. Therefore, a single call to set_respawn() can cause at most
one respawn to occur.
!*/
void restart (
);
/*!
requires
- is not called from this->thread()
ensures
- This function atomically executes set_respawn() and start(). The precise meaning of this
is defined below.
- if (is_alive()) then
- #should_respawn() == true
- else
- #should_respawn() == false
- #is_alive() == true
- #is_running() == true
- #should_stop() == false
throws
- std::bad_alloc or dlib::thread_error
If either of these exceptions are thrown then
#is_alive() == false and #is_running() == false
!*/
void pause (
);
/*!
requires
- is not called from this->thread()
ensures
- #is_running() == false
!*/
void stop (
);
/*!
requires
- is not called from this->thread()
ensures
- #should_stop() == true
- #is_running() == false
- #should_respawn() == false
!*/
protected:
bool should_stop (
) const;
/*!
requires
- is only called from the thread that executes this->thread()
ensures
- calls to this function block until (#is_running() == true || #should_stop() == true)
- if (this thread is supposed to terminate) then
- returns true
- else
- returns false
!*/
private:
virtual void thread (
) = 0;
/*!
requires
- is executed in its own thread
- is only executed in one thread at a time
throws
- does not throw any exceptions
!*/
// restricted functions
threaded_object(threaded_object&); // copy constructor
threaded_object& operator=(threaded_object&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREADED_OBJECT_EXTENSIOn_ABSTRACT_

View File

@@ -0,0 +1,18 @@
// Copyright (C) 2006 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADs_KERNEL_
#define DLIB_THREADs_KERNEL_
#include "../platform.h"
#ifdef WIN32
#include "windows.h"
#endif
#ifndef WIN32
#include "posix.h"
#endif
#endif // DLIB_THREADs_KERNEL_

View File

@@ -0,0 +1,158 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_1_
#define DLIB_THREADS_KERNEl_1_
#ifdef DLIB_ISO_CPP_ONLY
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code. Turn DLIB_ISO_CPP_ONLY off if you want to use it."
#endif
#include "threads_kernel_abstract.h"
#include "../windows_magic.h"
#include <windows.h>
#include "../algs.h"
#include <condition_variable>
#include <mutex>
#include <chrono>
namespace dlib
{
// ----------------------------------------------------------------------------------------
typedef DWORD thread_id_type;
inline thread_id_type get_thread_id (
)
{
return GetCurrentThreadId();
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// mutex object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// forward declaration of signaler
class signaler;
class mutex
{
public:
mutex (
)
{
}
~mutex (
) { }
void lock (
) const { cs.lock(); }
void unlock (
) const { cs.unlock(); }
private:
friend class signaler;
mutable std::mutex cs;
// restricted functions
mutex(mutex&); // copy constructor
mutex& operator=(mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// signaler object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class signaler
{
public:
signaler (
const mutex& associated_mutex
) :
m(associated_mutex)
{
}
~signaler (
) { }
void wait (
) const
{
std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
cv.wait(cs);
}
bool wait_or_timeout (
unsigned long milliseconds
) const
{
std::unique_lock<std::mutex> cs(m.cs, std::defer_lock);
auto status = cv.wait_until(cs, std::chrono::system_clock::now() + std::chrono::milliseconds(milliseconds));
return status == std::cv_status::no_timeout;
}
void signal (
) const
{
cv.notify_one();
}
void broadcast (
) const
{
cv.notify_all();
}
const mutex& get_mutex (
) const { return m; }
private:
mutable std::condition_variable cv;
const mutex& m;
// restricted functions
signaler(signaler&); // copy constructor
signaler& operator=(signaler&); // assignment operator
};
// ----------------------------------------------------------------------------------------
namespace threads_kernel_shared_helpers
{
bool spawn_thread (
void (*funct)(void*),
void* param
);
/*!
is identical to create_new_thread() but just doesn't use any thread pooling.
!*/
}
// ----------------------------------------------------------------------------------------
}
#include "threads_kernel_shared.h"
#ifdef NO_MAKEFILE
#include "threads_kernel_1.cpp"
#endif
#endif // DLIB_THREADS_KERNEl_1_

View File

@@ -0,0 +1,180 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_2_
#define DLIB_THREADS_KERNEl_2_
#ifdef DLIB_ISO_CPP_ONLY
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code. Turn DLIB_ISO_CPP_ONLY off if you want to use it."
#endif
#include "threads_kernel_abstract.h"
#include <pthread.h>
#include <errno.h>
#include <sys/time.h>
#include "../algs.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
typedef pthread_t thread_id_type;
inline thread_id_type get_thread_id (
)
{
return pthread_self();
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// mutex object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// forward declaration of signaler
class signaler;
class mutex
{
// give signaler access to hMutex
friend class signaler;
public:
mutex (
)
{
if (pthread_mutex_init(&myMutex,0))
{
throw dlib::thread_error(ECREATE_MUTEX,
"in function mutex::mutex() an error occurred making the mutex"
);
}
}
~mutex (
) { pthread_mutex_destroy(&myMutex); }
void lock (
) const { pthread_mutex_lock(&myMutex); }
void unlock (
) const { pthread_mutex_unlock(&myMutex); }
private:
mutable pthread_mutex_t myMutex;
// restricted functions
mutex(mutex&); // copy constructor
mutex& operator=(mutex&); // assignement opertor
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// signaler object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class signaler
{
public:
signaler (
const mutex& assoc_mutex
) :
associated_mutex(&assoc_mutex.myMutex),
m(assoc_mutex)
{
if (pthread_cond_init(&cond,0))
{
throw dlib::thread_error(ECREATE_SIGNALER,
"in function signaler::signaler() an error occurred making the signaler"
);
}
}
~signaler (
) { pthread_cond_destroy(&cond); }
void wait (
) const
{
pthread_cond_wait(&cond,associated_mutex);
}
bool wait_or_timeout (
unsigned long milliseconds
) const
{
timespec time_to_wait;
timeval curtime;
gettimeofday(&curtime,0);
// get the time and adjust the timespec object by the appropriate amount
time_to_wait.tv_sec = milliseconds/1000 + curtime.tv_sec;
time_to_wait.tv_nsec = curtime.tv_usec;
time_to_wait.tv_nsec *= 1000;
time_to_wait.tv_nsec += (milliseconds%1000)*1000000;
time_to_wait.tv_sec += time_to_wait.tv_nsec/1000000000;
time_to_wait.tv_nsec = time_to_wait.tv_nsec%1000000000;
if ( pthread_cond_timedwait(&cond,associated_mutex,&time_to_wait) == ETIMEDOUT)
{
return false;
}
else
{
return true;
}
}
void signal (
) const { pthread_cond_signal(&cond); }
void broadcast (
) const { pthread_cond_broadcast(&cond); }
const mutex& get_mutex (
) const { return m; }
private:
pthread_mutex_t* const associated_mutex;
mutable pthread_cond_t cond;
const mutex& m;
// restricted functions
signaler(signaler&); // copy constructor
signaler& operator=(signaler&); // assignement opertor
};
// ----------------------------------------------------------------------------------------
namespace threads_kernel_shared_helpers
{
bool spawn_thread (
void (*funct)(void*),
void* param
);
/*!
is identical to create_new_thread() but just doesn't use any thread pooling.
!*/
}
// ----------------------------------------------------------------------------------------
}
#include "threads_kernel_shared.h"
#ifdef NO_MAKEFILE
#include "threads_kernel_2.cpp"
#endif
#endif // DLIB_THREADS_KERNEl_2_

View File

@@ -0,0 +1,302 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#undef DLIB_THREADS_KERNEl_ABSTRACT_
#ifdef DLIB_THREADS_KERNEl_ABSTRACT_
namespace dlib
{
// ----------------------------------------------------------------------------------------
/*!
THREAD POOLING
When threads end they go into a global thread pool and each waits there
for 30 seconds before timing out and having its resources returned to the
operating system. When create_new_thread() is called it first looks in the
thread pool to see if there are any threads it can snatch from the pool, if
not then it makes a new one.
Note that whenever I say something happens when a thread "terminates" or "ends"
I mean "when it returns to the thread pool." From the client programmer point
of view a thread terminates/ends when it returns to the dlib thread pool and you
shouldn't and indeed don't need to know when it actually gets its resources
reclaimed by the operating system.
If you want to change the timeout to a different value you can #define
DLIB_THREAD_POOL_TIMEOUT to whatever value (in milliseconds) that you like.
EXCEPTIONS
Unless specified otherwise, nothing in this file throws exceptions.
!*/
// ----------------------------------------------------------------------------------------
thread_id_type get_thread_id (
);
/*!
ensures
- returns a unique id for the calling thread. Note that while the id is unique
among all currently existing threads it may have been used by a previous
thread that has terminated.
!*/
// ----------------------------------------------------------------------------------------
bool is_dlib_thread (
thread_id_type id = get_thread_id()
);
/*!
ensures
- if (the thread with the given id was spawned by a call to
dlib::create_new_thread) then
- returns true
- else
- returns false
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
void register_thread_end_handler (
T& obj,
void (T::*handler)()
);
/*!
requires
- handler == a valid member function pointer for class T
- handler does not throw
- handler does not call register_thread_end_handler()
- handler does not block
- is_dlib_thread() == true (i.e. the calling thread was spawned by dlib::create_new_thread())
ensures
- let ID == the thread id for the thread calling register_thread_end_handler()
- (obj.*handler)() will be called when the thread with thread id ID is
terminating and it will be called from within that terminating thread.
(i.e. inside the handler function get_thread_id() == ID == the id of the
thread that is terminating. )
- each call to this function adds another handler that will be called when
the given thread terminates. This means that if you call it a bunch of
times then you will end up registering multiple handlers (or single
handlers multiple times) that will be called when the thread ends.
throws
- std::bad_alloc
If this exception is thrown then the call to this function had no effect.
!*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
void unregister_thread_end_handler (
T& obj,
void (T::*handler)()
);
/*!
requires
- handler == a valid member function pointer for class T
ensures
- Undoes all previous calls to register_thread_end_handler(obj,handler).
So the given handler won't be called when any threads end.
throws
- std::bad_alloc
If this exception is thrown then the call to this function had no effect.
!*/
// ----------------------------------------------------------------------------------------
bool create_new_thread (
void (*funct)(void*),
void* param
);
/*!
ensures
- creates a new thread for the function pointed to by funct
- passes it param as its parameter. (i.e. calls funct(param) from the new thread)
- returns true upon success and false upon failure to create the new thread
!*/
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// mutex object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class mutex
{
/*!
INITIAL VALUE
mutex is in the unlocked state
WHAT THIS OBJECT REPRESENTS
This object represents a mutex intended to be used for synchronous
thread control of shared data. When a thread wants to access some
shared data it locks out other threads by calling lock() and calls
unlock() when it is finished.
!*/
public:
mutex (
);
/*!
ensures
- #*this is properly initialized
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the mutex.
!*/
~mutex (
);
/*!
requires
- *this is not locked
ensures
- all resources allocated by *this have been freed
!*/
void lock (
) const;
/*!
requires
- the thread calling lock() does not already have a lock on *this
ensures
- if (*this is currently locked by another thread) then
- the thread that called lock() on *this is put to sleep until
it becomes available
- if (*this is currently unlocked) then
- #*this becomes locked and the current thread is NOT put to sleep
but now "owns" #*this
!*/
void unlock (
) const;
/*!
requires
- the thread calling unlock() already has a lock on *this
ensures
- #*this is unlocked (i.e. other threads may now lock this object)
!*/
private:
// restricted functions
mutex(mutex&); // copy constructor
mutex& operator=(mutex&); // assignment operator
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
// signaler object
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
class signaler
{
/*!
WHAT THIS OBJECT REPRESENTS
This object represents an event signaling system for threads. It gives
a thread the ability to wake up other threads that are waiting for a
particular signal.
Each signaler object is associated with one and only one mutex object.
More than one signaler object may be associated with a single mutex
but a signaler object may only be associated with a single mutex.
NOTE:
You must guard against spurious wakeups. This means that a thread
might return from a call to wait even if no other thread called
signal. This is rare but must be guarded against.
!*/
public:
signaler (
const mutex& associated_mutex
);
/*!
ensures
- #*this is properly initialized
- #get_mutex() == associated_mutex
throws
- dlib::thread_error
the constructor may throw this exception if there is a problem
gathering resources to create the signaler.
!*/
~signaler (
);
/*!
ensures
- all resources allocated by *this have been freed
!*/
void wait (
) const;
/*!
requires
- get_mutex() is locked and owned by the calling thread
ensures
- atomically unlocks get_mutex() and blocks the calling thread
- calling thread may wake if another thread calls signal() or broadcast()
on *this
- when wait() returns the calling thread again has a lock on get_mutex()
!*/
bool wait_or_timeout (
unsigned long milliseconds
) const;
/*!
requires
- get_mutex() is locked and owned by the calling thread
ensures
- atomically unlocks get_mutex() and blocks the calling thread
- calling thread may wake if another thread calls signal() or broadcast()
on *this
- after the specified number of milliseconds has elapsed the calling thread
will wake once get_mutex() is free
- when wait returns the calling thread again has a lock on get_mutex()
- returns false if the call to wait_or_timeout timed out
- returns true if the call did not time out
!*/
void signal (
) const;
/*!
ensures
- if (at least one thread is waiting on *this) then
- at least one of the waiting threads will wake
!*/
void broadcast (
) const;
/*!
ensures
- any and all threads waiting on *this will wake
!*/
const mutex& get_mutex (
) const;
/*!
ensures
- returns a const reference to the mutex associated with *this
!*/
private:
// restricted functions
signaler(signaler&); // copy constructor
signaler& operator=(signaler&); // assignment operator
};
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_THREADS_KERNEl_ABSTRACT_

View File

@@ -0,0 +1,274 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_SHARED_
#define DLIB_THREADS_KERNEl_SHARED_
// this file should be included at the bottom of one of the thread kernel headers for a
// specific platform.
//#include "../threads.h"
#include "auto_mutex_extension.h"
#include "../binary_search_tree.h"
#include "../member_function_pointer.h"
#include "../memory_manager.h"
#include "../queue.h"
#include "../set.h"
#include "../test_for_odr_violations.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
namespace threads_kernel_shared
{
void thread_starter (
void*
);
class threader
{
/*!
INITIAL VALUE
- pool_count == 0 and
- data_ready is associated with the mutex data_mutex
- data_empty is associated with the mutex data_mutex
- destructed is associated with the mutex data_mutex
- destruct == false
- total_count == 0
- function_pointer == 0
- do_not_ever_destruct == false
CONVENTION
- data_ready is associated with the mutex data_mutex
- data_empty is associated with the mutex data_mutex
- data_ready == a signaler used signal when there is new data waiting
to start a thread with.
- data_empty == a signaler used to signal when the data is now empty
- pool_count == the number of suspended threads in the thread pool
- total_count == the number of threads that are executing anywhere. i.e.
pool_count + the ones that are currently running some user function.
- if (function_pointer != 0) then
- parameter == a void pointer pointing to the parameter which
should be used to start the next thread
- function_pointer == a pointer to the next function to make a
new thread with
- if (the destructor is running) then
- destruct == true
- else
- destruct == false
- thread_ids is locked by the data_mutex
- thread_ids == a set that contains the thread id for each thread spawned by this
object.
!*/
public:
threader (
);
~threader (
);
void destruct_if_ready (
);
/*!
ensures
- if (there are no threads currently running and we haven't set do_not_ever_destruct) then
- calls delete this
- else
- does nothing
!*/
bool create_new_thread (
void (*funct)(void*),
void* param
);
template <
typename T
>
void unregister_thread_end_handler (
T& obj,
void (T::*handler)()
)
{
member_function_pointer<> mfp, junk_mfp;
mfp.set(obj,handler);
thread_id_type junk_id;
// find any member function pointers in the registry that point to the same
// thing as mfp and remove them
auto_mutex M(reg.m);
reg.reg.reset();
while (reg.reg.move_next())
{
while (reg.reg.current_element_valid() && reg.reg.element().value() == mfp)
{
reg.reg.remove_current_element(junk_id, junk_mfp);
}
}
}
template <
typename T
>
void register_thread_end_handler (
T& obj,
void (T::*handler)()
)
{
thread_id_type id = get_thread_id();
member_function_pointer<> mfp;
mfp.set(obj,handler);
auto_mutex M(reg.m);
reg.reg.add(id,mfp);
}
bool is_dlib_thread (
thread_id_type id
);
private:
friend void thread_starter (
void*
);
void call_end_handlers (
);
/*!
ensures
- calls the registered end handlers for the calling thread and
then removes them from reg.reg
!*/
// private data
set<thread_id_type,memory_manager<char>::kernel_2b>::kernel_1b_c thread_ids;
unsigned long total_count;
void* parameter;
void (*function_pointer)(void*);
unsigned long pool_count;
mutex data_mutex; // mutex to protect the above data
signaler data_ready; // signaler to signal when there is new data
signaler data_empty; // signaler to signal when the data is empty
bool destruct;
signaler destructed; // signaler to signal when a thread has ended
bool do_not_ever_destruct;
struct registry_type
{
mutex m;
binary_search_tree<
thread_id_type,
member_function_pointer<>,
memory_manager<char>::kernel_2a
>::kernel_2a_c reg;
};
// stuff for the register_thread_end_handler
registry_type reg;
// restricted functions
threader(threader&); // copy constructor
threader& operator=(threader&); // assignement opertor
};
// ------------------------------------------------------------------------------------
threader& thread_pool (
);
/*!
ensures
- returns a reference to the global threader object
!*/
// ------------------------------------------------------------------------------------
extern bool thread_pool_has_been_destroyed;
}
bool is_dlib_thread (
thread_id_type id
);
bool is_dlib_thread (
);
// ----------------------------------------------------------------------------------------
inline bool create_new_thread (
void (*funct)(void*),
void* param
)
{
try
{
// now make this thread
return threads_kernel_shared::thread_pool().create_new_thread(funct,param);
}
catch (std::bad_alloc&)
{
return false;
}
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
inline void register_thread_end_handler (
T& obj,
void (T::*handler)()
)
{
DLIB_ASSERT(is_dlib_thread(),
"\tvoid register_thread_end_handler"
<< "\n\tYou can't register a thread end handler for a thread dlib didn't spawn."
);
threads_kernel_shared::thread_pool().register_thread_end_handler(obj,handler);
}
// ----------------------------------------------------------------------------------------
template <
typename T
>
inline void unregister_thread_end_handler (
T& obj,
void (T::*handler)()
)
{
// Check if the thread pool has been destroyed and if it has then don't do anything.
// This bool here is always true except when the program has started to terminate and
// the thread pool object has been destroyed. This if is here to catch other global
// objects that have destructors that try to call unregister_thread_end_handler().
// Without this check we get into trouble if the thread pool is destroyed before these
// objects.
if (threads_kernel_shared::thread_pool_has_been_destroyed == false)
threads_kernel_shared::thread_pool().unregister_thread_end_handler(obj,handler);
}
// ----------------------------------------------------------------------------------------
}
#ifdef NO_MAKEFILE
#include "threads_kernel_shared.cpp"
#endif
#endif // DLIB_THREADS_KERNEl_SHARED_

View File

@@ -0,0 +1,6 @@
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_THREADS_KERNEl_2_
#include "threads_kernel_1.h"
#endif