Sign in Join

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

Mailing lists / fpc-pascal / 2010-07

writeln: FPC runtime race conditions in external C threads

17 messages · 3 people · –

Reply by email Subscribe to this list Your subscription

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

Alexander Grau

Hello,

While using an external C framework (Mac OS X, discrecording OS framework, FPC 2.4), I noticed that my FPC console app crashes when I use writeln's in both C threads (callbacks).

Example: Two FPC functions that are called from an external C library (c_callback1 and c_callback2) - callback2 is called within callback1 in another C thread (other than the FPC main thread).

// called from external C framework within the FPC main thread
c_callback1; cdecl;
begin
   writeln('callback1begin');
   // c_ callback2 is called here from another C thread
   writeln('callback1end');   <==   my FPC console app crashes here
( "EInOutError - Disk Full")
end;
// called from external C framework in another C thread
c_callback2; cdecl;
begin
   writeln('callback2begin');
   writeln('callback2end');
end;

An unhandled exception occurred at $000669E3 :
EInOutError : Disk Full
$000669E3 CALLBACK1, line xxx

Question: Why isn't it safe to call writeln's in non-FPC threads? Are there any other FPC runtime functions I should not call within non- FPC threads?

I'm going to make an FPC wiki page ('Threading Pitfalls') that will discuss these and other issues related to threading and I welcome all explanations on this so I can better understand this problem! :-)

Thanks,
Alexander

Michael Van Canneyt
to Alexander Grau

Basically, any heap memory access and standard pascal I/O are forbidden in threads started outside FPC, because the necessary thread structures are not initialized correctly.

Michael.

Alexander Grau wrote 47 lines
Hello, While using an external C framework (Mac OS X, discrecording OS framework, FPC 2.4), I noticed that my FPC console app crashes when I use writeln's in both C threads (callbacks). Example: Two FPC functions that are called from an external C library (c_callback1 and c_callback2) - callback2 is called within callback1 in another C thread (other than the FPC main thread). // called from external C framework within the FPC main thread c_callback1; cdecl; begin writeln('callback1begin'); // c_ callback2 is called here from another C thread writeln('callback1end'); <== my FPC console app crashes here ( "EInOutError - Disk Full") end; // called from external C framework in another C thread c_callback2; cdecl; begin writeln('callback2begin'); writeln('callback2end'); end; An unhandled exception occurred at $000669E3 : EInOutError : Disk Full $000669E3 CALLBACK1, line xxx Question: Why isn't it safe to call writeln's in non-FPC threads? Are there any other FPC runtime functions I should not call within non-FPC threads? I'm going to make an FPC wiki page ('Threading Pitfalls') that will discuss these and other issues related to threading and I welcome all explanations on this so I can better understand this problem! :-) Thanks, Alexander _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Alexander Grau
to Michael Van Canneyt
Michael Van Canneyt wrote 7 lines
Basically, any heap memory access and standard pascal I/O are forbidden in threads started outside FPC, because the necessary thread structures are not initialized correctly.

Is my assumption correct that this means I cannot use much of FPC then? As far as I can see, calling an object seems to be possible without any crash (aren't these on the heap too ?).

Due to these limitations, I think the best then would be to switch from the C callback thread into the FPC thread - how would that be (safely) possible?

Show quoted text 57 lines
Michael. On Mon, 12 Jul 2010, Alexander Grau wrote: > Hello, > > While using an external C framework (Mac OS X, discrecording OS > framework, FPC 2.4), I noticed that my FPC console app crashes when > I use writeln's in both C threads (callbacks). > > Example: Two FPC functions that are called from an external C > library (c_callback1 and c_callback2) - callback2 is called within > callback1 in another C thread (other than the FPC main thread). > > > // called from external C framework within the FPC main thread > c_callback1; cdecl; > begin > writeln('callback1begin'); > // c_ callback2 is called here from another C thread > writeln('callback1end'); <== my FPC console app crashes here > ( "EInOutError - Disk Full") > end; > > > // called from external C framework in another C thread > c_callback2; cdecl; > begin > writeln('callback2begin'); > writeln('callback2end'); > end; > > An unhandled exception occurred at $000669E3 : > EInOutError : Disk Full > $000669E3 CALLBACK1, line xxx > > > Question: Why isn't it safe to call writeln's in non-FPC > threads? Are there any other FPC runtime functions I should not > call within non-FPC threads? > > I'm going to make an FPC wiki page ('Threading Pitfalls') that > will discuss these and other issues related to threading and I > welcome all explanations on this so I can better understand this > problem! :-) > > Thanks, > Alexander > > _______________________________________________ > fpc-pascal maillist - > http://lists.freepascal.org/mailman/listinfo/fpc-pascal _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Best regards / Mit freundlichen Grüßen/順致最崇高的敬意 Alexander Grau

Grau GbR
Hardware & Software Solutions
Eschenweg 12
32609 Huellhorst
Germany

www.grauonline.de
Tel: +49 (0) 5741 2301259
Fax: +49 (0) 5741 235530
E-Mail:

Michael Van Canneyt
to Alexander Grau
Alexander Grau wrote 13 lines
Am 12.07.2010 um 13:24 schrieb Michael Van Canneyt: > > > Basically, any heap memory access and standard pascal I/O are forbidden in > threads started outside FPC, because the necessary thread structures are > not > initialized correctly. Is my assumption correct that this means I cannot use much of FPC then? As far as I can see, calling an object seems to be possible without any crash (aren't these on the heap too ?).

Class instances are allocated on the heap, yes. And strings as well.

Due to these limitations, I think the best then would be to switch from the C callback thread into the FPC thread - how would that be (safely) possible?

I haven't found a way to do this. I would love to, since it would mean that Apache modules would also work with apache thread workers.

Michael.

Alexander Grau
to Jonas Maebe
Jonas Maebe wrote 11 lines
On 12 Jul 2010, at 13:24, Michael Van Canneyt wrote: > Basically, any heap memory access and standard pascal I/O are > forbidden in > threads started outside FPC, because the necessary thread > structures are not > initialized correctly. I think that I've finally found a way to resolve this properly in r15557.

Wow, that's interesting! (was it so easy or was it already on your TODO/wish list ;-)) - I'll give it a try!

r15557 | jonas | 2010-07-12 18:16:50 +0200 (Mo, 12 Jul 2010) | 3 lines

+ added support for intialising/finalising threads not started via the FPC
runtime

Show quoted text 6 lines
Jonas_______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Alexander Grau
to Jonas Maebe: I think that I've finally found a way to resolve this properly in r15557. Jonas
Jonas Maebe wrote 11 lines
On 12 Jul 2010, at 13:24, Michael Van Canneyt wrote: > Basically, any heap memory access and standard pascal I/O are > forbidden in > threads started outside FPC, because the necessary thread > structures are not > initialized correctly. I think that I've finally found a way to resolve this properly in r15557.

This doesn't seem to work fully yet- let me try to create a simple test for reproducing my crash ;-) ...

-------------burn-------------
aquireExclusiveAccess
burn started
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=-100.00 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=0.00 currentTrack=0
druFileProc thread=B0185000
druFileProc thread=B0185000
druFileProc thread=B0185000
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=45.00 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=90.00 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=93.33 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=96.67 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStatePreparing progress=100.00 currentTrack=0
druProgressCallback threadid=A07F4F60 state=DRStatusStateSessionOpen progress=-100.00 currentTrack=0
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druFileProc thread=B0207000
druProgressCallback threadid=A07F4F60 state=DRStatusStateTrackOpen progress=end
An unhandled exception occurred at $0006977C :
EInOutError : Disk Full
$0006977C

Michael Van Canneyt
to Alexander Grau: Wow, that's interesting! (was it so easy or was it already on your TODO/wish list ;-)) -…
Alexander Grau wrote 23 lines
Am 12.07.2010 um 18:17 schrieb Jonas Maebe: > > On 12 Jul 2010, at 13:24, Michael Van Canneyt wrote: > >> Basically, any heap memory access and standard pascal I/O are forbidden in >> threads started outside FPC, because the necessary thread structures are >> not >> initialized correctly. > > I think that I've finally found a way to resolve this properly in r15557. Wow, that's interesting! (was it so easy or was it already on your TODO/wish list ;-)) - I'll give it a try! r15557 | jonas | 2010-07-12 18:16:50 +0200 (Mo, 12 Jul 2010) | 3 lines + added support for intialising/finalising threads not started via the FPC runtime

I don't see how this helps, because CRelocateThreadvar is only called from within the FPC thread creation routine, or am I wrong in that ?

Michael.

Jonas Maebe
to Alexander Grau: This doesn't seem to work fully yet- let me try to create a simple test for reproducing…

Alexander Grau wrote on Mon, 12 Jul 2010:

Show quoted text 13 lines
Am 12.07.2010 um 18:17 schrieb Jonas Maebe: > On 12 Jul 2010, at 13:24, Michael Van Canneyt wrote: > >> Basically, any heap memory access and standard pascal I/O are forbidden in >> threads started outside FPC, because the necessary thread >> structures are not >> initialized correctly. > > I think that I've finally found a way to resolve this properly in r15557. This doesn't seem to work fully yet- let me try to create a simple test for reproducing my crash ;-) ...

I forgot to mention: before this works, you have to create at least one thread using the FPC RTL in order to initialise the threading subsystem. It can be terminated/freed immediately, see the test program that was committed together with the fix for an example.

Jonas

---------------------------------------------------------------- This message was sent using IMP, the Internet Messaging Program.

Jonas Maebe
to Michael Van Canneyt: I don't see how this helps, because CRelocateThreadvar is only called from within the FPC…

Michael Van Canneyt wrote on Mon, 12 Jul 2010:

Show quoted text 9 lines
On Mon, 12 Jul 2010, Alexander Grau wrote: > r15557 | jonas | 2010-07-12 18:16:50 +0200 (Mo, 12 Jul 2010) | 3 lines > > + added support for intialising/finalising threads not started via the FPC > runtime I don't see how this helps, because CRelocateThreadvar is only called from within the FPC thread creation routine, or am I wrong in that ?

CRelocateThreadvar is called every time a threadvar is accessed. All the FPC thread creation routine does related to CRelocateThreadvar is to install it as "the" threadvar relocation routine the very first time you create a thread using the FPC RTL (hence the addendum I just posted).

Jonas

---------------------------------------------------------------- This message was sent using IMP, the Internet Messaging Program.

Alexander Grau
to Jonas Maebe: Alexander Grau wrote on Mon, 12 Jul 2010: I forgot to mention: before this works, you…
Jonas Maebe wrote 23 lines
Alexander Grau wrote on Mon, 12 Jul 2010: > Am 12.07.2010 um 18:17 schrieb Jonas Maebe: > >> On 12 Jul 2010, at 13:24, Michael Van Canneyt wrote: >> >>> Basically, any heap memory access and standard pascal I/O are >>> forbidden in >>> threads started outside FPC, because the necessary thread >>> structures are not >>> initialized correctly. >> >> I think that I've finally found a way to resolve this properly in >> r15557. > > This doesn't seem to work fully yet- let me try to create a simple > test for reproducing my crash ;-) ... I forgot to mention: before this works, you have to create at least one thread using the FPC RTL in order to initialise the threading subsystem. It can be terminated/freed immediately, see the test program that was committed together with the fix for an example.

That did the trick! Seems to work - I'm really curious how this works, since I assume there's probably no control over how the external C thread is created and so no chance to initialize something for the FPC runtime ...

Jonas Maebe
to Alexander Grau

Alexander Grau wrote on Mon, 12 Jul 2010:

Show quoted text 11 lines
Am 12.07.2010 um 22:31 schrieb Jonas Maebe: > I forgot to mention: before this works, you have to create at least > one thread using the FPC RTL in order to initialise the threading > subsystem. It can be terminated/freed immediately, see the test > program that was committed together with the fix for an example. That did the trick! Seems to work - I'm really curious how this works, since I assume there's probably no control over how the external C thread is created and so no chance to initialize something for the FPC runtime ...

The FPC runtime support for such a thread is initialised the first time you access a threadvar from that thread. And there's also a way to make libpthread automatically call a "destructor" routine when such a thread finishes (that's what I discovered today, and which was the missing piece of the puzzle).

Jonas

---------------------------------------------------------------- This message was sent using IMP, the Internet Messaging Program.

