



  constructor TLockupGuard.Create;
  var
    i: integer;
    k, c: TLockupCause;
    s: string;
    w: WideString;
  begin
    // if the application has already restarted itself due to timeout,
    //   display the error message box, then quit.
    For i:=1 to ParamCount do
      if copy(lowercase(ParamStr(i)), 1, 9) = '--lockup-' then begin
        s:= copy(ParamStr(i), 10, length(ParamStr(i)) - 9);
        c:= low(TLockupCause);
        for k:= low(TLockupCause) to high (TLockupCause) do
          if s = GetEnumName(typeinfo(TLockupCause), ord(k)) then c:= k;
        w:=PervertedFormat(RuEn(
          'Приложение зависло %0'#10#13'  и аварийно завершило себя (таймаут %1 секунд).',
          'The application have locked up %0'#10#13'  and terminated itself (time-out %1 seconds).'),
          [RuEn(LockupCauseName[c], LockupCauseNameEn[c]), LockupTimeout[c]]);
        AddLog(w);
        ErrorMessageBox(CGEString, w);
        Halt(0);
      end;

    // load the timeout values if defined in the config
    for k:= low(TLockupCause) to high (TLockupCause) do begin
      i:= Config.Int['lockup_guard', GetEnumName(typeinfo(TLockupCause), ord(k))];
      if i < 1 then continue;
      LockupTimeout[k]:= i;
    end;

    inherited Create(true, 100 * 1024); //create the thread suspended

    cs:= TCriticalSection.Create; //InitCriticalSection(cs);
    Checkup;
    Resume;
  end;

  destructor TLockupGuard.Destroy;
  begin
    cs.Free; //DoneCriticalSection(cs);
    inherited;
  end;

  procedure TLockupGuard.Execute;
  var pt, n: TDateTime;
  begin
    //since TDateTime is 64 bit, setting it may not be atomar on 32-bit systems. Thus the critical section.
    cs.Enter; //EnterCriticalSection(cs);
    pt:= Now();
    cs.Leave; //LeaveCriticalSection(cs);
    while not Terminated do begin
      Sleep(500); //half a second
      cs.Enter; //EnterCriticalSection(cs);
      n:= Now();
      cs.Leave; //LeaveCriticalSection(cs);
      if (n - pt) < (1 / SecondsPerDay) then begin //a full second

        //Perform the check ONLY if the lockup guard itself had't lagged (e.g. due to machine coming out of sleep)
        if n > f_timeout then DieAndBeReborn;
      end;
      pt:= n;
    end;
  end;

  procedure TLockupGuard.Guard(p: TProcedure; op: TLockupCause; timeout: integer = -1);
  begin
    if timeout <= 0 then timeout:= LockupTimeout[op];
    cs.Enter; //EnterCriticalSection(cs);
    f_timeout:= Now() + (1 / SecondsPerDay) * timeout;
    f_cause:= op;
    cs.Leave; //LeaveCriticalSection(cs);
    p;
    Checkup;
  end;

  procedure TLockupGuard.DieAndBeReborn;
  begin
    Self.Freeze;
    RestartMyself('--lockup-' + GetEnumName(typeinfo(TLockupCause), ord(f_cause)));
    KillMyself;
  end;

  procedure TLockupGuard.Checkup; // to be called each cycle, or else!
  begin
    cs.Enter; //EnterCriticalSection(cs);
    f_cause:= luc_Checkup;
    f_timeout:= Now() + (1 / SecondsPerDay) * LockupTimeout[luc_Checkup];
    cs.Leave; //LeaveCriticalSection(cs);
  end;

  procedure TLockupGuard.Freeze; //Suspend;
  begin
    cs.Enter; //EnterCriticalSection(cs);
    f_timeout:= Now() + 365.0; // one year
    cs.Leave; //LeaveCriticalSection(cs);
    //inherited
  end;

{ Ye bloody morons HAD to deprecate the suspend/resume mechanism. And of course the new function SetSuspended is NOT virtual.
  procedure TLockupGuard.Resume;
  begin
    inherited;
    Checkup; //just reset the countdown
  end;
}

