Delphi threading: CriticalSection is not "Release'd" when using Synchronize inside its method

In my project, I have a Thread that can be modified by the thread itself, another thread, or VCL (main application). This way I use TCriticalSection.Acquire / Release for every data access.

Under normal circumstances, the code below works as expected: enters Acquire, Synchronizes with DoCallback, and then releases the lock. However, if any other context acquires a lock at the time of its blocking, the execution below the code stops in Synchronize - and this time it does NOT introduce the DoCallback method.

Should I skip the Synchronize method (even if the Synchronize'd code calls the VCL) and rely on the CriticalSection itself? What is the reason for this behavior?

main stream code:

fData:= nil; try fSingleRequest.Acquire; if fItem <> nil then begin fData:= fItem.Request; SubmitRequest(); fCallbackData:= fItem.fExtraData; fCallback:= fItem.fCallback; Synchronize(DoCallback); // <-- this line is called end; finally fSingleRequest.Release; // <-- this isn't under the specific situation end; 
+4
source share
1 answer

If your so-called "main" thread receives a critical section, and then the VCL thread (which we usually call the "main" thread) tries to receive it, then the VCL thread will block until the critical section is released. Then your "main" thread calls Synchronize , which runs this function in the context of the VCL thread. Since the VCL thread is blocked in the critical section, it does not process messages, so it cannot notice that there is a synchronous method to call. Therefore, a dead end.

Do not hold the lock between calls within the thread. Release the lock in your "main" thread before you call Synchronize , and then acquire it again if you still need to. If the data used in the synchronized method needs constant protection against simultaneous access, I think you should find a way to copy the data to a separate, not shared object. Ask the synchronized method to use this object instead of the general one, and then destroy it later.

Your comment indicates that you can call the callback function without Synchronize , and everything will work. In this case, the synchronized method is not called in the same thread context as before. It is called directly by your "main" thread, not the VCL thread. This explicitly removes the lock conflict, but whether it is really safe depends on the callback function.

+9
source

Source: https://habr.com/ru/post/1381159/


All Articles