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
// this variable holds a mapping from window handles to the base_window
// objects which represent them. Note that this objects mutex is always locked
// when inside the event loop.
// Also, put these objects on the heap because we want to ensure that they
// aren't destroyed until the event_handler is destroyed
static window_table_type& window_table = *(new window_table_type);
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
static window_table_type window_table;
static rsignaler window_close_signaler(window_table.get_mutex());
static rsignaler et_signaler(window_table.get_mutex());
// note that this is the thread that will perform all the event
// processing.
......@@ -1335,7 +1333,6 @@ namespace dlib
)
{
status = uninitialized;
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
}
~event_handler_thread ()
......@@ -1351,15 +1348,6 @@ namespace dlib
wait();
}
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct()
{
delete this;
}
private:
......@@ -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 ()
{
......@@ -1463,13 +1451,13 @@ namespace dlib
// start up the event handler thread
event_handler->start();
event_handler.start();
// 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();
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");
}
}
......
......@@ -63,11 +63,9 @@ namespace dlib
static int num_lock_mask = 0;
static int scroll_lock_mask = 0;
// put these objects on the heap because we want to ensure that they
// aren't destroyed until the event_handler_thread_object is destroyed
static window_table_type& window_table = *(new window_table_type);
static rsignaler& window_close_signaler = *(new rsignaler(window_table.get_mutex()));
static rsignaler& et_signaler = *(new rsignaler(window_table.get_mutex()));
static window_table_type window_table;
static rsignaler window_close_signaler(window_table.get_mutex());
static rsignaler et_signaler(window_table.get_mutex());
struct user_event_type
{
......@@ -1004,7 +1002,6 @@ namespace dlib
event_handler_thread(
)
{
register_program_ending_handler(*this, &event_handler_thread::self_destruct);
status = uninitialized;
}
......@@ -1050,16 +1047,6 @@ namespace dlib
wait();
}
}
delete &et_signaler;
delete &window_close_signaler;
delete &window_table;
}
void self_destruct (
)
{
delete this;
}
private:
......@@ -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 ()
{
......@@ -1263,13 +1250,13 @@ namespace dlib
// 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
while (event_handler_thread_object->status == event_handler_thread::uninitialized)
while (event_handler_thread_object.status == event_handler_thread::uninitialized)
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");
init_keyboard_mod_masks();
......
......@@ -13,33 +13,6 @@ namespace dlib
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.
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
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
......@@ -133,33 +106,6 @@ namespace dlib
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 (
......
......@@ -28,10 +28,23 @@ namespace dlib
threader& thread_pool (
)
{
static threader thread_pool;
return thread_pool;
static threader* thread_pool = new threader;
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::
......@@ -49,11 +62,13 @@ namespace dlib
threader (
) :
total_count(0),
function_pointer(0),
pool_count(0),
data_ready(data_mutex),
data_empty(data_mutex),
destruct(false),
destructed(data_mutex)
destructed(data_mutex),
should_destruct(false)
{}
// ----------------------------------------------------------------------------------------
......@@ -66,17 +81,6 @@ namespace dlib
destruct = true;
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
while (total_count > 0)
destructed.wait();
......@@ -86,12 +90,25 @@ namespace dlib
// ----------------------------------------------------------------------------------------
void threader::
add_ender (
member_function_pointer<>::kernel_1a mfp
destruct_when_ready (
)
{
auto_mutex M(data_mutex);
queue_of_enders.enqueue(mfp);
data_mutex.lock();
// 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
// get a reference to the calling threader object
threader& self = *reinterpret_cast<threader*>(object);
bool should_destroy_threader = false;
{
auto_mutex M(self.data_mutex);
// add this thread id
......@@ -244,7 +263,7 @@ namespace dlib
++self.pool_count;
}
if (self.destruct == true)
if (self.destruct == true || self.should_destruct == true)
break;
// if we timed out and there isn't any work to do then
......@@ -264,6 +283,14 @@ namespace dlib
--self.total_count;
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
- destructed is associated with the mutex data_mutex
- destruct == false
- total_count == 0
- should_destruct == false
- function_pointer == 0
CONVENTION
- data_ready is associated with the mutex data_mutex
......@@ -55,13 +57,14 @@ namespace dlib
- else
- 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 == a set that contains the thread id for each thread spawned by this
object.
- if (destruct_when_ready() has been called) then
- should_destruct == true
- else
- should_destruct == false
!*/
......@@ -72,9 +75,16 @@ namespace dlib
~threader (
);
void add_ender (
member_function_pointer<>::kernel_1a mfp
void destruct_when_ready (
);
/*!
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 (
void (*funct)(void*),
......@@ -144,7 +154,6 @@ namespace dlib
// private data
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;
void* parameter;
void (*function_pointer)(void*);
......@@ -154,6 +163,7 @@ namespace dlib
signaler data_empty; // signaler to signal when the data is empty
bool destruct;
signaler destructed; // signaler to signal when a thread has ended
bool should_destruct;
struct registry_type
{
......@@ -241,21 +251,6 @@ namespace dlib
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
shutdown(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::
......@@ -193,8 +183,8 @@ namespace dlib
timer_kernel_2_global_clock& get_global_clock()
{
static timer_kernel_2_global_clock* d = new timer_kernel_2_global_clock;
return *d;
static timer_kernel_2_global_clock 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