Michael Van Canneyt
to Jonas Maebe
Jonas Maebe wrote 18 lines
Alexander Grau wrote on Mon, 12 Jul 2010: > Am 12.07.2010 um 22:31 schrieb Jonas Maebe: > >> I forgot to mention: before this works, you have to create at least one >> thread using the FPC RTL in order to initialise the threading subsystem. >> It can be terminated/freed immediately, see the test program that was >> committed together with the fix for an example. > > That did the trick! Seems to work - I'm really curious how this works, > since I assume there's probably no control over how the external C thread > is created and so no chance to initialize something for the FPC runtime ... The FPC runtime support for such a thread is initialised the first time you access a threadvar from that thread. And there's also a way to make libpthread automatically call a "destructor" routine when such a thread finishes (that's what I discovered today, and which was the missing piece of the puzzle).

Jonas, if only I had known these 2 little details 5 years ago, when I needed it for Apache module support :/

Thank you very much. This is a great step forward !!

One small detail : maybe we should create a call 'inittheading' or make the 'IsMultithreaded' variable a property, that, when written, does the initialization ?

That's a bit cleaner than creating a thread ?

Michael.

Jonas Maebe
to Michael Van Canneyt

Michael Van Canneyt wrote on Mon, 12 Jul 2010:

Show quoted text 7 lines
On Mon, 12 Jul 2010, Jonas Maebe wrote: One small detail : maybe we should create a call 'inittheading' or make the 'IsMultithreaded' variable a property, that, when written, does the initialization ? That's a bit cleaner than creating a thread ?

I agree that would be a good idea.

Jonas

---------------------------------------------------------------- This message was sent using IMP, the Internet Messaging Program.

Michael Van Canneyt
to Jonas Maebe
Jonas Maebe wrote 11 lines
Michael Van Canneyt wrote on Mon, 12 Jul 2010: > On Mon, 12 Jul 2010, Jonas Maebe wrote: > > One small detail : maybe we should create a call 'inittheading' or make the > 'IsMultithreaded' variable a property, that, when written, does the > initialization ? > > That's a bit cleaner than creating a thread ? I agree that would be a good idea.

Which one has your preference, or should I do both ?

Michael.

Jonas Maebe
to Michael Van Canneyt

Michael Van Canneyt wrote on Mon, 12 Jul 2010:

Show quoted text 13 lines
On Mon, 12 Jul 2010, Jonas Maebe wrote: > Michael Van Canneyt wrote on Mon, 12 Jul 2010: > >> One small detail : maybe we should create a call 'inittheading' or make the >> 'IsMultithreaded' variable a property, that, when written, does the >> initialization ? >> >> That's a bit cleaner than creating a thread ? > > I agree that would be a good idea. Which one has your preference, or should I do both ?

Delphi also appears to have an IsMultiThreaded variable. Does setting it to "true" have a similar effect as what we are doing here? If so, I would go for the property, otherwise for the procedure.

Jonas

Jonas

---------------------------------------------------------------- This message was sent using IMP, the Internet Messaging Program.

Alexander Grau
to Jonas Maebe: Alexander Grau wrote on Mon, 12 Jul 2010: The FPC runtime support for such a thread is…
Jonas Maebe wrote 23 lines
Alexander Grau wrote on Mon, 12 Jul 2010: > Am 12.07.2010 um 22:31 schrieb Jonas Maebe: > >> I forgot to mention: before this works, you have to create at >> least one thread using the FPC RTL in order to initialise the >> threading subsystem. It can be terminated/freed immediately, see >> the test program that was committed together with the fix for an >> example. > > That did the trick! Seems to work - I'm really curious how this > works, since I assume there's probably no control over how the > external C thread is created and so no chance to initialize > something for the FPC runtime ... The FPC runtime support for such a thread is initialised the first time you access a threadvar from that thread. And there's also a way to make libpthread automatically call a "destructor" routine when such a thread finishes (that's what I discovered today, and which was the missing piece of the puzzle). Jonas

I think that's excellent - how about the other (non-Unix) OS' s FPC supports, I assume they have the same problem with externally created threads? (e.g. has there already been found a similar solution for Win32 ?)