1

to write information on the processing state to the GUI inside a tcpserver.onexecute(..) function , i used the following command sequence

ExecuteDUMMYCommand(Global_Send_Record);

BitMap_PaintImageProcess;

TThread.Synchronize(nil, BitMap_PaintImageProcess);

The code is working well on some machines, but on a few it fails. The code execution stops atTThread.Synchronize command. I guess on these machines the function call is trapped inside a deadlock Any chance to figure out the real problem behind ?

The procedure BitMap_PaintImageProcess , here I create a Bitmap and do a lot of painting stuff , but is seems that this code is never executed ?


I try to explain the very long code and reduce to the main points , the critical thread issues are hidden in processing the bitmap inside my Bitmapprocessingclass. This class is accessed inside the GUIProcessing procedures of my ServerMainForm which also has the INDY TCP Server component.

   {---------------   CLASS DEFINITION   -----------------------------}

   TBitMapProcessingClass = class()
        FBitmap : TBitmap;
        FList : TListOfSomething;

        procedure ProcessTheBitmap(....);
        ......   
        (many many functions);
        procedure Init;
        procedure Free;
        Procedure Create;
   end;


   TMainform = class(TForm)

      MyServer : TIdTCPServer;
      aBitMaoProcessingClass : TBitMaoProcessingClass;
      procedure BitMap_PaintImageProcess;
      procedure BitMap_ListProcess;

      .....
      end;       



{-------------------------   Implemantation ------------------------------}


procedure TMainform.IndyTCPServer.Onexecute()
begin

   .......

   ExecuteDUMMYCommand(Global_Send_Record);

   BitMap_PaintImageProcess;

   TThread.Synchronize(nil, BitMap_PaintImageProcess);

   .......


end;



procedure TMainform.BitMap_PaintImageProcess;
   begin

      DoSomeServerVCLStuff(....);

      aBitMapProcessingClass.ProcessTheBitmap;


      DoSomeServerVCLStuff(....);

  end;
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
user1769184
  • 1,571
  • 1
  • 19
  • 44
  • 1
    Welcome to StackOverflow. Please don't SHOUT when you post your questions here. It makes them harder to read, and won't get you help any faster. It's also rather rude to shout at people you're asking for help. ;-) And please don't put tag info in the subject line - the tag system here works extremely well and doesn't need any help. :-) The `delphi` tag makes it clear you're asking about Delphi, and the `indy` tag makes it clear your question is about Indy. – Ken White Oct 26 '12 at 21:32
  • @KenWhite: In XE2 TThread has a class procedure overload: Synchronize(AThread: TThread; AMethod: TThreadMethod). If you pass nil as a first argument the AMethod is executed in the main thread: http://docwiki.embarcadero.com/Libraries/XE2/en/System.Classes.TThread.Synchronize – iPath ツ Oct 26 '12 at 22:15
  • @iPath: You're absolutely correct; I checked the wrong version of the VCL documentation before posting. However, that doesn't address the other issue. – Ken White Oct 26 '12 at 22:19
  • @user1769184: Why BitMap_PaintImageProcess is called in the onExecute and then with tthread.synchronize? Is it a mistake or you're using the same procedure to do the real stuff and to do GUI updating? – iPath ツ Oct 26 '12 at 22:32

3 Answers3

0

Having no idea what BitMap_PaintImageProcess() does in fact, I have a few suppositions:

  • In the TThread.Synchronize call you try to read some data from the socket/idContext, but the data is not yet available. This will block the main thread until the data becomes available. But Indy's thread that is responsible for reading from the underlying socket buffer is currently blocked by your TThread.Synchronize call in the OnExecute event i.e. deadlock occurs;
  • In the TThread.Synchronize call you use Application.ProcessMessages (a common mistake);
  • In the OnExecute event you enter a critical section. Then during the TThread.Synchronize call you try to enter the same critical section again;
  • You modify the GUI in the onExecute event. Because onExecute is not thread safe, accessing VCL/FM from Indy's thread could lead to unpredictable results, including random deadlocks (hard to find).

I would suggest to use MadExcept / Eurekalog. They both have options to check if the main thread is "frozen". When that happens (during the deadlock) they will show you the current call stack. Having the call stack you can figure out which function is causing the deadlock.

iPath ツ
  • 2,468
  • 20
  • 31
  • 1
    Another possibility - a server event handler is calling `TThread.Sychronize()` while the main thread is busy trying to shut down the server. That is a classic deadlock that many `TIdTCPServer` newbies encounter. – Remy Lebeau Oct 26 '12 at 23:19
  • @RemyLebeau for this reason I tend to use tidnotify descendants to prevent the possibility of this cause for a deadlock. – Mike Taylor Oct 27 '12 at 00:44
  • there is a similar solutuion given at http://stackoverflow.com/questions/13036579/indy-10-tcp-server-combine-with-non-thread-safe-vcl-code but here my complex TBitmap processing Class must be alive the whole time my Server is active, I can't create a New Instance of my TBitmap class once I call the TIdNotify derived class as given in that example – user1769184 Oct 27 '12 at 10:13
0

Regarding the posted code:

procedure TMainform.IndyTCPServer.Onexecute()
begin
   .......
   ExecuteDUMMYCommand(Global_Send_Record);
   BitMap_PaintImageProcess; //-> You do VCL stuff in the context of Indy's thread!
   TThread.Synchronize(nil, BitMap_PaintImageProcess);
end;

In the BitMap_PaintImageProcess() you call DoSomeServerVCLStuff(....). Do not forget that OnExecute is fired from Indy's thread for the current context. I.e. you modify VCL from another thread (other from the Main Thread) which is not thread safe.

On your comment:

...but here my complex TBitmap processing Class must be alive the whole time my Server is active...

If you have only one (global) instance for image processing, then what will happen if another client connects, while you are still processing the old connection (think Parallel :) )? Your image processing class should be instantiated separately for each new connection/context. For GUI updating you can use TIdNotify descendant.

A possible solution:

type 
{ tIdNotify Stuff }
TVclProc= procedure(imgClass: tMyImageProcessingClass) of object;

tIdNotifyDescendant = (tIdNotify)
  protected
    fImgClass: tMyImageProcessingClass;
    fProc: TVclProc;
    procedure DoNotify; override;
  public
    class procedure updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
end;

procedure tIdNotifyDescendant.DoNotify;
begin
 inherited DoNotify;
 FProc(fImgClass);
end;

class procedure tIdNotifyDescendant.updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
begin
 with Create do
 begin
   fImgClass := imgClass;
   fProc := vclProc;
   Notify;
 end;
end;

{ Indy stuff & other logic }

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  //  Create your instance when the client connects
  AContext.Data := tMyImageProcessingClass.Create;
end;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  //  Do cleanup
  if assinged(AContext.Data) then
    (AContext.Data as tMyImageProcessingClass).Free // Casting just for clarity
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  imgProcClass: tMyImageProcessingClass;
begin
  imgProcClass := acontext.Data as tMyImageProcessingClass;
  //  Do image processing
  //  Notify GUI for the progress:
  tIdNotifyDescendant.updateVcl(AContext.data as tMyImageProcessingClass);
end;

Tip: If you do JPEG processing and you use Draw() method have in mind this: TJPEGImage.Draw() is not thread safe

iPath ツ
  • 2,468
  • 20
  • 31
  • this code looks familiar http://stackoverflow.com/questions/13036579/indy-10-tcp-server-combine-with-non-thread-safe-vcl-code/13052385#13052385 – Mike Taylor Oct 29 '12 at 09:51
  • @MikeT: Yes, some parts of tIdNotifyDescendant are borrowed from there, but adapted it to fit more closely this particular question – iPath ツ Oct 29 '12 at 15:23
0

I added a few more details on my BitmapProcessingclass and the idea of a thread safe extension of the existing class... I need the existing class u nchanged in others apps ... I need a extension inside my app with the indy server. ONly one client my connect to one server, or he has to query the state of the server

    type TBmpNotify = class(TIdNotify)
   protected
     FBMP: MyImageProcessingClass;

     procedure DoNotify; override;
   public
     constructor Create(aBMP: MyImageProcessingClass);
       function SetImageView(LL, UR: TPoint): Boolean;
              procedure PaintBitMap;
      function InitBitMap(x, y: Integer;
         PixelFormat: TPixelFormat = pf24bit): Boolean;
      destructor free;
   end;


   implementation

   { TBmpNotify }

   constructor TBmpNotify.Create(aBMP: MyImageProcessingClass);
   begin
       //   indise this class  I also create
       //   class.TBitmap
       //   class.TList
       //   much more stuff ....
       FBmp := MyImageProcessingClass.Create;
   end;

   procedure TBmpNotify.DoNotify;
   begin
     inherited;

   end;

   destructor TBmpNotify.free;
   begin

       FBmp.Free;

       inherited;
   end;

   function TBmpNotify.InitBitMap(x, y: Integer;
     PixelFormat: TPixelFormat): Boolean;
   begin
       //   Write values to the List
       //   also modify TBitmap
       //   execution time of this function ~ 5 min
       FBmp.InitBitMap(x,y,PixelFormat)
   end;

   procedure TBmpNotify.PaintBitMap;
   begin
       //   much TBitmap, bitmap.canvas .... is used
       //   execution time of this function ~ 1  min
      FBmp.PaintBitMap;
   end;

   function TBmpNotify.SetImageView(LL, UR: TPoint): Boolean;
   begin
      //  this function takes about 1 min
      FBmp.SetImageView(LL, UR);
   end;

   end.
user1769184
  • 1,571
  • 1
  • 19
  • 44