Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TAnonymousThread.Execute_Sync question.. #134

Open
ijbranch opened this issue Sep 19, 2024 · 10 comments
Open

TAnonymousThread.Execute_Sync question.. #134

ijbranch opened this issue Sep 19, 2024 · 10 comments

Comments

@ijbranch
Copy link

ijbranch commented Sep 19, 2024

D12.2, 32 bit App.
I have implemented this code:

TAnonymousThread.Execute_Sync(procedure
      begin
        try
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          //
        except
          // Handle any exceptions that might occur during the execution of the SQL code
          on E: Exception do ShowMessage('Error performing database backup: ' + E.Message);
          //
        end;
      end
      )
      .Start;

I interpreted Execute_Sync would allow me to move the Form while the thread executed.
It would seem not.
How to be able to please?

Regards & TIA,Ian

@exilon
Copy link
Owner

exilon commented Sep 25, 2024

Use Execute_Sync only if inside code updates UI like a button, label, etc. In your case seems you're not updating any UI interface.

@ijbranch
Copy link
Author

ijbranch commented Sep 25, 2024

Ahhh. Ok. Tks.
So, using just "TAnonymousThread.Execute(procedure..." then, how do I keep the UI responsive so the form can be moved?
Or do I use one of the other Quick.Threads options?

@exilon
Copy link
Owner

exilon commented Sep 25, 2024

Sorry, Showmessage is UI dialog, you need Execute_Sync()
Where you're calling this? There're any code after this call?

@ijbranch
Copy link
Author

ijbranch commented Sep 25, 2024

This is the complete function:

function TMainForm.ARBackup: Boolean;
begin
  //
  try
    //
    nlhWaiter2.Active := True;
    //
	// Create an anonymous thread to execute the SQL code
  TAnonymousThread.Execute_Sync(procedure
      begin
        try
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          //
        except
          // Handle any exceptions that might occur during the execution of the SQL code
          on E: Exception do ShowMessage('Error performing database backup: ' + E.Message);
          //
        end;
      end
      )
      .Start;
    //
    Result := True;
    //
    nlhWaiter2.Active := False;
    //
  except
    on E: Exception do
    begin
      //
      Screen.Cursor := crDefault;
      if (E is EDatabaseError) and (E is EEDBError) then
        StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' +
          sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0)
      else
        StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF +
          'Please inform your Workflow System Administrator.', mtError, [mbOK], 0);
      //
      btnClose.Enabled := True;
      btnRestore.Enabled := True;
      btnBackup.Enabled := True;
      //
      Backup.Refresh;
      Backup.Last;
      //
      Screen.Cursor := crDefault;
      //
      lBackupDone := False;
      //
      Result := False;
      //
    end;
    //
  end;
  //
end;

@exilon
Copy link
Owner

exilon commented Sep 25, 2024

Try something like this:

function TMainForm.ARBackup: Boolean;
begin
	// Create an anonymous thread to execute the SQL code
  TAnonymousThread.Execute_Sync(procedure
      begin
        nlhWaiter2.Active := True;
          //
          var dtStart: TDateTime := Now;
          //
          DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
            now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
          //
          BackupsLog.Open;
          BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
          BackupsLog.Close;
          Result := True;
      end;
      )
      .OnException(procedure(aException : Exception)
      begin
        //
        ShowMessage('Error performing database backup: ' + aException.Message);
        Screen.Cursor := crDefault;
        if (E is EDatabaseError) and (E is EEDBError) then
          StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' +
            sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0)
        else
          StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF +
            'Please inform your Workflow System Administrator.', mtError, [mbOK], 0);
        //
        btnClose.Enabled := True;
        btnRestore.Enabled := True;
        btnBackup.Enabled := True;
        //
        Backup.Refresh;
        Backup.Last;
        //
        Screen.Cursor := crDefault;
        //
        lBackupDone := False;
        //
        Result := False;
      //
     end)
    .OnTerminate_Sync(procedure
      begin
        nlhWaiter2.Active := False;
      end;)
    .Start;
end;

@ijbranch
Copy link
Author

Thank you.
Unfortunately Result does not carry into the Thread. Nor it seems does E for the Exception.

image

@exilon
Copy link
Owner

exilon commented Sep 26, 2024

Sorry, I did without an Delphi IDE. You need to change E variable with aException procedure parameter inside OnException.
Result is not needed because you have to change to procedure. No senses to have a function than launches an async method without waiting for them and get a result. After .start, delphi continues with execution, but TAnonymousThread opens a separate execution thread.

@jkour
Copy link
Contributor

jkour commented Nov 13, 2024

@ijbranch Hi, this is how you can use TAnonymousThread in your case:

