简体   繁体   中英

Indy10 Deadlock at TCPServer

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;

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 ie 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.

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. Ie 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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM