In Delphi 2009 and the Windows API, is there a way to detect that a particular piece of code is working in the context of a secondary stream? In pseudo code, I would like to say:
procedure DoSomething; begin if InvokedBySecondaryThread then DoIt_ThreadSafeWay else DoIt_RegularWay; end;
This is for a journal library that I have written and used for many years, and now I'm trying to adapt to a situation where one procedure can be called from multiple threads. My "normal way" is not thread safe. I know how to make this thread safe, but I would like to use the threadafe method only when it is really needed.
Explanation (optional reading :-)
It comes down to choosing between SendMessage and PostMessage to send a registered message to multiple recipients, such as a log file, console, or VCL control. Using PostMessage means that messages will not be received while a long blocking operation is performed, which leads to an incorrect purpose of logging, especially. when used to indicate progress. I believe that I could protect the SendMessage call with a critical section, but again I would prefer to do this only when it is really needed.
I know about the global var IsMultiThread in system.pas, but this will only tell me that the application started the secondary threads. These can be streams created by third-party libraries, and therefore they can never access my code, so their existence will not affect my logging logic.
I would very much like to use the same low-level library code, regardless of whether it is called from one or more threads. For example, it would be easy to invoke modified, thread-safe logging procedures from secondary threads, but this will duplicate a lot of code, and I still have to remember that always do the right thing.
@Lieven: Currently, the logging logic looks something like this: somewhat simplified
I want the log to be as painless as possible, with minimal setup code and not worry about managing the lifetime of the object, so the library provides only a few overloaded auxiliary 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
Almost everything else happens when the device is implemented. All helper routines ultimately cause a call
procedure _LogPostMessage( const msgText : string; level : TLogLevel );
which (a) checks if the singleton dispatcher object has been initialized; (b) instantiate the TLogMessagePacket object (container for message text, timestamp, etc.) and finally (c) send 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 originating from the abstract TLogReceiver class. One of these classes accepts registered messages and writes them to a file, the other updates TMemo, etc. I create specific recipients that I want to use in the project, and register them with the dispatcher who owns them from this point.
When the dispatcher receives the message "packet", he passes it to each receiver in turn, and then releases it.
Therefore, I am probably corrected in the above way of thinking, so I don’t quite understand your idea when you say that the code that passes the dispatcher object to the registration library should choose one depending on the context of the stream. The dispatcher is indeed the main engine of the library, and only one exists at a time.