delphizmq
delphizmq copied to clipboard
Separate Terminate and Signal handling logic
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.
Hello, sorry for the delay.
A separate signal handling is a good idea, soon I'll put it into the code.
thanks.