summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/threads/threads_kernel_shared.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/threads/threads_kernel_shared.cpp')
-rw-r--r--ml/dlib/dlib/threads/threads_kernel_shared.cpp318
1 files changed, 318 insertions, 0 deletions
diff --git a/ml/dlib/dlib/threads/threads_kernel_shared.cpp b/ml/dlib/dlib/threads/threads_kernel_shared.cpp
new file mode 100644
index 000000000..8e81193e9
--- /dev/null
+++ b/ml/dlib/dlib/threads/threads_kernel_shared.cpp
@@ -0,0 +1,318 @@
+// 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_CPp_
+#define DLIB_THREADS_KERNEL_SHARED_CPp_
+
+#include "threads_kernel_shared.h"
+#include "../assert.h"
+#include "../platform.h"
+#include <iostream>
+
+
+#ifndef DLIB_THREAD_POOL_TIMEOUT
+// default to 30000 milliseconds
+#define DLIB_THREAD_POOL_TIMEOUT 30000
+#endif
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+// threader functions
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ namespace threads_kernel_shared
+ {
+
+ bool thread_pool_has_been_destroyed = false;
+
+// ----------------------------------------------------------------------------------------
+
+ 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_if_ready();
+ }
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ threader& thread_pool (
+ )
+ {
+ static threader* thread_pool = new threader;
+ static threader_destruct_helper a;
+ return *thread_pool;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ bool threader::
+ is_dlib_thread (
+ thread_id_type id
+ )
+ {
+ auto_mutex M(data_mutex);
+ return thread_ids.is_member(id);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ threader::
+ threader (
+ ) :
+ total_count(0),
+ function_pointer(0),
+ pool_count(0),
+ data_ready(data_mutex),
+ data_empty(data_mutex),
+ destruct(false),
+ destructed(data_mutex),
+ do_not_ever_destruct(false)
+ {
+#ifdef WIN32
+ // Trying to destroy the global thread pool when we are part of a DLL and the
+ // DLL is being unloaded can sometimes lead to weird behavior. For example, in
+ // the python interpreter you will get the interpreter to hang. Or if we are
+ // part of a MATLAB mex file and the file is being unloaded there can also be
+ // similar weird issues. So when we are using dlib on windows we just disable
+ // the destruction of the global thread pool since it doesn't matter anyway.
+ // It's resources will just get freed by the OS. This is even the recommended
+ // thing to do by Microsoft (http://blogs.msdn.com/b/oldnewthing/archive/2012/01/05/10253268.aspx).
+ //
+ // As an aside, it's worth pointing out that the reason we try and free
+ // resources on program shutdown on other operating systems is so we can have
+ // clean reports from tools like valgrind which check for memory leaks. But
+ // trying to do this on windows is a lost cause so we give up in this case and
+ // follow the Microsoft recommendation.
+ do_not_ever_destruct = true;
+#endif // WIN32
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ threader::
+ ~threader (
+ )
+ {
+ data_mutex.lock();
+ destruct = true;
+ data_ready.broadcast();
+
+ // wait for all the threads to end
+ while (total_count > 0)
+ destructed.wait();
+
+ thread_pool_has_been_destroyed = true;
+ data_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void threader::
+ destruct_if_ready (
+ )
+ {
+ if (do_not_ever_destruct)
+ return;
+
+ 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)
+ {
+ destruct = true;
+ data_ready.broadcast();
+ data_mutex.unlock();
+ delete this;
+ }
+ else
+ {
+ // There are still some user threads running so there isn't
+ // much we can really do. Just let the program end without
+ // cleaning up threading resources.
+ data_mutex.unlock();
+ }
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void threader::
+ call_end_handlers (
+ )
+ {
+ reg.m.lock();
+ const thread_id_type id = get_thread_id();
+ thread_id_type id_copy;
+ member_function_pointer<> mfp;
+
+ // Remove all the member function pointers for this thread from the tree
+ // and call them.
+ while (reg.reg[id] != 0)
+ {
+ reg.reg.remove(id,id_copy,mfp);
+ reg.m.unlock();
+ mfp();
+ reg.m.lock();
+ }
+ reg.m.unlock();
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ bool threader::
+ create_new_thread (
+ void (*funct)(void*),
+ void* param
+ )
+ {
+
+ // get a lock on the data mutex
+ auto_mutex M(data_mutex);
+
+ // loop to ensure that the new function pointer is in the data
+ while (true)
+ {
+ // if the data is empty then add new data and quit loop
+ if (function_pointer == 0)
+ {
+ parameter = param;
+ function_pointer = funct;
+ break;
+ }
+ else
+ {
+ // wait for data to become empty
+ data_empty.wait();
+ }
+ }
+
+
+ // get a thread for this new data
+ // if a new thread must be created
+ if (pool_count == 0)
+ {
+ // make thread and add it to the pool
+ if ( threads_kernel_shared_helpers::spawn_thread(thread_starter, this) == false )
+ {
+ function_pointer = 0;
+ parameter = 0;
+ data_empty.signal();
+ return false;
+ }
+ ++total_count;
+ }
+ // wake up a thread from the pool
+ else
+ {
+ data_ready.signal();
+ }
+
+ return true;
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ void thread_starter (
+ void* object
+ )
+ {
+ // get a reference to the calling threader object
+ threader& self = *static_cast<threader*>(object);
+
+
+ {
+ auto_mutex M(self.data_mutex);
+
+ // add this thread id
+ thread_id_type thread_id = get_thread_id();
+ self.thread_ids.add(thread_id);
+
+ // indicate that this thread is now in the thread pool
+ ++self.pool_count;
+
+ while (self.destruct == false)
+ {
+ // if data is ready then process it and launch the thread
+ // if its not ready then go back into the pool
+ while (self.function_pointer != 0)
+ {
+ // indicate that this thread is now out of the thread pool
+ --self.pool_count;
+
+ // get the data for the function call
+ void (*funct)(void*) = self.function_pointer;
+ void* param = self.parameter;
+ self.function_pointer = 0;
+
+ // signal that the data is now empty
+ self.data_empty.signal();
+
+ self.data_mutex.unlock();
+ // Call funct with its intended parameter. If this function throws then
+ // we intentionally let the exception escape the thread and result in whatever
+ // happens when it gets caught by the OS (generally the program is terminated).
+ funct(param);
+ self.call_end_handlers();
+
+ self.data_mutex.lock();
+
+ // indicate that this thread is now back in the thread pool
+ ++self.pool_count;
+ }
+
+ if (self.destruct == true)
+ break;
+
+ // if we timed out and there isn't any work to do then
+ // this thread will quit this loop and end.
+ if (self.data_ready.wait_or_timeout(DLIB_THREAD_POOL_TIMEOUT) == false &&
+ self.function_pointer == 0)
+ break;
+
+ }
+
+ // remove this thread id from thread_ids
+ thread_id = get_thread_id();
+ self.thread_ids.destroy(thread_id);
+
+ // indicate that this thread is now out of the thread pool
+ --self.pool_count;
+ --self.total_count;
+
+ self.destructed.signal();
+
+ } // end of auto_mutex M(self.data_mutex) block
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ bool is_dlib_thread (
+ thread_id_type id
+ )
+ {
+ return threads_kernel_shared::thread_pool().is_dlib_thread(id);
+ }
+
+ bool is_dlib_thread (
+ )
+ {
+ return is_dlib_thread(get_thread_id());
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_THREADS_KERNEL_SHARED_CPp_
+