2
0
mirror of https://github.com/boostorg/thread.git synced 2026-01-24 18:32:32 +00:00
Files
thread/src/tss_hooks.cpp
2004-08-06 15:31:08 +00:00

185 lines
5.2 KiB
C++

// (C) Copyright Michael Glassford 2004.
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/thread/detail/config.hpp>
#if defined(BOOST_HAS_WINTHREADS)
#include <boost/thread/detail/tss_hooks.hpp>
#include <boost/assert.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/once.hpp>
#include <list>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace
{
typedef std::list<thread_exit_handler> thread_exit_handlers;
boost::once_flag once_init_threadmon_mutex = BOOST_ONCE_INIT;
boost::mutex* threadmon_mutex;
void init_threadmon_mutex(void)
{
threadmon_mutex = new boost::mutex;
if (!threadmon_mutex)
throw boost::thread_resource_error();
}
const DWORD invalid_tls_key = TLS_OUT_OF_INDEXES;
DWORD tls_key = invalid_tls_key;
unsigned long attached_thread_count = 0;
}
/*
Calls to DllMain() and tls_callback() are serialized by the OS;
however, calls to at_thread_exit are not, so it must be protected
by a mutex. Since we already need a mutex for at_thread_exit(),
and since there is no guarantee that on_process_enter(),
on_process_exit(), on_thread_enter(), and on_thread_exit()
will be called only from DllMain() or tls_callback(), it makes
sense to protect those, too.
*/
extern "C" BOOST_THREAD_DECL int at_thread_exit(
thread_exit_handler exit_handler
)
{
boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex);
boost::mutex::scoped_lock lock(*threadmon_mutex);
//Allocate a tls slot if necessary.
if (tls_key == invalid_tls_key)
tls_key = TlsAlloc();
if (tls_key == invalid_tls_key)
return -1;
//Get the exit handlers list for the current thread from tls.
thread_exit_handlers* exit_handlers =
static_cast<thread_exit_handlers*>(TlsGetValue(tls_key));
if (!exit_handlers)
{
//No exit handlers list was created yet.
try
{
//Attempt to create a new exit handlers list.
exit_handlers = new thread_exit_handlers;
if (!exit_handlers)
return -1;
//Attempt to store the list pointer in tls.
if (TlsSetValue(tls_key, exit_handlers))
++attached_thread_count;
else
{
delete exit_handlers;
return -1;
}
}
catch (...)
{
return -1;
}
}
//Like the C runtime library atexit() function,
//functions should be called in the reverse of
//the order they are added, so push them on the
//front of the list.
try
{
exit_handlers->push_front(exit_handler);
}
catch (...)
{
return -1;
}
//Like the atexit() function, a result of zero
//indicates success.
return 0;
}
extern "C" BOOST_THREAD_DECL void on_process_enter(void)
{
boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex);
boost::mutex::scoped_lock lock(*threadmon_mutex);
BOOST_ASSERT(attached_thread_count == 0);
}
extern "C" BOOST_THREAD_DECL void on_process_exit(void)
{
boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex);
boost::mutex::scoped_lock lock(*threadmon_mutex);
BOOST_ASSERT(attached_thread_count == 0);
//Free the tls slot if one was allocated.
if (tls_key != invalid_tls_key)
{
TlsFree(tls_key);
tls_key = invalid_tls_key;
}
}
extern "C" BOOST_THREAD_DECL void on_thread_enter(void)
{
//boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex);
//boost::mutex::scoped_lock lock(*threadmon_mutex);
}
extern "C" BOOST_THREAD_DECL void on_thread_exit(void)
{
boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex);
boost::mutex::scoped_lock lock(*threadmon_mutex);
//Get the exit handlers list for the current thread from tls.
if (tls_key == invalid_tls_key)
return;
thread_exit_handlers* exit_handlers =
static_cast<thread_exit_handlers*>(TlsGetValue(tls_key));
//If a handlers list was found, use it.
if (exit_handlers && TlsSetValue(tls_key, 0))
{
BOOST_ASSERT(attached_thread_count > 0);
--attached_thread_count;
lock.unlock();
//Call each handler and remove it from the list
while (!exit_handlers->empty())
{
if (thread_exit_handler exit_handler = *exit_handlers->begin())
(*exit_handler)();
exit_handlers->pop_front();
}
delete exit_handlers;
exit_handlers = 0;
}
}
#endif //defined(BOOST_HAS_WINTHREADS)