function TMainForm.ARBackup: Boolean;
begin
  nlhWaiter2.Active := True;

  TAnonymousThread.Execute(procedure
      begin
        //
        var dtStart: TDateTime := Now;
        //
        DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn',
          now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG');
        //
        BackupsLog.Open;
        BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]);
        BackupsLog.Close;
      end)
    .OnTerminate_Sync(procedure
      begin
        //
        Result := True;
        //
        nlhWaiter2.Active := False;
        //
      end)
    .OnException(procedure (E: Exception)
      begin
         Screen.Cursor := crDefault;
          if (E is EDatabaseError) and (E is EEDBError) then
            StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' +
              sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0)
          else
            StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF +
              'Please inform your Workflow System Administrator.', mtError, [mbOK], 0);
          //
          btnClose.Enabled := True;
          btnRestore.Enabled := True;
          btnBackup.Enabled := True;
          //
          Backup.Refresh;
          Backup.Last;
          //
          Screen.Cursor := crDefault;
          //
          lBackupDone := False;
          //
          Result := False;
      end
      )
      .Start;
end;

A few things to consider in your code:

  1. nlhWaiter2 should be disabled when an exception occurs as well
  2. Should the buttons be enabled when the thread terminates without exceptions?
  3. You've got Screen.cursor twice
  4. I would probably have the "Result:=false" at the beginning and then alter the value only in successful completion

Hope this helps

@ijbranch
Copy link
Author

jkour - Thank you. Appreciated.
I now have this:
function TBackUpsForm.ARBackup: Boolean; begin // nlhWaiter2.Active := True; Result := False; // TAnonymousThread.Execute(procedure begin // var dtStart: TDateTime := Now; // dmB.DBSBackup.Execute('BACKUP DATABASE "DBiArchive" AS "DBiArchive Manual Backup - ' + FormatDateTime('yyyy-mm-dd @ HHnn', now) + '" TO STORE "Backups" COMPRESSION 9 INCLUDE CATALOG'); // BackupsLog.Open; BackupsLog.AppendRecord([dtStart, now, 'DBiArchive', 'Manual', AUD.UserID, now]); BackupsLog.Close; end) .OnTerminate_Sync(procedure begin // Result := True; // nlhWaiter2.Active := False; // end) .OnException(procedure(E: Exception) begin Screen.Cursor := crDefault; // nlhWaiter2.Active := False; // if (E is EDatabaseError) and (E is EEDBError) then StyledTaskMessageDlg('Unhandled Database error!', 'An Unexpected database error # ' + EEDBError(E).ErrorCode.ToString + ' occurred!' + sCRLF + EEDBError(E).ErrorMsg + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0) else StyledTaskMessageDlg('Unhandled error!', 'An unexpected error has occurred while doing the Backup!' + sCRLF + E.Message + sCRLF + 'Please inform your Workflow System Administrator.', mtError, [mbOK], 0); // Backup.Refresh; Backup.Last; // lBackupDone := False; // end ) .Start; // btnClose.Enabled := True; btnRestore.Enabled := True; btnBackup.Enabled := True; // end;
Delphi is telling me that Result in .OnTerminate
image

Regards,
Ian

@jkour
Copy link
Contributor

jkour commented Nov 14, 2024

Hi @ijbranch ,

Yes, that is correct. Result to a function can't be captured within a thread.

So, there are two options:

  1. Either you have a Status field in TMainForm that is altered
  2. Or, you use a different technique. This is how I generally do this (keep in mind that I am writing off the top of my head, as I am away from my PC right now).
function TMainForm.ARBackup: boolean;
var 
  shouldContinue: boolean;
  intResult: boolean;
begin
  result:=false;
 // nlhWaiter2.Active:=true;

 shouldContinue:=false;
 intResult:=false;

 TAnonymousThread.Execute(procedure
      begin
        //
       // ---> do stuff here
       
      shouldContinue:=true;
      intResult:=true; // Or, false, depending on the result in the database
     end)
    .OnTerminate_Sync(procedure
      begin
        //
        shouldContinue:=true;
        intResult := True; // Or, false, depending on the result in the database
        //
        nlhWaiter2.Active := False;
        //
      end)
    .OnException(procedure (E: Exception)
      begin
          // ---> do stuff here
         shouldContinue:=true;
         intResult:=false;
      end
      )
      .Start;

while not shouldContinue do
   ; /// ---> or, Sleep(1);

result:=intResult;

end;

I mentioned earlier that you can have a Status field in the class. If you do this, you can have the Styled Message dialogs outside the thread and after the while...do loop.

This will also allow you to move all the database operations to a class separating the business model from the UI part of your application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants