5

VCL is not thread safe. Therefore I guess it is not a good idea to write information to the gui in the INDY 10 TCP server.execute(...) function .

How to send information from the server execute to the VCL ?

I need to modify a TBitmap inside a tcpserver.execute function. How to make that thread safe ?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
user1769184
  • 1,571
  • 1
  • 19
  • 44

3 Answers3

4

Write stuff to the VCL thread from Indy the same way to write stuff to the VCL thread from anywhere else. Common options include TThread.Synchronize and TThread.Queue.

Modifying a standalone TBitmap should not require synchronization with the main thread. You can modify it from any thread you want, as long as you do it from only one thread at a time. You can use the standard synchronization objects like critical sections and events to make sure only one thread uses it at a time.

Dr.eel
  • 1,837
  • 3
  • 18
  • 28
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • I did all VCL stuff inside the Id.notify methode call but is works only 80 % succesfully ... – user1769184 Oct 23 '12 at 18:41
  • [code] procedure TServerMainForm.IdTCPServerExecute(AContext: TIdContext); var .... begin /// /// TIdNotify.NotifyMethod(ShowStartServerMessage); ...... – user1769184 Oct 23 '12 at 18:44
  • `TIdNotify.NotifyMethod` runs asynchronously because it does nothing more than call `TThread.Queue`. Could it be that 20% of the things you're trying to do require synchronous access to the VCL? – Rob Kennedy Oct 23 '12 at 19:08
  • need a simple sample copy how to do this with TIdsync , is thus the solution ? – user1769184 Oct 23 '12 at 20:24
  • found http://stackoverflow.com/questions/12175361/indy-synchronize-servertcpexecute, should I really write vcl stuff inside this class ? – user1769184 Oct 23 '12 at 20:35
  • Why do you need a sample of how to use `TIdSync`? `TIdSync` is to `TThread.Synchronize` as `TIdNotify` is to `TThread.Queue`. If your delphi version is new enough to support `Synchronize` and `Queue` as class methods, then you don't need the Indy classes at all. Remy's answer demonstrates overriding `TIdSync`, but you can instead call `TIdSync.SynchronizeMethod`, or just call `TThread.Synchronize` directly. My main point is that there's nothing special about Indy in your question. Do inter-thread communication the same as you would in any other program. – Rob Kennedy Oct 23 '12 at 21:05
  • I use Delphi XE2 . In my TIdNotify.NotifyMethod(ShowStartServerMessage); function there is just simple VCL access, like Memo1.Lines.add ( Global_section_strings) ; How to syncronize the VCL activieties? – user1769184 Oct 23 '12 at 21:55
  • @user Rob has told you how to do it already. Remy's answer does so also. – David Heffernan Oct 23 '12 at 21:58
  • You can synchronize it by using a *synchronous* method, like `TThread.Synchronize`. As I said, `Notify` is like `Queue`; it doesn't wait for the passed method to finish before continuing the calling thread. That's probably the source of your problem since your Indy thread probably modifies your global string variable before the main thread gets to use it. – Rob Kennedy Oct 24 '12 at 04:08
  • thanks for your help; now I added a to the code TIdNotify.NotifyMethod(ShowStartServerMessage); the line : TThread.Synchronize(nil, ShowStartServerMessage ); guess the problem is now solved. – user1769184 Oct 24 '12 at 07:31
2

the best way to synch is by creating and using a TidNotify descendant.

define a tidnotify descendant and vcl proc like this with the appropriate private fields.

TVclProc= procedure(aBMP: TBitmap) of object;

TBmpNotify = class(TIdNotify)
protected
  FBMP: TBitmap;
  FProc: TVclProc;
  procedure DoNotify; override;
public
  constructor Create(aBMP: TBitmap; aProc: TVclProc); reintroduce;
  class procedure NewBMP(aBMP: TBitmap; aProc: TVclProc);
end;

then implement it like this

{ TBmpNotify }

constructor TBmpNotify.Create(aBMP: TBitmap; aProc: TVclProc);
begin
  inherited Create;
  FBMP:= aBMP;
  FProc:= aProc;
end;

procedure TBmpNotify.DoNotify;
begin
  inherited;
  FProc(FBMP);
end;

class procedure TBmpNotify.NewBMP(aBMP: TBitmap; aProc: TVclProc);
begin
  with Create(aBMP, aProc) do
  begin
    Notify;
  end;

end;

then from the

server.execute(...)

call it like this

procedure TTCPServer.DoExecute(aContext: TIdContext);
var
  NewBMP: TBitmap;
begin
  TBmpNotify.NewBMP(NewBMP, FVclBmpProc);  
end;

Where the FVclBmpProcis a private field pointing to a procedure on the form that matches the parameter signature of TVclProc. This field should be set via a property on the server object just after creation and before starting the server.

the method on the form will be free to use the bitmap it receives without fear of thread contention, deadlock and other nasties created by accessing the VCL controls without synchronisation.

Mike Taylor
  • 2,376
  • 2
  • 17
  • 33
  • Thanks Mike, I#ll test that code inside my program; there are now many many useful code sniplets on stackoverflow for Delphi and INDY 10; someone should copy this to an E book :-) – user1769184 Oct 24 '12 at 21:32
1

One simple PostMessage (inside the thread) and handling message (outside the thread) was necessary to make UI updates...

alijunior
  • 317
  • 1
  • 12