-2

UPDATE Problem Still Exists.

Is it possible to run code in already running thread? for example: thread1 is running some code & i want to run code from thread2 in thread1.

I want to run code in idTCPServer thread to send some data to client

Edit: After research seems that my problem is that when Client data is received or is receiving same time another thread is trying to write to that socket.

Edit:

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
begin
// Application.ProcessMessages;
 Command := AContext.Connection.Socket.ReadLn;
// messagebox(0,'snd','',$40);
 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then   // keepalive 
  begin
  //Application.ProcessMessages;
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;


procedure Client_ProcessData(AContext: TIdContext; cmd : String; data : TMemoryStream);
var
 Hnd     : THandle;
 clData  : TStringArray;
 TmpStr1 : String;
 Tmp     : String;
 TN      : TIdNotify;

 Sync    : TMySync;
 I,I2    : Integer;
begin
 Hnd := AContext.Connection.Socket.Binding.Handle;

 if cmd = 'scr' then  // RECEIVE COMMAND TO SEND TO CLIENT TO RECEIVE DATA FROM CLIENT
  begin
   Tmp := StreamToString(data);
   {Sync := TMySync2.Create(True);
   try
    Sync.cmd := cmd;
    Sync.hnd := Hnd;
    Sync.tmp := TmpStr1;
    Sync.Resume;
   finally
    //Sync.Free;
   end;  }
   log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::');
 //  SendCMDToSocket(MainFrm.UserSRV,StrToInt(Trim(Tmp)),'scr'+IntToStr(Hnd));
   I2 := StrToInt(Trim(Tmp));
  for I := 0 to 100 do
  if USRVData[i].hnd = I2 then
   begin
   // cs.Acquire;
    USRVData[i].CTX.Connection.Socket.WriteLn('scr'+IntToStr(Hnd));  // PLACED ALL CONTEXTs IN GLOBAL VARIABLE + ALL SOCKET HANDLES. <--- HERE IS THE PROBLEM
   // cs.Release;
    Break;
   end;
 //  log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::'+streamtostring(data));
   Exit;
  end;

   if Copy(cmd,1,Length('scr4u')) = 'scr4u' then // RECEIVE DATA FROM CLIENT TO SEND IT TO ADMIN CLIENT REQUEST ABOVE
   begin
    if Length(cmd) > Length('scr4u') then
     begin
      Delete(cmd,1,Length('scr4u'));
      Data.Position := 0;
     { Sync := TMySync.Create;
      try
       Sync.cmd := cmd;
       Sync.hnd := Hnd;
       Sync.data := TMemoryStream.Create;
       Sync.data.CopyFrom(data,data.Size);
       Sync.data.Position := 0;
       Sync.DoNotify;
      finally
       Sync.data.Free;
       Sync.Free;
      end;  }
      SendStreamToSocket(MainFrm.UserSRV,strtoint(cmd),'scr4u',Data);

      log('>>>>> ADMIN: '+IntToStr(HND)+':::'+cmd+':::'{+streamtostring(data)});
     end else TmpStr1 := '';
    Exit;
   end;

   ...

UPDATE

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
 I              : Integer;
 List, Messages : TStringList;
begin
  Messages := nil;
  try
   List := TMyContext(AContext).OutgoingMessages.Lock;
   try
    if List.Count > 0 then
     begin
      Messages := TStringList.Create;
      Messages.Assign(List);
      List.Clear;
     end;
   finally
    TMyContext(AContext).OutgoingMessages.Unlock;
   end;
   if Messages <> nil then
    begin
     for I := 0 to Messages.Count-1 do
      begin
       AContext.Connection.IOHandler.WriteLn(Messages.Strings[I]);
      end;
    end;
  finally
   Messages.Free;
  end;

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
   begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
   end;

 Command := AContext.Connection.Socket.ReadLn;

 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then
  begin
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;
user2200585
  • 283
  • 5
  • 15
  • 1
    Can you explain what exactly you want to do? It is not quite clear. Your thread can have multiple procedures and it can run any code after you send some signal to run one. Or you thread1 can start thread2. Or thread1 can run thread2.Execute (or any other procedure). – smooty86 Sep 03 '15 at 06:35
  • 1
    You need your threads to co-operate and communicate. Have one thread ask the other to do some work. – David Heffernan Sep 03 '15 at 06:44
  • I want to run code in idTCPServer thread to send some data to client – user2200585 Sep 03 '15 at 08:48
  • Please don't ask the question in a comment. Please [edit](http://stackoverflow.com/posts/32368441/edit) the question to add the missing details. – David Heffernan Sep 03 '15 at 09:16
  • @user2200585: Where is the thread2? IdTCPServer has own thread for each connection but you don't have to care about that. IdTCPClients sends data to IdTCPServer which sends data back. Why do you need another thread? You should probably check demos for this type of communications first. – smooty86 Sep 03 '15 at 09:28
  • because my second thread is second tcpserver thread and they must communicate each other :) – user2200585 Sep 03 '15 at 09:42
  • Why do you need second tcpserver? Do you have both in the same program? – smooty86 Sep 03 '15 at 11:52
  • because I need several servers – user2200585 Sep 03 '15 at 12:16
  • Actually you (probably) don't. One server can handle all the request. As you don't provide any actuall code or any explanation how it should work, it is difficult to help. As you use 2 servers, you never know from thread1 if thread2 is running (requests are quite "random" thing). So the best way is to keep data in some variable (object). Both servers can have such data-holder - one thread will insert data, second thread will read data. You have to make it thread-safe so you will use TCriticalSection to make any change. – smooty86 Sep 03 '15 at 15:43

