Fixed lazarus hanging when chmhelp pipe is leftover from an improperly closed lhelp

git-svn-id: trunk@37824 -
This commit is contained in:
andrew 2012-06-30 15:29:38 +00:00
parent eea630f3bb
commit ac177fc0e2
2 changed files with 73 additions and 14 deletions

View File

@ -23,6 +23,12 @@ unit lhelpcore;
{$NOTE You can add http capability to lhelp by adding the lnetvisual package v0.6.3 or greater requirement to lhelp.} {$NOTE You can add http capability to lhelp by adding the lnetvisual package v0.6.3 or greater requirement to lhelp.}
{$ENDIF} {$ENDIF}
{$IFDEF UNIX}
{$if FPC_FULLVERSION <= 20700}
{$DEFINE STALE_PIPE_WORKAROUND}
{$ENDIF}
{$ENDIF}
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
interface interface
@ -219,6 +225,7 @@ procedure THelpForm.FormCreate(Sender: TObject);
begin begin
fContext := -1; fContext := -1;
ReadCommandLineOptions; ReadCommandLineOptions;
LoadPreferences(fServerName);
if fServerName <> '' then begin if fServerName <> '' then begin
StartServer(fServerName); StartServer(fServerName);
end; end;
@ -237,7 +244,7 @@ begin
if FHasShowed then if FHasShowed then
Exit; Exit;
FHasShowed := True; FHasShowed := True;
LoadPreferences(fServerName);
end; end;
procedure THelpForm.ForwardToolBtnClick(Sender: TObject); procedure THelpForm.ForwardToolBtnClick(Sender: TObject);
@ -372,11 +379,15 @@ var
begin begin
fOutputIPC := TSimpleIPCClient.Create(nil); fOutputIPC := TSimpleIPCClient.Create(nil);
fOutputIPC.ServerID := fServerName+'client'; fOutputIPC.ServerID := fServerName+'client';
fOutputIPC.Active := True; if fOutputIPC.ServerRunning {$IFDEF STALE_PIPE_WORKAROUND} and not IPCPipeIsStale(fOutputIPC){$ENDIF}
then
fOutputIPC.Active := True;
Stream := TMemoryStream.Create; Stream := TMemoryStream.Create;
Stream.WriteDWord(Response); Stream.WriteDWord(Response);
fOutputIPC.SendMessage(mtUnknown, Stream);
if fOutputIPC.Active then
fOutputIPC.SendMessage(mtUnknown, Stream);
if fOutputIPC.Active then if fOutputIPC.Active then
fOutputIPC.Active := False; fOutputIPC.Active := False;
@ -474,6 +485,7 @@ end;
procedure THelpForm.StartServer(ServerName: String); procedure THelpForm.StartServer(ServerName: String);
begin begin
fInputIPC := TSimpleIPCServer.Create(nil); fInputIPC := TSimpleIPCServer.Create(nil);
fInputIPC.ServerID := ServerName; fInputIPC.ServerID := ServerName;
fInputIPC.Global := True; fInputIPC.Global := True;

View File

