Solved

TIdFTP inside thread do not abort

Posted on 2003-12-03
3
1,137 Views
Last Modified: 2007-12-19
Hi guys, I have a thread that refreshes a jpg image on a ftp server using TidFtp (I use that in a webcam program). The program creates several instances of this thread each are responsable for a couple of webcams (the program cam manage several webcams at a time), so there are several connection to the ftp server. The problem that I'm having is the following:

Sometimes the threads need to be finalized and restarted (like when a user change the ftp configurations). So the program needs to wait all thread finished and start all again. I make that using a Counter. When a thread is created the Counter is incremented, and the counter is decremented on the OnTerminate event of each thread. The program uses
           
             //(..) Call Finalize for all runnig threads.
             While FtpThreadsCount>0 do Application.ProcessMessages;

to wait all thread finish without hangs the application. And then start all the threads again.
I will put some code here to explain how the things are working now:

The Thread.Create code has that
 (..)
  Ftp:=tidFtp.Create(nil);
  ftp.OnWork:=Self.FTPWork;
  ftp.OnWorkBegin:=Self.FTPWorkBegin;
  ftp.OnWorkEnd:=Self.FTPWorkEnd;
(..)
so I can manage some events inside the thread

The finalize method of the thread has:

     Self.Terminate;
     AbortTransfer:=Transferring;

That set Teminated to True and see if we need to abort a transfer in progress.


The Ftp events have:

procedure ThreadFTP.FTPWork(Sender: TObject; AWorkMode: TWorkMode;
  const AWorkCount: Integer);
begin

If AbortTransfer then
        Ftp.Abort;

end;

procedure ThreadFTP.FTPWorkBegin(Sender: TObject;
  AWorkMode: TWorkMode; const AWorkCountMax: Integer);
begin
AbortTransfer:=false;
Transferring:=true;
end;

procedure ThreadFTP.FTPWorkEnd(Sender: TObject; AWorkMode: TWorkMode);
begin
Transferring:=false;
end;

The main code of the Execute method is:
               
               (..)
                try
                        ftp.Put(Stream,'img' + InttoStr(i+1)+ '.jpg');
                Except on E: Exception do
                        begin
                                Self.Error:='Error ' + E.Message + ' ' + E.ClassName;
                                Synchronize(ShowError);
                                Self.Terminate;
                        end;
                end;
               (..)
The execute method Sleeps 500 ms check for if Terminate=false and do all over again.


The problem is that the thread doens´t seem to abort the upload. They apper to be waiting the transfer terminates. When the threads are finalized the application stays a lot of time inside the loop:

 While FtpThreadsCount>0 do Application.ProcessMessages.

The problem doesn't occur eveytime. Sometimes the thread are finalized fast sometimes not. The network load seems to have something to do with that.
The main problem is when I need to close the application. It stays waiting the threads finalize, and the user thinks that the application will never close.

Somebody see another  way to fast abort the upload and terminate all threads?

Thanks.
0
Comment
Question by:MauricioMaia
  • 2
3 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
Comment Utility
You, Sir, have a synchronization problem! The Terminate code of a thread is still executed inside the thread so when you say:
  FtpThreadsCount := FtpThreadsCount - 1;
in your OnTerminate thread, the following could happen:

Thread 1 reads FtpThreadsCount (2)
Thread 2 reads FtpThreadsCount (2)
Thread 1 decreases FtpThreadsCount and writes result back (1)
Thread 2 decreases FtpThreadsCount and writes result back (1)

But you expect this happens:
Thread 1 reads FtpThreadsCount (2)
Thread 1 decreases FtpThreadsCount and writes result back (1)
Thread 2 reads FtpThreadsCount (1)
Thread 2 decreases FtpThreadsCount and writes result back (0)

Well, a critical section inside your OnTerminate event will fix it. But you could also use InterlockedDecrement() and InterlockedIncrement() to modify the value of FtpThreadsCount in a more secure way.
0
 

Author Comment

by:MauricioMaia
Comment Utility
I just don't get into one point Workshop_Alex, please help with my doubt

The VCL documentation says the OnTerminate event handler is called in the context of the main VCL thread. My code does the following:

procedure TMAINFORM.EndFtpThread(Sender: TObject);
begin
        Dec(FtpThreadsCount);
end;

The Procedure EndFtpThread of the main form which is associated with the OnTerminate Event of each thread can create a deadlock? Even running in the main VCL Thread?

Thanks for the help!
0
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 150 total points
Comment Utility
A quick check... Yes, the OnTerminate event is synchronised with the main thread. My example of FtpThreadsCount is just an example. It could well be that some other global variable is causing this problem. But the problem you described seems familiar to me since I did have the same problems in the past with multi-threaded applications.

Anyway, try to see if using InterlockedDecrement and InterlockedIncrement work better when you change FtpThreadsCount.

Another problem could be that your thread is raising an exception before it has a chance to decrease the counter. If there is no exception handler around your threaded code, the thread will be killed silently without calling the OnTerminate method. And no, it won't even pop up an error message either. It just dies instantly, without warning. So if you have an unhandled exception in your code then chances are that the threads are killed before they manage to decrease the counter.

You could, of course, use a different technique. For example, use: "var ThreadList: array of TThread;" which is a dynamic array of threads. Increase the length with one every time you add a thread. By walking through this list you could easily keep ttrack of all your threads. (Delphi also has a TThreadList that you can use but I like dynamic arrays better.) This dynamic list would actually replace your thread counter, if you would also remove terminated threads from the list and decrease it's size again. And, because you have access to the handles of all threads this way, you could use WaitForMultipleObjects instead of Application.ProcessMessages to wait for all threads to finish. Means you won't need the OnTerminate event in this case if all you do in the OnTerminate is decreasing a counter.
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This video discusses moving either the default database or any database to a new volume.
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now