Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2010-11

Question regarding thread finalization

4 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Sven Barth

Hello together!

As you might now, I'm working on a (according to Michael maybe temporary) solution for the clean up of external threads (see http://bugs.freepascal.org/view.php?id=17300 ).

My current state is that the collector thread which is used inside a "program" is working correctly and now I'm going to tackle the problem of DLLs.

I've looked at the necessary include files some time now and I might have spotted a potential problem with thread finalization in DLLs.

Consider the following: in a DLL we start a thread and once it has done its duty we call "EndThread" and return from the thread method (I'm thinking in terms of "BeginThread" not "TThread" for this example). After we've returned from the method Windows will call our DLL's entry point (and that of any other loaded DLL) with DLL_THREAD_DETACH where we call "DoneThread" because the current thread is not the "main" thread (another interesting story...).
The call of "DoneThread" is decorated with a comment "Assume everything is idempotent there". I'm hereby proposing that this assumption is not correct.

I've looked at "EndThread" and "DoneThread". "EndThread" calls the thread manager's "EndThread" method which on Windows calls "DoneThread"(!) and the API method "ExitThread".

"DoneThread" finalizes the RTL for that thread and starts with the WideString manager (not looked into that) and continues with the heap. Now the first thing the heap does is getting the address of its "freelists" record (which is a threadvar!) and plays around a bit with its contents (not really important for this discussion). "DoneThread" then continues finalizing other parts of the RTL and finally calls the thread managers "ReleaseThreadVars" method.

I sincerely hope that every alarm bell rings now. By calling "DoneThread" twice in a DLL we are basically accessing already freed memory: the thread var data.

As this is a very serious problem I now ask you how this should be solved:
1) keep it as it is (I hope not O.o)
2) fullfill the assumption and check the code which is called by "DoneThread" to be idempotent (which can break again at any time)
3) stop assuming that "DoneThread" is idempotent and do some checks here. E.g. by checking whether TlsGetValue(TLSKey) is Nil before calling "DoneThread" in DLL_THREAD_DETACH, which means that a) the RTL was never initialised (a thread that was created before DLL_PROCESS_ATTACH and never called into the DLL's code - see below) or b) the thread was already finalized.

Problem 3a is the following:
A thread is created in an application. Now the main thread loads a FPC library and some time after that the thread exits (it has never called any method in the DLL). Now the DLL receives a DLL_THREAD_DETACH and currently blindly finalizes a never initialized RTL (because it has never received a DLL_THREAD_ATTACH call for this thread!).

Modifying the RTL in this way might also solve the external thread problem for DLLs (I still have to think that one through, though).

Regards,
Sven

Reply by email

Michael Van Canneyt
to Sven Barth

Pierre muller has a better solution.

He uses TLS callbacks. His solution is finished, but is in need of testing. If you want, I can send you his patch, and then you can test it too.

As for the DoneThread: your solution number 3 is the one to take. It is by far the safest. We'll need to implement it anyway like that to fix the case of the TLS done callback and the DLL_DETACH_THREAD.

Michael.

Sven Barth wrote 66 lines
Hello together! As you might now, I'm working on a (according to Michael maybe temporary) solution for the clean up of external threads (see http://bugs.freepascal.org/view.php?id=17300 ). My current state is that the collector thread which is used inside a "program" is working correctly and now I'm going to tackle the problem of DLLs. I've looked at the necessary include files some time now and I might have spotted a potential problem with thread finalization in DLLs. Consider the following: in a DLL we start a thread and once it has done its duty we call "EndThread" and return from the thread method (I'm thinking in terms of "BeginThread" not "TThread" for this example). After we've returned from the method Windows will call our DLL's entry point (and that of any other loaded DLL) with DLL_THREAD_DETACH where we call "DoneThread" because the current thread is not the "main" thread (another interesting story...). The call of "DoneThread" is decorated with a comment "Assume everything is idempotent there". I'm hereby proposing that this assumption is not correct. I've looked at "EndThread" and "DoneThread". "EndThread" calls the thread manager's "EndThread" method which on Windows calls "DoneThread"(!) and the API method "ExitThread". "DoneThread" finalizes the RTL for that thread and starts with the WideString manager (not looked into that) and continues with the heap. Now the first thing the heap does is getting the address of its "freelists" record (which is a threadvar!) and plays around a bit with its contents (not really important for this discussion). "DoneThread" then continues finalizing other parts of the RTL and finally calls the thread managers "ReleaseThreadVars" method. I sincerely hope that every alarm bell rings now. By calling "DoneThread" twice in a DLL we are basically accessing already freed memory: the thread var data. As this is a very serious problem I now ask you how this should be solved: 1) keep it as it is (I hope not O.o) 2) fullfill the assumption and check the code which is called by "DoneThread" to be idempotent (which can break again at any time) 3) stop assuming that "DoneThread" is idempotent and do some checks here. E.g. by checking whether TlsGetValue(TLSKey) is Nil before calling "DoneThread" in DLL_THREAD_DETACH, which means that a) the RTL was never initialised (a thread that was created before DLL_PROCESS_ATTACH and never called into the DLL's code - see below) or b) the thread was already finalized. Problem 3a is the following: A thread is created in an application. Now the main thread loads a FPC library and some time after that the thread exits (it has never called any method in the DLL). Now the DLL receives a DLL_THREAD_DETACH and currently blindly finalizes a never initialized RTL (because it has never received a DLL_THREAD_ATTACH call for this thread!). Modifying the RTL in this way might also solve the external thread problem for DLLs (I still have to think that one through, though). Regards, Sven _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Sven Barth
to Michael Van Canneyt
Michael Van Canneyt wrote 6 lines
Pierre muller has a better solution. He uses TLS callbacks. His solution is finished, but is in need of testing. If you want, I can send you his patch, and then you can test it too.

