This is a little long question, but here we go. There is a version of FormatDateTime called thread safe because you use
GetLocaleFormatSettings(3081, FormatSettings);
to get the value, and then you can use it like this;
FormatDateTime('yyyy', 0, FormatSettings);
Now imagine two timers, one of which uses TTimer (interval, say, 1000 ms), and then the other timer created in this way (interval of 10 ms);
CreateTimerQueueTimer ( FQueueTimer, 0, TimerCallback, nil, 10, 10, WT_EXECUTEINTIMERTHREAD );
Now the Narly bit, if in the callback, as well as a timer event, you have the following code:
for i := 1 to 10000 do begin FormatDateTime('yyyy', 0, FormatSettings); end;
Please note that the appointment is missing. This leads to access violations almost instantly, sometimes after 20 minutes, regardless of whether in arbitrary places. Now, if you write this code in C ++ Builder, it never fails. The header conversion we are using is JEDI JwaXXXX. Even if we put locks in the Delphi version around the code, this only delays the inevitable. We looked at the source C header files, and it all looks good, is there any other way that C ++ uses the Delphi runtime? The streaming version of FormatDatTime is re-looking. Any ideas or thoughts from those who may have seen this before.
UPDATE:
To narrow it down a bit, FormatSettings is passed as const, so what does it matter if they use the same copy (as it turns out, passing the local version inside the call yeilds function the same problem)? Also, the version of FormatDateTime that accepts FormatSettings does not call GetThreadLocale, because it already has Locale information in the FormatSettings structure (I double check it through the code).
I mentioned the lack of a destination so that it is clear that access to shared memory is not performed, so no lock is required.
WT_EXECUTEINTIMERTHREAD is used to simplify the problem. I got the impression that you should use it only for very short tasks, because it can mean that it will skip the next interval if it starts something for a long time?
If you use plain old TThread, the problem does not occur. I suppose using TThread or TTimer works, but using a thread created outside of VCL is not, so I asked if there is a difference in how C ++ Builder uses VCL / Delphi RTL.
As an aside, this code, as mentioned earlier, also fails (but takes longer), after some time CS: = TCriticalSection.Create;
CS.Acquire; for i := 1 to LoopCount do begin FormatDateTime('yyyy', 0, FormatSettings); end; CS.Release;
And now for a bit that I really don't understand, I wrote it as suggested;
function ReturnAString: string; begin Result := 'Test'; UniqueString(Result); end;
and then inside each type of timer there is a code:
for i := 1 to 10000 do begin ReturnAString; end;
This leads to the same failures as I said before the error will never be in the same place inside the CPU window, etc. Sometimes this is a violation of access rights, and sometimes it can be a wrong pointer operation. I am using Delphi 2009 btw.
UPDATE 2:
Roddy (below) points to the Ontimer event (and, unfortunately, Winsock, i.e. TClientSocket), uses a Windows message pump (as a side, it would be nice to have some nice Winsock2 components using IOCP and Overlapping IO), therefore, push to get away from him. However, does anyone know how to determine which local thread store is configured on CreateQueueTimerQueue?
Thank you for taking the time to think and answer this problem.