bvarga / delphizmq

0MQ Delphi binding
GNU Lesser General Public License v3.0
137 stars 63 forks source link

Separate Terminate and Signal handling logic #8

Open uaply opened 11 years ago

uaply commented 11 years ago

Now zmqapi unit installs Ctrl-C handlers, which is good idea for clean termination of application. But such signal handler could also be helpful for other task, so unit should only expose such handler, but not install it by default and unconditionally.

Inspired by the code found in zmqapi.pas, I wrote separate module for handling break signals. I compiled it in FPC 2.7.1 and tested under Windows XP and Linux.

My application is multi-threaded, so signal handler should not only free ZMQ contexts, but also terminate all running threads.

Under Linux when you catch a signal default action is not executed, so when I intercept SIGINT or SIGTERM it's my duty to exit application (or not exit, if it is impossible right now). So I also implemented the same policy under Windows - ConsoleCtrlHandler() returns True, and Windows doesn't kill application - it should exit by itself.

I've found than in my case there is no need to Terminate ZMQ context inside signal handler.

The main program looks something like

var
  MyThread: TMyThread;
  zmqContext: TZMQContext;

procedure BreakSignalHandler;
begin
  writeln('Break signal received');
  MyThread.Terminate;
end;

begin
  OnBreakSignal := @BreakSignalHandler;
  InstallBreakSignalHandler;

  zmqContext := TZMQContext.Create;

  MyThread := TMyThread.Create(True);
  MyThread.Start;
  MyThread.WaitFor;

  //zmqContext.Terminate;

  zmqContext.Free;
end.

And with such approach signal handler only notifies application that is should exit, so there is no need in convoluted code performing Terminate on all contexts from inside handler. ZMQ context is explicitly destroyed by main application, not by library.

Maybe we could leave TZMQContext.Terminate as helper method, and also provide some TerminateThemAll procedure for convenience. But it should not be called by library, only by developer who decide to put in signal handler or somewhere else.

Here is my code of signal handlers. I think it could be incorporated somehow in the project, maybe directly to zmqapi.pas or maybe as separate helper unit.

unit BreakSigHandler;

interface

type
  TBreakSignalHandler = procedure;

var
  OnBreakSignal: TBreakSignalHandler = nil;

procedure InstallBreakSignalHandler;
procedure RestoreBreakSignalHandler;

implementation

uses
  {$if defined(unix)}
  BaseUnix;
  {$elseif defined(mswindows)}
  Windows;
  {$else}
  {$WARNING Support for signal handler not implemented for this platform!}
  ;
  {$ifend}

{$if defined(unix)}

procedure SigHandler(signum: longint; siginfo:PSigInfo; sigcontext: PSigContext); cdecl;
begin
  if Assigned(OnBreakSignal) then
    OnBreakSignal;
end;

procedure InstallSignalHandler(signum: longint; out oldact: SigActionRec);
var
  act: SigActionRec;
begin
  FillChar(act, sizeof(SigActionRec),0);
  act.sa_handler := SigActionHandler(@SigHandler);
  FpSigAction(signum,@act,@oldact);
end;

var
  oldsigint: SigActionRec;
  oldsigterm: SigActionRec;

{$elseif defined(mswindows)}

function ConsoleCtrlHandler(dwCtrlType: dword): bool; stdcall;
begin
  // CTRL_SHUTDOWN_EVENT is received only by services
  if dwCtrlType in [CTRL_C_EVENT, CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT] then begin
    if Assigned(OnBreakSignal) then
      OnBreakSignal;
    Result := True;
  end else begin
    Result := False;
  end;
end;

{$ifend}

procedure InstallBreakSignalHandler;
begin
  {$if defined(unix)}
  InstallSignalHandler(SIGINT,oldsigint);
  InstallSignalHandler(SIGTERM,oldsigterm);
  {$elseif defined(mswindows)}
  SetConsoleCtrlHandler(@ConsoleCtrlHandler, True);
  {$ifend}
end;

procedure RestoreBreakSignalHandler;
begin
  {$if defined(unix)}
  FpSigAction(SIGINT,@oldsigint,nil);
  FpSigAction(SIGTERM,@oldsigterm,nil);
  {$elseif defined(mswindows)}
  SetConsoleCtrlHandler(@ConsoleCtrlHandler, False);
  {$ifend}
end;

end.
bvarga commented 11 years ago

Hello, sorry for the delay.

A separate signal handling is a good idea, soon I'll put it into the code.

thanks.