Delphi (Indy) Server Freezing on Write

2019-08-13 18:52发布

问题:

I currently have stable code between a client (IdTCPClient) and a server (IdTCPServer) working all as planned. When I call the following function from an OnClick event of a button, however, the server freezes.

var
  SendStream: TMemoryStream;
  FileStream: TFileStream;
  List: TList;
  I: Integer;
begin
  try
    //Load
    FileStream := TFileStream.Create(Path, fmOpenRead);
    FileStream.Position := 0;
    //Place into stream
    SendStream := TMemoryStream.Create;
    SendStream.Write(Header, SizeOf(Header)); //This works; supporting code ommitted  for brevity
    SendStream.CopyFrom(FileStream, FileStream.Size);
    SendStream.Position := 0;

    TIdContext(fTCP.Contexts.LockList[0]).Connection.IOHandler.Write(SendStream, 0, True);
    fTCp.Contexts.LockList;
   if Assigned(Self.fServerOnSync) then Self.fServerOnSync(SizeOf(Header)) //event handler for main form

  finally
    FileStream.Free;
    SendStream.Free;
  end;

I am guessing it has something to do with deadlocking threads but for the life of me I have no idea why it's occuring.

Also, if I encapsulate the above code in some class that contains the IdTCP server, which will call my custom fServerOnSync event, is this threadsafe?

Cheers, Adrian

回答1:

You are calling fTCp.Contexts.LockList() twice, but you are not calling fTCp.Contexts.UnlockList() at all, so the server will become deadlocked whenever it tries to access the Contexts list. You need to unlock the list after you have locked it, eg:

var 
  SendStream: TMemoryStream; 
  FileStream: TFileStream; 
  List: TList; 
  I: Integer; 
begin 
  FileStream := TFileStream.Create(Path, fmOpenRead or fmShareDenyWrite); 
  try 
    SendStream := TMemoryStream.Create; 
    try
      SendStream.Write(Header, SizeOf(Header));
      SendStream.CopyFrom(FileStream, FileStream.Size); 
      SendStream.Position := 0; 

      List := fTCP.Contexts.LockList; 
      try
        TIdContext(List[0]).Connection.IOHandler.Write(SendStream, 0, True); 
      finally
        fTCP.Contexts.UnlockList; 
      end;
      if Assigned(fServerOnSync) then fServerOnSync(SizeOf(Header)); 
    finally 
      SendStream.Free; 
    end; 
  finally
    FileStream.Free; 
  end;
  ...
end;