In Delphi 2009 and Windows API, is there a way to detect that a particular piece of code is running in the context of a secondary thread? In pseudocode, I would like to be able to say:
procedure DoSomething;
begin
if InvokedBySecondaryThread then
DoIt_ThreadSafeWay
else
DoIt_RegularWay;
end;
This is for a logging library which I wrote and have used for years, and am now trying to adapt to a situation where one procedure can be invoked from multiple threads. My "regular way" is not threadsafe. I know how to make it threadsafe, but I'd like to use the threadsafe method only when actually necessary.
Explanation (not a required reading :-)
It boils down to a choice between using SendMessage and PostMessage to dispatch a logged message to multiple receivers, such as a logfile, console or a VCL control. Using PostMessage means that the messages will not be received while a long blocking operation is in progress, which defeats the purpose of logging somewhat, esp. when used to indicate progress. I suppose I could protect the SendMessage call with a critical section, but again I would prefer to have to do it only when truly required.
I know about the global var IsMultiThread in system.pas, but this will only tell me that the application has started secondary threads. These may be threads created by 3rd party libraries, and as such they will never access "my" code, so their existence will not affect my logging logic.
I really wish I could use the same low-level library code regardless of whether it is invoked from one or multiple threads. It would be easy for example to call modified, threadsafe logging procedures from inside secondary threads, but this would duplicate much code, and I'd still have to remember to always do the right thing.
@Lieven: Currently, the logging logic goes like this, somewhat simplified
I want the logging to be as painless as possible, with minimal setup code and no worrying about managing object lifetime, so the library only exposes a number of overloaded helper procedures, such as
procedure Log( const msgText : string; level : TLogLevel = lvNotice ); overload;
procedure Log( const msgText : string; Args : array of const; level : TLogLevel = lvNotice ); overload;
etc, including specialized routines that log a StringList, a boolean, an Exception and so on
Pretty much everything else happens in the implementation of the unit. All the helper routines eventually end up calling
procedure _LogPostMessage( const msgText : string; level : TLogLevel );
which (a) checks if the singleton dispatcher object has been initialized; (b) creates an instance of a TLogMessagePacket object (container for the message text, timestamp etc.), and finally (c) does a SendMessage or PostMessage to send the "packet" to the dispatcher (which has a window handle to receive it).
Then there is a group of classes descended from an abstract TLogReceiver class. One such class receives the logged messages and writes them to file, another updates a TMemo, etc. I instantiate the concrete receivers I want to use in a project, and register them with the dispatcher, which owns them from that point on.
When the dispatcher receives a message "packet", it hands it off to each receiver in turn, then frees it.
So I am probably fixed in the above way of thinking, which is why I am not quite getting your idea when you say code handing a dispatcher object to the logging library should choose one depending on thread context. The dispatcher is really the main engine of the library, and only one exists at a time.
if GetCurrentThreadID = MainThreadID then begin
// in main thread
end
else begin
// in secondary thread
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With