Unable to enter critical section - multithreading

Why is it imposible to enter critical section without Sleep(1)?
type
TMyThread = class(TThread)
public
procedure Execute; override;
end;
var
T: TMyThread;
c: TRTLCriticalSection;
implementation
procedure TForm1.FormCreate(Sender: TObject);
begin
InitializeCriticalSection(c);
T := TMyThread.Create(false);
end;
procedure TMyThread.Execute;
begin
repeat
EnterCriticalSection(c);
Sleep(100);
LeaveCriticalSection(c);
sleep(1); // can't enter from another thread without it
until false;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
EnterCriticalSection(c);
Caption := 'entered';
LeaveCriticalSection(c);
end;
Can't post this because of too much code so text text text text text.
Oh by the way if the section is created by the thread then it is working fine.

There is no guarantee that threads acquire a critical section on a FIFO basis (MSDN). If your current thread always re-acquires the critical section a few uops after releasing it then chances are that any other waiting threads will likely never wake in time to find it available themselves.
If you want better control of lock sequencing there are other synchronization objects you can use. Events or a queue might be suitable but we don't really know what you are trying to achieve.

Related

Delphi - Is TDictionary thread safe

My idea is to use TDictionary to manage client connections on IdTCPServer. Here is a simple example code (not tested) for understanding purposes:
var
Dic: TDictionary<string, TIdContext>;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Dic := TDictionary<string, TIdContext>.Create;
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
Dic.Free;
end;
procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
Hostname: string;
begin
Hostname := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
if not Dic.ContainsKey(Hostname) then Dic.Add(Hostname, AContext);
end;
procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
Hostname: string;
begin
Hostname := UpperCase(GStack.HostByAddress(AContext.Binding.PeerIP));
if Dic.ContainsKey(Hostname) then
begin
Dic[Hostname].Free;
Dic.Remove(Hostname);
end;
end;
Is this code thread safe?
In a word: No.
If you inspect the source of TDictionary you should quickly realise that there is no provision for thread-safety in the implementation itself. Even if it were, by having discrete calls to a Dic instance you have potential race conditions to contend with:
if Dic.ContainsKey(Hostname) then
begin
// In theory the Hostname key may be removed by another thread before you
// get a chance to do this : ...
Dic[Hostname].Free;
Dic.Remove(Hostname);
end;
You need to make your own use of Dic thread safe, and fortunately in this sort of example this is easily achieved using a monitor on the object itself:
MonitorEnter(Dic);
try
if not Dic.ContainsKey(Hostname) then
Dic.Add(Hostname, AContext);
finally
MonitorExit(Dic);
end;
// ....
MonitorEnter(Dic);
try
if Dic.ContainsKey(Hostname) then
begin
Dic[Hostname].Free;
Dic.Remove(Hostname);
end;
finally
MonitorExit(Dic);
end;
If you are not familiar with monitors in Delphi, in simple terms you can think of a monitor as a ready-to-use critical section supported by every TObject descendant (in older versions of Delphi which did not support these monitors you could have achieved the same thing with an explicit critical section).
To answer your specific question - no, TDictionary is NOT thread-safe, so you must protect access to it.
Your code is not handling the possibility of multiple clients behind a proxy/router connecting to the same server. They will all have the same PeerIP and HostName values. Those values are not unique enough by themselves to identify clients. You need to create your own unique identifiers, for instance by having your clients login to your server with a username, and then use that as your dictionary key instead.
And lastly, DO NOT free TIdContext objects! They are owned by TIdTCPServer and will be freed automatically after the OnDisconnect event handler has exited.

Why does this code block my main thread?

I need my Thread work independent of Form.
For example I have a endless loop in my Thread:
procedure TCustomThread.doProc;
begin
repeat
.
.
.
until (1 = 2);
end;
procedure TCustomThread.Execute;
begin
inherited;
Synchronize(doProc);
end;
.
.
.
procedure TForm1.Button1Click(Sender: TObject);
var
thrd : TCustomThread;
begin
thrd := TCustomThread.Create(True);
thrd.Resume;
Application.ProcessMessages;
end;
Now, when I click on Button1, my Thread runs but main Form is locked. How can I avoid suspending Form?
The call to ProcessMessages is wrong and should be removed. As a broad and general rule, calls to ProcessMessages should be avoided. And this one serves no purpose at all.
The rest of your code simply runs a non-terminating loop. It does so using Synchronize which ensures that the non-terminating loop runs on the main thread. Hence the main thread is unable to service its message loop.
The entire purpose of threads is to be able to execute separate threads of execution. By using Synchronize you are running all your code in the main thread. Your code is equivalent to placing the non-terminating loop in the main thread.
You want to execute code in a different thread. So you should avoid the call to Synchronize. That should only be used for small, quick pieces of work that must execute on the main thread. Typically GUI updates.
Your execute method should be:
procedure TCustomThread.Execute;
begin
while not Terminated do
begin
....
end;
end;
This introduces your loop, but the loop now executes in the thread. You can now add the useful code of your thread inside the body of the loop.
Remember that any use of VCL components must happen on the main thread. And that's where Synchronize is to be used.
TThread.Synchronize() runs the specified procedure in the context of the main thread, not in the context of the worker thread. So your loop is running in the main thread and is not letting hte main thread process new messages from its message queue. That is why your UI is not responding while your thread is running.
You need to restructure your thread to something more like this:
procedure TCustomThread.doUpdateUI;
begin
... do something that updates the UI here ...
end;
procedure TCustomThread.Execute;
begin
while not Terminated do
begin
... do something in the worker thread ...
Synchronize(doUpdateUI);
... do something else in the worker thread ...
end;
end;
var
thrd : TCustomThread = nil;
procedure TForm1.Button1Click(Sender: TObject);
begin
if thrd = nil then
thrd := TCustomThread.Create(False);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
if thrd <> nil then
begin
thrd.Terminate;
thrd.WaitFor;
FreeAndNil(thrd);
end;
end;

