Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting secondary thread context in Delphi

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.

like image 645
Marek Jedliński Avatar asked Dec 07 '22 00:12

Marek Jedliński


1 Answers

  if GetCurrentThreadID = MainThreadID then begin
// in main thread
  end
  else begin
// in secondary thread
  end; 
like image 152
kludg Avatar answered Dec 14 '22 23:12

kludg