Commit 3543392f authored by Davis King's avatar Davis King

Removed the register_program_ending_handler() function from the threading API

and also made the dlib thread pool not block the termination of the program
if there are still threads executing.

--HG--
extra : convert_revision : svn%3Afdd8eb12-d10e-0410-9acb-85c331704f74/trunk%402524
parent 8e0eea0b
...@@ -65,11 +65,9 @@ namespace dlib ...@@ -65,11 +65,9 @@ namespace dlib
// this variable holds a mapping from window handles to the base_window // this variable holds a mapping from window handles to the base_window
// objects which represent them. Note that this objects mutex is always locked // objects which represent them. Note that this objects mutex is always locked
// when inside the event loop. // when inside the event loop.
// Also, put these objects on the heap because we want to ensure that they static window_table_type window_table;
// aren't destroyed until the event_handler is destroyed static rsignaler window_close_signaler(window_table.get_mutex());
static window_table_type& window_table = *(new window_table_type); static rsignaler et_signaler(window_table.get_mutex());
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
// note that this is the thread that will perform all the event // note that this is the thread that will perform all the event
// processing. // processing.
...@@ -1335,7 +1333,6 @@ namespace dlib ...@@ -1335,7 +1333,6 @@ namespace dlib
) )
{ {
status = uninitialized; status = uninitialized;
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
} }
~event_handler_thread () ~event_handler_thread ()
...@@ -1351,15 +1348,6 @@ namespace dlib ...@@ -1351,15 +1348,6 @@ namespace dlib
wait(); wait();
} }
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct()
{
delete this;
} }
private: private:
...@@ -1450,7 +1438,7 @@ namespace dlib ...@@ -1450,7 +1438,7 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
static event_handler_thread* event_handler = new event_handler_thread; static event_handler_thread event_handler;
void init_gui_core () void init_gui_core ()
{ {
...@@ -1463,13 +1451,13 @@ namespace dlib ...@@ -1463,13 +1451,13 @@ namespace dlib
// start up the event handler thread // start up the event handler thread
event_handler->start(); event_handler.start();
// wait for the event thread to get up and running // wait for the event thread to get up and running
while (event_handler->status == event_handler_thread::uninitialized) while (event_handler.status == event_handler_thread::uninitialized)
et_signaler.wait(); et_signaler.wait();
if (event_handler->status == event_handler_thread::failure_to_init) if (event_handler.status == event_handler_thread::failure_to_init)
throw gui_error("Failed to start event thread"); throw gui_error("Failed to start event thread");
} }
} }
......
...@@ -63,11 +63,9 @@ namespace dlib ...@@ -63,11 +63,9 @@ namespace dlib
static int num_lock_mask = 0; static int num_lock_mask = 0;
static int scroll_lock_mask = 0; static int scroll_lock_mask = 0;
// put these objects on the heap because we want to ensure that they static window_table_type window_table;
// aren't destroyed until the event_handler_thread_object is destroyed static rsignaler window_close_signaler(window_table.get_mutex());
static window_table_type& window_table = *(new window_table_type); static rsignaler et_signaler(window_table.get_mutex());
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
struct user_event_type struct user_event_type
{ {
...@@ -1004,7 +1002,6 @@ namespace dlib ...@@ -1004,7 +1002,6 @@ namespace dlib
event_handler_thread( event_handler_thread(
) )
{ {
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
status = uninitialized; status = uninitialized;
} }
...@@ -1050,16 +1047,6 @@ namespace dlib ...@@ -1050,16 +1047,6 @@ namespace dlib
wait(); wait();
} }
} }
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct (
)
{
delete this;
} }
private: private:
...@@ -1250,7 +1237,7 @@ namespace dlib ...@@ -1250,7 +1237,7 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
static event_handler_thread* event_handler_thread_object = new event_handler_thread; static event_handler_thread event_handler_thread_object;
void init_gui_core () void init_gui_core ()
{ {
...@@ -1263,13 +1250,13 @@ namespace dlib ...@@ -1263,13 +1250,13 @@ namespace dlib
// start up the event handler thread // start up the event handler thread
event_handler_thread_object->start(); event_handler_thread_object.start();
// wait for the event thread to get up and running // wait for the event thread to get up and running
while (event_handler_thread_object->status == event_handler_thread::uninitialized) while (event_handler_thread_object.status == event_handler_thread::uninitialized)
et_signaler.wait(); et_signaler.wait();
if (event_handler_thread_object->status == event_handler_thread::failure_to_init) if (event_handler_thread_object.status == event_handler_thread::failure_to_init)
throw gui_error("Failed to initialize X11 resources"); throw gui_error("Failed to initialize X11 resources");
init_keyboard_mod_masks(); init_keyboard_mod_masks();
......
...@@ -13,33 +13,6 @@ namespace dlib ...@@ -13,33 +13,6 @@ namespace dlib
Unlike the other API wrappers you may make instances of these objects at the Unlike the other API wrappers you may make instances of these objects at the
global scope and call these functions before main() has been entered. global scope and call these functions before main() has been entered.
PROGRAM TERMINATION
When the main() function ends the program will wait until all outstanding
threads have terminated before allowing the program itself to terminate.
This means that if you want your program to actually be able to terminate
you have to ensure that all your threads will eventually end. To help you
make sure all your threads end you can use the register_program_ending_handler()
function. It allows you to register a member function that will be called after
main() has ended. Thus, you can register a function that can tell your
threads to end.
Note that you can't safely use the destructor of a global static object
to tell a thread to end. This is because the order in which global objects
in different translation units is undefined. So what may happen if you
attempt this is that dlib may begin to wait for threads to terminate (
in the main program thread, i.e. the one that executes global object's
destructors) before your object is destructed. Thus your program would
hang forever. So just use the register_thread_end_handler() function
instead of a global object's destructor if you have threads that will
survive beyond the end of main().
Finally, note that once main() ends C++ will start destructing global and
static objects so any threads making use of those resources may get into
trouble. So you probably just want to ensure that all your threads are
done *before* you try to terminate the program. But if that isn't possible
for whatever reason then you can use the register_program_ending_handler()
function to notify those threads that it is time to end.
THREAD POOL THREAD POOL
When threads end they go into a global thread pool and each waits there 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 for 30 seconds before timing out and having its resources returned to the
...@@ -133,33 +106,6 @@ namespace dlib ...@@ -133,33 +106,6 @@ namespace dlib
If this exception is thrown then the call to this function had no effect. If this exception is thrown then the call to this function had no effect.
!*/ !*/
// ----------------------------------------------------------------------------------------
template <
typename T
>
void register_program_ending_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 call register_program_ending_handler()
- handler does not block
ensures
- (obj.*handler)() will be called after main() has terminated.
- each call to this function adds another handler that will be called at
program termination. This means that if you call it a bunch of
times then you will end up registering multiple handlers (or single
handlers multiple times).
throws
- std::bad_alloc
If this exception is thrown then the call to this function had no effect.
!*/
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
bool create_new_thread ( bool create_new_thread (
......
...@@ -28,10 +28,23 @@ namespace dlib ...@@ -28,10 +28,23 @@ namespace dlib
threader& thread_pool ( threader& thread_pool (
) )
{ {
static threader thread_pool; static threader* thread_pool = new threader;
return thread_pool; return *thread_pool;
} }
// ----------------------------------------------------------------------------------------
struct threader_destruct_helper
{
// cause the thread pool to begin its destruction process when
// global objects start to be destroyed
~threader_destruct_helper()
{
thread_pool().destruct_when_ready();
}
};
static threader_destruct_helper a;
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
bool threader:: bool threader::
...@@ -49,11 +62,13 @@ namespace dlib ...@@ -49,11 +62,13 @@ namespace dlib
threader ( threader (
) : ) :
total_count(0), total_count(0),
function_pointer(0),
pool_count(0), pool_count(0),
data_ready(data_mutex), data_ready(data_mutex),
data_empty(data_mutex), data_empty(data_mutex),
destruct(false), destruct(false),
destructed(data_mutex) destructed(data_mutex),
should_destruct(false)
{} {}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -66,17 +81,6 @@ namespace dlib ...@@ -66,17 +81,6 @@ namespace dlib
destruct = true; destruct = true;
data_ready.broadcast(); data_ready.broadcast();
member_function_pointer<>::kernel_1a mfp;
// call all the handlers for anything that has registered for this event
while(queue_of_enders.size())
{
queue_of_enders.dequeue(mfp);
data_mutex.unlock();
mfp();
data_mutex.lock();
}
// wait for all the threads to end // wait for all the threads to end
while (total_count > 0) while (total_count > 0)
destructed.wait(); destructed.wait();
...@@ -86,12 +90,25 @@ namespace dlib ...@@ -86,12 +90,25 @@ namespace dlib
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
void threader:: void threader::
add_ender ( destruct_when_ready (
member_function_pointer<>::kernel_1a mfp
) )
{ {
auto_mutex M(data_mutex); data_mutex.lock();
queue_of_enders.enqueue(mfp);
// if there aren't any active threads, just maybe some sitting around
// in the pool then just destroy the threader
if (total_count == pool_count)
{
data_mutex.unlock();
delete this;
}
else
{
// in this case we just let the thread pool know that it
// should self destruct whenever it gets a chance
should_destruct = true;
data_mutex.unlock();
}
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
...@@ -180,7 +197,9 @@ namespace dlib ...@@ -180,7 +197,9 @@ namespace dlib
// get a reference to the calling threader object // get a reference to the calling threader object
threader& self = *reinterpret_cast<threader*>(object); threader& self = *reinterpret_cast<threader*>(object);
bool should_destroy_threader = false;
{
auto_mutex M(self.data_mutex); auto_mutex M(self.data_mutex);
// add this thread id // add this thread id
...@@ -244,7 +263,7 @@ namespace dlib ...@@ -244,7 +263,7 @@ namespace dlib
++self.pool_count; ++self.pool_count;
} }
if (self.destruct == true) if (self.destruct == true || self.should_destruct == true)
break; break;
// if we timed out and there isn't any work to do then // if we timed out and there isn't any work to do then
...@@ -264,6 +283,14 @@ namespace dlib ...@@ -264,6 +283,14 @@ namespace dlib
--self.total_count; --self.total_count;
self.destructed.signal(); self.destructed.signal();
if (self.should_destruct && self.total_count == 0)
should_destroy_threader = true;
} // end of auto_mutex M(self.data_mutex) block
if (should_destroy_threader)
delete &self;
} }
// ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------
......
...@@ -34,6 +34,8 @@ namespace dlib ...@@ -34,6 +34,8 @@ namespace dlib
- destructed is associated with the mutex data_mutex - destructed is associated with the mutex data_mutex
- destruct == false - destruct == false
- total_count == 0 - total_count == 0
- should_destruct == false
- function_pointer == 0
CONVENTION CONVENTION
- data_ready is associated with the mutex data_mutex - data_ready is associated with the mutex data_mutex
...@@ -55,13 +57,14 @@ namespace dlib ...@@ -55,13 +57,14 @@ namespace dlib
- else - else
- destruct == false - destruct == false
- queue_of_enders is locked by the data_mutex
- queue_of_enders == a set of member_function_pointers that should be called
when we want to end all the threads. these come from calls made to
register_program_ending_handler().
- thread_ids is locked by the data_mutex - thread_ids is locked by the data_mutex
- thread_ids == a set that contains the thread id for each thread spawned by this - thread_ids == a set that contains the thread id for each thread spawned by this
object. object.
- if (destruct_when_ready() has been called) then
- should_destruct == true
- else
- should_destruct == false
!*/ !*/
...@@ -72,9 +75,16 @@ namespace dlib ...@@ -72,9 +75,16 @@ namespace dlib
~threader ( ~threader (
); );
void add_ender ( void destruct_when_ready (
member_function_pointer<>::kernel_1a mfp
); );
/*!
ensures
- if (there are no threads currently running) then
- calls delete this
- else
- sets a flag that will cause the last thread to
call delete this when it finishes
!*/
bool create_new_thread ( bool create_new_thread (
void (*funct)(void*), void (*funct)(void*),
...@@ -144,7 +154,6 @@ namespace dlib ...@@ -144,7 +154,6 @@ namespace dlib
// private data // private data
set<thread_id_type,memory_manager<char>::kernel_2b>::kernel_1b_c thread_ids; set<thread_id_type,memory_manager<char>::kernel_2b>::kernel_1b_c thread_ids;
queue<member_function_pointer<>::kernel_1a>::kernel_1a queue_of_enders;
unsigned long total_count; unsigned long total_count;
void* parameter; void* parameter;
void (*function_pointer)(void*); void (*function_pointer)(void*);
...@@ -154,6 +163,7 @@ namespace dlib ...@@ -154,6 +163,7 @@ namespace dlib
signaler data_empty; // signaler to signal when the data is empty signaler data_empty; // signaler to signal when the data is empty
bool destruct; bool destruct;
signaler destructed; // signaler to signal when a thread has ended signaler destructed; // signaler to signal when a thread has ended
bool should_destruct;
struct registry_type struct registry_type
{ {
...@@ -241,21 +251,6 @@ namespace dlib ...@@ -241,21 +251,6 @@ namespace dlib
threads_kernel_shared::thread_pool().unregister_thread_end_handler(obj,handler); threads_kernel_shared::thread_pool().unregister_thread_end_handler(obj,handler);
} }
// ----------------------------------------------------------------------------------------
template <
typename T
>
inline void register_program_ending_handler (
T& obj,
void (T::*handler)()
)
{
member_function_pointer<>::kernel_1a mfp;
mfp.set(obj,handler);
threads_kernel_shared::thread_pool().add_ender(mfp);
}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
} }
......
...@@ -17,18 +17,8 @@ namespace dlib ...@@ -17,18 +17,8 @@ namespace dlib
shutdown(false), shutdown(false),
running(false) running(false)
{ {
register_program_ending_handler(*this,&timer_kernel_2_global_clock::destruct);
} }
// ----------------------------------------------------------------------------------------
void timer_kernel_2_global_clock::
destruct (
)
{
delete this;
}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
timer_kernel_2_global_clock:: timer_kernel_2_global_clock::
...@@ -193,8 +183,8 @@ namespace dlib ...@@ -193,8 +183,8 @@ namespace dlib
timer_kernel_2_global_clock& get_global_clock() timer_kernel_2_global_clock& get_global_clock()
{ {
static timer_kernel_2_global_clock* d = new timer_kernel_2_global_clock; static timer_kernel_2_global_clock d;
return *d; return d;
} }
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment