diff --git a/src/tss_hooks.cpp b/src/tss_hooks.cpp index 33c6dc54..133324ea 100644 --- a/src/tss_hooks.cpp +++ b/src/tss_hooks.cpp @@ -1,4 +1,6 @@ // (C) Copyright Michael Glassford 2004. +// Copyright (c) 2006 Peter Dimov +// // 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) @@ -7,178 +9,148 @@ #if defined(BOOST_HAS_WINTHREADS) - #include +#include - #include - #include - #include +#include +#include - #include +#include - #define WIN32_LEAN_AND_MEAN - #include +#define WIN32_LEAN_AND_MEAN +#include - namespace +namespace +{ + +typedef std::list thread_exit_handlers; + +const DWORD invalid_tls_key = TLS_OUT_OF_INDEXES; +DWORD tls_key = invalid_tls_key; + +boost::once_flag once_init_tls_key = BOOST_ONCE_INIT; + +void init_tls_key() +{ + tls_key = TlsAlloc(); +} + +} // unnamed namespace + +extern "C" BOOST_THREAD_DECL int at_thread_exit( thread_exit_handler exit_handler ) +{ + boost::call_once( init_tls_key, once_init_tls_key ); + + if( tls_key == invalid_tls_key ) { - typedef std::list 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; + return -1; } - /* - 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. - */ + // Get the exit handlers list for the current thread from tls. - extern "C" BOOST_THREAD_DECL int at_thread_exit( - thread_exit_handler exit_handler - ) + thread_exit_handlers* exit_handlers = + static_cast< thread_exit_handlers* >( TlsGetValue( tls_key ) ); + + if( exit_handlers == 0 ) { - 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(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. + // No exit handlers list was created yet. try { - exit_handlers->push_front(exit_handler); + // Attempt to create a new exit handlers list. + + exit_handlers = new thread_exit_handlers; + + if( exit_handlers == 0 ) + { + return -1; + } + + // Attempt to store the list pointer in tls. + + if( !TlsSetValue( tls_key, exit_handlers ) ) + { + delete exit_handlers; + return -1; + } } - catch (...) + 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); + // 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. - BOOST_ASSERT(attached_thread_count == 0); + try + { + exit_handlers->push_front( exit_handler ); + } + catch( ... ) + { + return -1; } - extern "C" BOOST_THREAD_DECL void on_process_exit(void) + // Like the atexit() function, a result of zero + // indicates success. + + return 0; +} + +extern "C" BOOST_THREAD_DECL void on_process_enter() +{ +} + +extern "C" BOOST_THREAD_DECL void on_process_exit() +{ +} + +extern "C" BOOST_THREAD_DECL void on_thread_enter() +{ +} + +extern "C" BOOST_THREAD_DECL void on_thread_exit() +{ + // Initializing tls_key here ensures its proper visibility + boost::call_once( init_tls_key, once_init_tls_key ); + + // Get the exit handlers list for the current thread from tls. + + if( tls_key == invalid_tls_key ) { - boost::call_once(init_threadmon_mutex, once_init_threadmon_mutex); - boost::mutex::scoped_lock lock(*threadmon_mutex); + return; + } - BOOST_ASSERT(attached_thread_count == 0); + thread_exit_handlers* exit_handlers = + static_cast< thread_exit_handlers* >( TlsGetValue( tls_key ) ); - //Free the tls slot if one was allocated. + // If a handlers list was found, invoke its handlers. - if (tls_key != invalid_tls_key) + if( exit_handlers != 0 ) + { + // Call each handler and remove it from the list + + while( !exit_handlers->empty() ) { - 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(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() ) { - if (thread_exit_handler exit_handler = *exit_handlers->begin()) - (*exit_handler)(); - exit_handlers->pop_front(); + (*exit_handler)(); } + exit_handlers->pop_front(); + } + + // If TlsSetValue fails, we can't delete the list, + // since a second call to on_thread_exit will try + // to access it. + + if( TlsSetValue( tls_key, 0 ) ) + { delete exit_handlers; - exit_handlers = 0; } } +} #endif //defined(BOOST_HAS_WINTHREADS)