Thread.FreeOnTerminate := True, memory leak and ghost running

Years ago, I decided never to rely solely on setting a thread's FreeOnTerminate property to true to be sure of its destruction, because I discovered and reasoned two things at application's termination:
it produces a memory leak, and
after program's termination, the thread is still running somewhere below the keyboard of my notebook.
I familiarized myself with a workaround, and it did not bother me all this time. Until tonight, when again someone (#MartinJames in this case) commented on my answer in which I refer to some code that does not use FreeOnTerminate in combination with premature termination of the thread. I dove back in the RTL code and realized I may have made the wrong assumptions. But I am not quite sure about that either, hence this question.
First, to reproduce the above mentioned statements, this illustrative code is used:
unit Unit3;
interface
uses
Classes, Windows, Messages, Forms;
type
TMyThread = class(TThread)
FForm: TForm;
procedure Progress;
procedure Execute; override;
end;
TMainForm = class(TForm)
procedure FormClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
FThread: TMyThread;
end;
implementation
{$R *.dfm}
{ TMyThread }
procedure TMyThread.Execute;
begin
while not Terminated do
begin
Synchronize(Progress);
Sleep(2000);
end;
end;
procedure TMyThread.Progress;
begin
FForm.Caption := FForm.Caption + '.';
end;
{ TMainForm }
procedure TMainForm.FormClick(Sender: TObject);
begin
FThread := TMyThread.Create(True);
FThread.FForm := Self;
FThread.FreeOnTerminate := True;
FThread.Resume;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
FThread.Terminate;
end;
end.
Now (situation A), if you start the thread with a click on the form, and close the form right after the caption changed, there is a memory leak of 68 bytes. I assume this is because the thread is not freed. Secondly, the program terminates immediately, and the IDE is at that same moment back again in normal state. That in contrast to (situation B): when not making use of FreeOnTerminate and the last line of the above code is changed into FThread.Free, it takes (max.) 2 seconds from the disappearance of the program to the normal IDE state.
The delay in situation B is explained by the fact that FThread.Free calls FThread.WaitFor, both which are executed in the context of the main thread. Further investigation of Classes.pas learned that the destruction of the thread due to FreeOnTerminate is done in the context of the worker thread. This lead to the following questions on situation A:
Is there indeed a memory leak? And if so: is it important, could it be ignored? Because when an application terminates, doesn't Windows give back all its reserved resources?
What happens with the thread? Does it indeed run further somewhere in memory until its work is done, or not? And: is it freed, despite the evidence of the memory leak?
Disclaimer: For memory leak detection, I use this very simple unit as first in the project file.
Indeed, the OS reclaims all a process's memory when it terminates, so even if those 68 bytes refer to the non-freed thread object, the OS is going to take those bytes back anyway. It doesn't really matter whether you've freed the object at that point.
When your main program finishes, it eventually reaches a place where it calls ExitProcess. (You should be able to turn on debug DCUs in your project's linker options and step through to that point with the debugger.) That API call does several things, including terminating all other threads. The threads are not notified that they're terminating, so the cleanup code provided by TThread never runs. The OS thread simply ceases to exist.

How to implement thread which periodically checks something using minimal resources?

I would like to have a thread running in background which will check connection to some server with given time interval. For example for every 5 seconds.
I don't know if there is a good "desing pattern" for this? If I remember corretly, I've read somewehere that sleeping thread in its execute method is not good. But I might be wrong.
Also, I could use normal TThread class or OTL threading library.
Any ideas?
Thanks.
In OmniThreadLibrary, you would do:
uses
OtlTask,
OtlTaskControl;
type
TTimedTask = class(TOmniWorker)
public
procedure Timer1;
end;
var
FTask: IOmniTaskControl;
procedure StartTaskClick;
begin
FTask := CreateTask(TTimedTask.Create())
.SetTimer(1, 5*1000, #TTimedTask.Timer1)
.Run;
end;
procedure StopTaskClick;
begin
FTask.Terminate;
FTask := nil;
end;
procedure TTimedTask.Timer1;
begin
// this is triggered every 5 seconds
end;
As for sleeping in Execute - it depends on how you do it. If you use Sleep, then this might not be very wise (for example because it would prevent the thread to stop during the sleep). Sleeping with WaitForSingleObject is fine.
An example of TThread and WaitForSingleObject:
type
TTimedThread = class(TThread)
public
procedure Execute; override;
end;
var
FStopThread: THandle;
FThread: TTimedThread;
procedure StartTaskClick(Sender: TObject);
begin
FStopThread := CreateEvent(nil, false, false, nil);
FThread := TTimedThread.Create;
end;
procedure StopTaskClick(Sender: TObject);
begin
SetEvent(FStopThread);
FThread.Terminate;
FThread.Free;
CloseHandle(FStopThread);
end;
{ TTimedThread }
procedure TTimedThread.Execute;
begin
while WaitForSingleObject(Form71.FStopThread, 5*1000) = WAIT_TIMEOUT do begin
// this is triggered every 5 seconds
end;
end;
OTL timer implementation is similar to the TThread code above. OTL timers are kept in priority list (basically the timers are sorted on the "next occurence" time) and internal MsgWaitForMultipleObjects dispatcher in TOmniWorker specifies the appropriate timeout value for the highest-priority timer.
You could use an event and implement the Execute method of the TThread descendant by a loop with WaitForSingleObject waiting for the event, specifying the timeout. That way you can wake the thread up immediately when needed, e.g. when terminating.
If the thread runs for the life of the app, can be simply terminated by the OS on app close and does not need accurate timing, why bother with solutions that require more typing than sleep(5000)?
To add another means of achieving a 5-sec event it is possible to use the Multimedia Timer which is similar to TTimer but has no dependence on your application. After configuring it (you can setup one-shot or repetitive) it calls you back in another thread. By its nature it is very accurate (to within better than 1ms). See some sample Delphi code here.
The code to call the timer is simple and it is supported on all Windows platforms.
Use CreateWaitableTimer and SetWaitableTimer

Delphi - Updating a global string from a second thread

I am experimenting with multithreading in Delphi (XE) and have run into a problem with the use of a Global Variable between the main VCL thread and a second work thread.
My project involves a 2nd worker thread that scans through some files, and updates a globalvar string with the current filename its on. This globalvar is then picked up via a timer on the main VCL thread, and updates a statusbar.
I have noticed though that it occasionally comes up with a 'Invalid Pointer Operation'...or 'Out of Memory' or the work thread just stops responding (deadlock probably).
I therefore created a test app to identify and greatly increase the chance of error so i could see what's going on.
type
TSyncThread = class(TThread)
protected
procedure Execute; override;
end;
var
Form11: TForm11;
ProgressString : String;
ProgressCount : Int64;
SyncThread : TSyncThread;
CritSect : TRTLCriticalSection;
implementation
{$R *.dfm}
procedure TForm11.StartButtonClick(Sender: TObject);
begin
Timer1.Enabled := true;
SyncThread := TSyncThread.Create(True);
SyncThread.Start;
end;
procedure TForm11.StopbuttonClick(Sender: TObject);
begin
Timer1.Enabled := false;
SyncThread.Terminate;
end;
procedure TForm11.Timer1Timer(Sender: TObject);
begin
StatusBar1.Panels[0].Text := 'Count: ' + IntToStr(ProgressCount);
StatusBar1.Panels[1].Text := ProgressString;
end;
procedure TSyncThread.Execute;
var
i : Int64;
begin
i := 0;
while not Terminated do begin
inc(i);
EnterCriticalSection(CritSect);
ProgressString := IntToStr(i);
ProgressCount := i;
LeaveCriticalSection(CritSect);
end;
end;
initialization
InitializeCriticalSection(CritSect);
finalization
DeleteCriticalSection(CritSect);
I set the timer interval to 10ms so that it is reading a lot, whilst the worker thread is running flat out updating the global var string. Sure enough this app barely lasts a second when run before it comes up with the above errors.
My question is, does the read operation of the Global var in the VCL Timer need to be run in a critical section? - if so, why?. From my understanding it is only a read, and with the writes already running in a critical section, i cannot see why it runs into a problem. If i do put the read in the timer into a critical section as well - it works fine....but im unhappy just doing that without knowing why!
I am new to multithreading so would appreciate any help in explaining why this simple example causes all sorts of problems and if there is a better way to be accessing a string from a worker thread.
Delphi String is allocated on a heap, it is not a static buffer somewhere. The variable itself is just a pointer. When your reading thread accesses a String, and at the same time this very string is being deallocated by another thread, bad things happen. You are accessing already freed memory, possibly allocated again for something else, etc.
Even if this String was a static buffer, update operations are not atomic, therefore you could be using a corrupted string that is being updated at this very moment (half new data and half old).
So you need to protect your reading operations with the same critical section you used around the writing operations.

Resources