The first thing I discovered when trying to get threads working is that the -mthreads option was not being used in jam. I added the following to the gcc-tools.jam file. I'm not totally sure it's right -- it's needed for mingw32, but I don't know if it is necessary for cygwin. if $(NT) { flags gcc CFLAGS <threading>multi : -mthreads ; flags gcc LINKFLAGS <threading>multi : -mthreads ; } Adding that let's gdb actually work on it... It doesn't crash deep in the bowels of thread exiting in some kernel library routine. Although it still crashes in on_thread_exit doing handlers->push_front(func). Actually, it crashes in some STL stuff, but that's I'm not sure why, I'm not that good with gdb yet. After digging through the implementation of threadmon.cpp, I realized that (unless I missed something) on_thread_exit was always called with the same argument, which is the address of cleanup in tss.cpp. Therefore all of the stuff in threadmon.cpp for keeping a list of functions registered with on_thread exit were not really needed, it would always have a list of the same pointer. So why not just keep that pointer and call it on thread exit. I also discovered that DllMain wasn't getting called at all because it wasn't defined as extern "C". This drastically simplifies threadmon.cpp: // threadmon.cpp : Defines the entry point for the DLL application. // #define BOOST_THREADMON_EXPORTS #include "threadmon.hpp" #ifdef BOOST_HAS_WINTHREADS #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <windows.h> #ifdef BOOST_MSVC # pragma warning(disable : 4786) #endif typedef void (__cdecl * handler)(void); namespace { handler exit_handler; } #if defined(__BORLANDC__) #define DllMain DllEntryPoint #endif extern "C" BOOL APIENTRY DllMain(HANDLE module, DWORD reason, LPVOID) { switch (reason) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: { if (exit_handler) (*exit_handler)(); } break; } return TRUE; } int on_thread_exit(void (__cdecl * func)(void)) { exit_handler = func; return 0; } #endif // BOOST_HAS_WINTHREADS Note that the above was not copied directly, but has had some editing. I think I transcribed it correctly, but maybe I missed a semicolon or something. Also, in tss.cpp, cleanup may be called without any handlers registered... I'm not sure if this was always the case, or if I introduced this case, so the cleanup function needs to do an if(handlers) check before actually trying to call them. And now the tests pass! Wheee! One more thing. I'm still not convinced for the need of the dll. The issue is needing to know when a thread has exited, and I think that could be done by an object that was destructed in a thread-proxy function. Since you are already using a thread_proxy function, I'm wondering if you considered this approach and if so, why you rejected it.
--- In Boost-Users@y..., "bkarsjens" <karsjens+yahoo@v...> wrote:
The first thing I discovered when trying to get threads working is that the -mthreads option was not being used in jam. I added the following to the gcc-tools.jam file. I'm not totally sure it's right -- it's needed for mingw32, but I don't know if it is necessary for cygwin.
if $(NT) { flags gcc CFLAGS <threading>multi : -mthreads ; flags gcc LINKFLAGS <threading>multi : -mthreads ; }
Thanks. This may help me with the current problem we're having with gcc.
Adding that let's gdb actually work on it... It doesn't crash deep in the bowels of thread exiting in some kernel library routine. Although it still crashes in on_thread_exit doing handlers->push_front (func). Actually, it crashes in some STL stuff, but that's I'm not sure why, I'm not that good with gdb yet.
After digging through the implementation of threadmon.cpp, I realized that (unless I missed something) on_thread_exit was always called with the same argument, which is the address of cleanup in tss.cpp. Therefore all of the stuff in threadmon.cpp for keeping a list of functions registered with on_thread exit were not really needed, it would always have a list of the same pointer. So why not just keep that pointer and call it on thread exit. I also discovered that DllMain wasn't getting called at all because it wasn't defined as extern "C". This drastically simplifies threadmon.cpp:
on_thread_exit, even though it's an implementation detail, is a generalized concept that may be reused in the future. For instance, I'm considering a static thread::atexit() member. [snip code that changed threadmon to not use a list of registered functions]
Also, in tss.cpp, cleanup may be called without any handlers registered... I'm not sure if this was always the case, or if I introduced this case, so the cleanup function needs to do an if(handlers) check before actually trying to call them.
I don't quite follow this one, but I'll research it.
And now the tests pass! Wheee!
Great! I'll see if I can follow this message in order to get things working in CVS.
One more thing. I'm still not convinced for the need of the dll. The issue is needing to know when a thread has exited, and I think that could be done by an object that was destructed in a thread-proxy function. Since you are already using a thread_proxy function, I'm wondering if you considered this approach and if so, why you rejected it.
Because of the need for "thread adoption". Thread A is created outside of Boost.Threads. A callback is registered with Thread A that uses Boost.Thread's thread_specific_ptr<>. The memory allocated is never recovered since Thread A wasn't created by Boost.Threads and so isn't using the proxy. Bill Kempf
--- In Boost-Users@y..., "bill_kempf" <williamkempf@h...> wrote:
--- In Boost-Users@y..., "bkarsjens" <karsjens+yahoo@v...> wrote:
After digging through the implementation of threadmon.cpp, I
realized
that (unless I missed something) on_thread_exit was always called with the same argument, which is the address of cleanup in tss.cpp. Therefore all of the stuff in threadmon.cpp for keeping a list of functions registered with on_thread exit were not really needed, it would always have a list of the same pointer. So why not just keep that pointer and call it on thread exit. I also discovered that DllMain wasn't getting called at all because it wasn't defined as extern "C". This drastically simplifies threadmon.cpp:
on_thread_exit, even though it's an implementation detail, is a generalized concept that may be reused in the future. For instance, I'm considering a static thread::atexit() member.
[snip code that changed threadmon to not use a list of registered functions]
You still don't really need the registered functions in the DLL. You could have thread::atexit() add it's function to the same list as the thread-specific pointers. The cleanup_handlers type could be the current map as well as a set of functions to call at exit.
Also, in tss.cpp, cleanup may be called without any handlers registered... I'm not sure if this was always the case, or if I introduced this case, so the cleanup function needs to do an if(handlers) check before actually trying to call them.
I don't quite follow this one, but I'll research it.
The only thing I can figure out is that get_handlers() was never called for the current thread. Since My version of the DLL get the cleanup functions address and never forgets it, I suppose it is possible for a new thread that doesn't use tss stuff to never call get_handlers, but the cleanup function would be called for it anyway. And just for the sake of clarity: void __cdecl cleanup() { cleanup_handlers* handlers = static_cast<cleanup_handlers*>(TlsGetValue(key)); if (handlers) for (cleanup_handlers::iterator it = handlers->begin(); it != handlers->end(); ++it) { cleanup_info info = it->second; if (info.second) info.first(info.second); } delete handlers; }
And now the tests pass! Wheee!
Great! I'll see if I can follow this message in order to get things working in CVS.
Let me know, and I'll give it a test drive.
One more thing. I'm still not convinced for the need of the dll. The issue is needing to know when a thread has exited, and I think that could be done by an object that was destructed in a thread-proxy function. Since you are already using a thread_proxy function, I'm wondering if you considered this approach and if so, why you rejected it.
Because of the need for "thread adoption". Thread A is created outside of Boost.Threads. A callback is registered with Thread A that uses Boost.Thread's thread_specific_ptr<>. The memory allocated is never recovered since Thread A wasn't created by Boost.Threads and so isn't using the proxy.
And the light finally dawns on me... I guess the only other way to do this would be to create another thread that waits for the thread handles of other threads created, and that would probably get complicated fast.
Bill Kempf
--- In Boost-Users@y..., "bill_kempf" <williamkempf@h...> wrote:
--- In Boost-Users@y..., "bkarsjens" <karsjens+yahoo@v...> wrote:
After digging through the implementation of threadmon.cpp, I
realized
that (unless I missed something) on_thread_exit was always called with the same argument, which is the address of cleanup in tss.cpp. Therefore all of the stuff in threadmon.cpp for keeping a list of functions registered with on_thread exit were not really needed, it would always have a list of the same pointer. So why not just keep that pointer and call it on thread exit. I also discovered that DllMain wasn't getting called at all because it wasn't defined as extern "C". This drastically simplifies threadmon.cpp:
on_thread_exit, even though it's an implementation detail, is a generalized concept that may be reused in the future. For instance, I'm considering a static thread::atexit() member.
[snip code that changed threadmon to not use a list of registered functions]
You still don't really need the registered functions in the DLL. You could have thread::atexit() add it's function to the same list as
--- In Boost-Users@y..., "bkarsjens" <karsjens+yahoo@v...> wrote: the
thread-specific pointers. The cleanup_handlers type could be the current map as well as a set of functions to call at exit.
One more thing. I'm still not convinced for the need of the
The
issue is needing to know when a thread has exited, and I think
This just makes the code harder to decipher, IMHO. In the end none of this matters, though, as it's simply part of the implementation. dll. that
could be done by an object that was destructed in a thread-proxy function. Since you are already using a thread_proxy function, I'm wondering if you considered this approach and if so, why you rejected it.
Because of the need for "thread adoption". Thread A is created outside of Boost.Threads. A callback is registered with Thread A that uses Boost.Thread's thread_specific_ptr<>. The memory allocated is never recovered since Thread A wasn't created by Boost.Threads and so isn't using the proxy.
And the light finally dawns on me... I guess the only other way to do this would be to create another thread that waits for the thread handles of other threads created, and that would probably get complicated fast.
Not to mention it would be a tremendous waste of resources ;). Bill Kempf
participants (2)
-
bill_kempf
-
bkarsjens