1 Answers1

1

Is it possible to run code in already running thread? for example: thread1 is running some code & i want to run code from thread2 in thread1.

No. Thread1 needs to be explicitly coded to stop what it is currently doing, do something else, and then go back to what it was previous doing. All Thread2 can do is signal Thread1 to perform that stop+continue at its earliest convenience.

I want to run code in idTCPServer thread to send some data to client

Your TIdTCPServer.OnExecute event handler needs to check for that data periodically and send it when it is available.

You can use the TIdContext.Data property, or derive a custom class from TIdServerContext and assign it to the TIdTCPServer.ContextClass property, to provide a per-client thread-safe buffer for your outbound data. Your OnExecute handler can then access that buffer when needed.

For example:

type
  TMyContext = class(TIdServerContext)
  public
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;
    OutgoingMessages: TIdThreadSafeStringList;
  end;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
  inherited;
  OutgoingMessages := TIdThreadSafeStringList.Create;
end;

destructor TMyContext.Destroy;
begin
  OutgoingMessages.Free;
  inherited;
end;

procedure TMyForm.FormCreate(Sender: TObject);
begin
  // this must be set before activating the server...
  IdTCPServer1.ContextClass := TMyContext;
end;

procedure TMyForm.IdTCPServer1Execute(AContext: TIdContext);
var
  List, Messages: TStringList;
begin
  // check for outgoing data...

  Messages := nil;
  try
    List := TMyContext(AContext).OutgoingMessages.LockList;
    try
      if List.Count > 0 then
      begin
        Messages := TStringList.Create;
        Messages.Assign(List);
        List.Clear;
      end;
    finally
      TMyContext(AContext).OutgoingMessages.UnlockList;
    end;
    if Messages <> nil then
    begin
      // send Messages using AContext.Connection.IOHandler as needed...
    end;
  finally
    Messages.Free;
  end;

  // check for incoming data...

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
  end;

  // process incoming data as needed...
end;

procedure TForm1.SomeProcedure;
var
  List: TIdContextList;
  Context: TMyContext;
begin
  List := IdTCPServer1.Contexts.LockList;
  try
    Context := TMyContext(List[SomeIndex]);
    Context.OutgoingMessages.Add('something');
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • It is perfectly safe to have one thread *reading* from a socket while another thread is *writing* to the same socket. What is not safe is having two or more threads *reading* from the same socket, or two or more thread *writing* to the same socket, at the same time. You have to synchronize the threads to make sure that one thread does not steal another thread's incoming data, and that one thread's outgoing data does not overlap another thread's outgoing data. – Remy Lebeau Sep 05 '15 at 00:00
  • [Edit your question](http://stackoverflow.com/posts/32368441/edit) to show your code. – Remy Lebeau Sep 05 '15 at 00:08
  • The code you have shown is not thread-safe. You are accessing UI controls without syncing with the main UI thread. You are accessing socket connections without protecting them from concurrent access by multiple threads. You have not applied any of the concepts I outlined in my answer above. You need to do so. – Remy Lebeau Sep 05 '15 at 01:23
  • yes but can this handle 100 clients ? or more? wouldn't it be slow? all "log" procedures will be thread safe or removed, it's not forever, i've added them to track the problem.. – user2200585 Sep 05 '15 at 02:28
  • Yes, what I describe can handle as many clients as `TIdTCPServer` can accept – Remy Lebeau Sep 05 '15 at 02:33
  • But I don't get it, why should my code fail? from Client wich sends data to another client (admin client) packets are being sent by queue, by the logic that should work just fine, but I think when one Client sends data and at that moment server Sends data to that client too there comes a "traffic jam", why should this happen, why socket isn't checking if the socket isn't in use.. In your code I think there is no Send Stream procedure, so I must change it? Sorry for my bad English – user2200585 Sep 05 '15 at 02:43
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88855/discussion-between-remy-lebeau-and-user2200585). – Remy Lebeau Sep 05 '15 at 04:46
  • @user2200585: What exactly are you still having a problem with? I already told you what you need to do. Have you spent any time in the past week applying the changes I described to you? If so, and you are still having problems, then please show your updated code. – Remy Lebeau Sep 12 '15 at 03:05
  • by your code sending data only works when client writes to server, am I right? I've done some work but can't show right now because I'm not home for couple days, will be day after tomorrow. – user2200585 Sep 12 '15 at 12:06
  • @user2200585: The code I gave you sends outgoing messages from the server to the client, even if the client never sends anything to the server. – Remy Lebeau Sep 12 '15 at 17:09
  • You are still accessing UI controls without syncing with the main UI thread. You MUST sync, or you are going to cause crashes/deadlocks/deadUI. Aside from that, this topic has been dragged on for almost 2 weeks now, we are not getting anywhere further with it. I'm done. I suggest you narrow down the problem and post new, smaller, focused questions about more specific issues. – Remy Lebeau Sep 15 '15 at 01:26