@ -2,9 +2,18 @@ unit LHelpControl;
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
{$IFDEF UNIX}
{$if FPC_FULLVERSION <= 20700}
{$DEFINE STALE_PIPE_WORKAROUND}
{$ENDIF}
{$ENDIF}
interface interface
uses uses
{$IFDEF STALE_PIPE_WORKAROUND}
BaseUnix,
{$ENDIF}
Classes, SysUtils, FileUtil, SimpleIPC, UTF8Process; Classes, SysUtils, FileUtil, SimpleIPC, UTF8Process;
type type
@ -48,7 +57,10 @@ type
property ProcessWhileWaiting: TProcedureOfObject read FProcessWhileWaiting write FProcessWhileWaiting; property ProcessWhileWaiting: TProcedureOfObject read FProcessWhileWaiting write FProcessWhileWaiting;
end; end;
{$IFDEF STALE_PIPE_WORKAROUND}
function IPCPipeIsStale(AIPC: TSimpleIPC): Boolean;
{$ENDIF}
implementation implementation
@ -76,8 +88,12 @@ end;
function TLHelpConnection.SendMessage(Stream: TStream): TLHelpResponse; function TLHelpConnection.SendMessage(Stream: TStream): TLHelpResponse;
begin begin
fServerOut.SendMessage(mtUnknown, Stream); //try
Result := WaitForMsgResponse; fServerOut.SendMessage(mtUnknown, Stream);
Result := WaitForMsgResponse;
//except
// on EIPCError do Result := srNoAnswer;
//end;
end; end;
constructor TLHelpConnection.Create; constructor TLHelpConnection.Create;
@ -97,9 +113,46 @@ begin
inherited Destroy; inherited Destroy;
end; end;
{$IFDEF STALE_PIPE_WORKAROUND}
function IPCPipeIsStale(AIPC: TSimpleIPC): Boolean;
var
PipeName: String;
fd: cint;
begin
Result := False;
PipeName:='/tmp/'+AIPC.ServerID;
if (AIPC is TSimpleIPCServer) and (TSimpleIPCServer(AIPC).Global) and (TSimpleIPCServer(AIPC).InstanceID <> '') then
PipeName := PipeName +'-'+TSimpleIPCServer(AIPC).InstanceID;
// it's possible to have a stale file that is not open for reading which will
// cause fpOpen to hang/block later when .Active is set to true while it
// wait's for the pipe to be opened on the other end
// O_WRONLY | O_NONBLOCK causes fpOpen to return -1 if the file is not open for reading
// so in fact the 'server' is not running
fd := FpOpen(PipeName, O_WRONLY or O_NONBLOCK);
if fd = -1 then
begin
Result := True;
// delete the named pipe since it's orphaned
FpUnlink(PipeName);
end
else
FpClose(fd);
end;
{$ENDIF}
function TLHelpConnection.ServerRunning: Boolean; function TLHelpConnection.ServerRunning: Boolean;
{$IFDEF STALE_PIPE_WORKAROUND}
{$ENDIF}
begin begin
Result := (fServerOut<>nil) and (fServerOut.Active); Result := (fServerOut<>nil) and (fServerOut.Active);
{$IFDEF STALE_PIPE_WORKAROUND}
if not Result then
Exit; // ==>
Result := not IPCPipeIsStale(fServerOut);
{$ENDIF}
end; end;
function TLHelpConnection.StartHelpServer(NameForServer: String; function TLHelpConnection.StartHelpServer(NameForServer: String;
@ -109,13 +162,6 @@ var
begin begin
Result := False; Result := False;
{$IFDEF Unix}
{$NOTE ToDo: fix simpleipc to allow changing the filenames, then fix this }
// clean up old files
DeleteFileUTF8('/tmp/lazhelp');
DeleteFileUTF8('/tmp/lazhelpclient');
{$ENDIF}
fServerIn.Active := False; fServerIn.Active := False;
fServerIn.ServerID := NameForServer+'client'; fServerIn.ServerID := NameForServer+'client';
fServerIn.Global := True; fServerIn.Global := True;
@ -123,7 +169,7 @@ begin
fServerOut.Active := False; fServerOut.Active := False;
fServerOut.ServerID := NameForServer; fServerOut.ServerID := NameForServer;
if not fServerOut.ServerRunning then begin if not ServerRunning then begin
with TProcessUTF8.Create(nil) do begin with TProcessUTF8.Create(nil) do begin
CommandLine := ServerExe + ' --ipcname ' + NameForServer; CommandLine := ServerExe + ' --ipcname ' + NameForServer;
Execute; Execute;
@ -131,6 +177,7 @@ begin
end; end;
// give the server some time to get started // give the server some time to get started
for X := 0 to 40 do begin for X := 0 to 40 do begin
// use fServerOut.ServerRunning here instead of Self.ServerRunning to avoid a race condition
if not fServerOut.ServerRunning then Sleep(200); if not fServerOut.ServerRunning then Sleep(200);
end; end;
end; end;