Yes, I'd like to test that. Especially as I'm curious how he's done that :D

As for the DoneThread: your solution number 3 is the one to take. It is by far the safest. We'll need to implement it anyway like that to fix the case of the TLS done callback and the DLL_DETACH_THREAD.

Great :D

Regards,
Sven

Show quoted text 75 lines
Michael. On Sat, 27 Nov 2010, Sven Barth wrote: > Hello together! > > As you might now, I'm working on a (according to Michael maybe > temporary) solution for the clean up of external threads (see > http://bugs.freepascal.org/view.php?id=17300 ). > > My current state is that the collector thread which is used inside a > "program" is working correctly and now I'm going to tackle the problem > of DLLs. > > I've looked at the necessary include files some time now and I might > have spotted a potential problem with thread finalization in DLLs. > > Consider the following: in a DLL we start a thread and once it has > done its duty we call "EndThread" and return from the thread method > (I'm thinking in terms of "BeginThread" not "TThread" for this example). > After we've returned from the method Windows will call our DLL's entry > point (and that of any other loaded DLL) with DLL_THREAD_DETACH where > we call "DoneThread" because the current thread is not the "main" > thread (another interesting story...). > The call of "DoneThread" is decorated with a comment "Assume > everything is idempotent there". I'm hereby proposing that this > assumption is not correct. > > I've looked at "EndThread" and "DoneThread". "EndThread" calls the > thread manager's "EndThread" method which on Windows calls > "DoneThread"(!) and the API method "ExitThread". > > "DoneThread" finalizes the RTL for that thread and starts with the > WideString manager (not looked into that) and continues with the heap. > Now the first thing the heap does is getting the address of its > "freelists" record (which is a threadvar!) and plays around a bit with > its contents (not really important for this discussion). "DoneThread" > then continues finalizing other parts of the RTL and finally calls the > thread managers "ReleaseThreadVars" method. > > I sincerely hope that every alarm bell rings now. By calling > "DoneThread" twice in a DLL we are basically accessing already freed > memory: the thread var data. > > As this is a very serious problem I now ask you how this should be > solved: > 1) keep it as it is (I hope not O.o) > 2) fullfill the assumption and check the code which is called by > "DoneThread" to be idempotent (which can break again at any time) > 3) stop assuming that "DoneThread" is idempotent and do some checks > here. E.g. by checking whether TlsGetValue(TLSKey) is Nil before > calling "DoneThread" in DLL_THREAD_DETACH, which means that a) the RTL > was never initialised (a thread that was created before > DLL_PROCESS_ATTACH and never called into the DLL's code - see below) > or b) the thread was already finalized. > > Problem 3a is the following: > A thread is created in an application. Now the main thread loads a FPC > library and some time after that the thread exits (it has never called > any method in the DLL). Now the DLL receives a DLL_THREAD_DETACH and > currently blindly finalizes a never initialized RTL (because it has > never received a DLL_THREAD_ATTACH call for this thread!). > > Modifying the RTL in this way might also solve the external thread > problem for DLLs (I still have to think that one through, though). > > Regards, > Sven > _______________________________________________ > fpc-devel maillist - > http://lists.freepascal.org/mailman/listinfo/fpc-devel > _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply by email

Unknown author
to Sven Barth
Sven Barth wrote 9 lines
On 27.11.2010 18:12, Michael Van Canneyt wrote: > > Pierre muller has a better solution. > > He uses TLS callbacks. His solution is finished, but is in need of testing. > If you want, I can send you his patch, and then you can test it too. > Yes, I'd like to test that. Especially as I'm curious how he's done that :D

I sent it in private.

Michael.

Reply by email