Jump to content

Dalija Prasnikar

Members
  • Content Count

    1061
  • Joined

  • Last visited

  • Days Won

    91

Posts posted by Dalija Prasnikar


  1. 4 hours ago, David Heffernan said:

    Isn't the top of the stack protected by guard pages? 

    Now we are going into territory I don't know well. 

     

    When guard pages are used, recovering from stack overflow exception requires some "manual" handling. While I don't know what Delphi actually does when it happens, it is fairly obvious that stack overflow exception corrupts memory as applications die almost immediately afterwards due to random AV exceptions or just get killed by the OS.  


  2. 2 is Stride parameter. However, it does not actually do what people think it does. It divides work in chunks that will run on same task effectively limiting number of threads used for running parallel for loop at a time. 

     

    Default value of stride is 1 which means that each pass will run independently of others. If the stride is 2, then Max number will be divided with 2 and there will be Max/2 number of chunks - tasks. If the Stride is same as Max then all passes will run inside single task.

     

    Someone writing this code though that stride defines index increments, but that is not so. Procedure will still run for each index from lower bound to upper bound  regardless of stride value and calculation of prime numbers will be run for index 1, 2, 3,...  

     

     

    • Thanks 1

  3. 14 hours ago, Der schöne Günther said:

    Absolutely, but I expected it to recover from it by popping the stack to the next exception handler and proceeding as usual.

    As the name says, stack overflow overflows the memory buffer reserved for stack. In other words, it writes data in memory that does not belong to the particular stack buffer. This can corrupt parts of memory used for something else and this is also why stack overflow exception is not recoverable and can crash the application.


  4. Your thread class Execute method is basically empty. Is there any other code in there you didn't show? If not then that is the reason your code is running sequentially, as threads don't do anything. Also using lower priority for threads can make threads wait longer to get the CPU slice. 


  5. On 4/28/2023 at 4:47 PM, Fr0sT.Brutal said:

    NO and never! I hate slow and fat apps. But vendors seem to think contrary. Skype, Slack (or Discord?) and many more turned from compact native apps to Electron based monsters.

    There are always other requirements besides speed. If the vendor requires support for many platforms then Electron is one of the available options that fits. Other requirements may be based on developers they have and their skillsets, as well as application features and target audience.

     

    For instance VSCode being written in Electron is foundation of their thriving plugin system not only because underlying technology is very suitable for that, but also because there is vast number of developers that can write those plugins.


  6. 13 minutes ago, Fr0sT.Brutal said:

    but my point here is that most of Delphi use cases are desktop apps where even awful performance is acceptable (these Electron apps) and compared to them Delphi code is fast enough.

    Are you sure that poor speed is acceptable on desktop applications? What about applications that need to load complex forms where opening such form can take half a minute or more? Or when you need to create plenty of business objects, or when you need to do some textual processing. Good compiler means a lot in such cases. In last few Delphi releases @Stefan Glienke has continuously submitted hand crafted code to improve speed of some widely used RTL routines that have impact on overall performance. With better compiler that would not be necessary. Not only that, but with better compiler all other code we write would be faster and when speed matters we wouldn't have to bend over backwards, writing horribly unreadable code just to get the speed we need. We could just write the clean and readable and maintainable code and leave the dirty work to the compiler.

     

    Speed always matters. Now, Delphi still might be fast enough for many purposes, but for many that is simply not enough. Once it was state of the art compiler, now it is seriously falling behind.

    • Like 8
    • Thanks 3

  7. I am sorry, but snippets of code where it is not clear what happens at which point and how everything is created and called are also not enough to figure out what exactly is wrong with your code.

     

    One of the things does sound suspicious: you say the thread is stuck in the limbo when timeout occurs and you have Sleep call which will cause thread to sleep if it is not terminated, Maybe there is a problem?

    • Like 1

  8. 3 hours ago, Ian Branch said:

    How do I achieve this please?

    Your code logic sounds backward. You should run code in FormCreate inside the calling form and then depending on the result create TChangesLogForm or not. Or if the code really belongs to TChangesLogForm, you should not run that code within FormCreate, but after the form is shown (along with some indication that there is a work in progress) and then if there is an error retrieving the data, you can show error message and close the form in that moment. 

     

     

    • Like 3

  9. Following code would do the job

    procedure TUExtractForm.LoadURLAsyncPOST(const AURL, AText: string);
    const
      CONST_TIMEOUT = 1000;
    begin
      TTask.Run(
        procedure
        var
          HttpClient: TNetHTTPClient;
          HTTPRequest: TNetHTTPRequest;
          ParamsStream: TBytesStream;
        begin
          ParamsStream := nil;
          try
            HttpClient := TNetHTTPClient.Create(nil);
            try
              HttpClient.ConnectionTimeout := CONST_TIMEOUT; // Timeout
              HttpClient.ResponseTimeout := CONST_TIMEOUT;   // Timeout
              HTTPRequest := TNetHTTPRequest.Create(HttpClient);
              HTTPRequest.Client := HttpClient;
              HTTPRequest.OnRequestCompleted := HTTPRequestCompleted;
              HTTPRequest.OnRequestError := HTTPRequestError;
              HTTPRequest.CustomHeaders['Content-Type'] := 'application/x-www-form-urlencoded';
    
              ParamsStream := TBytesStream.Create(TEncoding.UTF8.GetBytes('input=' + TNetEncoding.URL.Encode(AText)));
              HTTPRequest.Post(AURL, ParamsStream);
            finally
              HttpClient.Free;
              ParamsStream.Free;
            end;
          except
            on E: Exception do
              begin
                TThread.Queue(nil,
                  procedure
                  begin
                    ShowMessageFmt('Error: %s', [E.Message]);
                  end);
              end;
          end;
        end);
    end;
    
    procedure TUExtractForm.HTTPRequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
    var
      Content: string;
    begin
      if AResponse.StatusCode = 200 then
        begin
          Content := AResponse.ContentAsString;
          // replace #$A with new line
          Content := StringReplace(Content, #$A, sLineBreak, [rfReplaceAll]);
          URLMemo.Text := Content;
        end;
    end;
    
    procedure TUExtractForm.HTTPRequestError(const Sender: TObject; const AError: string);
    begin
      ShowMessageFmt('Error: %s', [AError]);
    end;

     

    However, your timeout setting is very low - one second, and this can cause issues if the server does not respond timely. I would just use default settings.

     


  10. If you want to use asynchronous mode for HTTP requests you should use TNetHTTPClient instead of THTTPClient. TNetHTTPClient is a wrapper that implements all the gory details in the background and leaves easy to use API for the developer.  https://docwiki.embarcadero.com/Libraries/Sydney/en/System.Net.HttpClientComponent.TNetHTTPClient 

     

    As far as synchronizing events is concerned, in THTTPClient they will run in the context of the background thread, so you need to synchronize them with main thread if you need to access the UI. TNetHTTPClient has property SynchronizeEvents and depending on its value events will run synchronized with the main thread or not. Default value is True, so events will run in the context of the main thread. If you drop TNetHTTPclient on the form you can use it in asynchronous mode with just few lines:

     

    Client.Asynchronous := True;
    Client.OnRequestCompleted := HTTPRequestRequestCompleted;
    Client.Get(’http://....’);

     

    However, asynchronous mode is hard to debug and control, and I would suggest using simpler approach using background threads or tasks.

     

    TTask.Run(
        procedure
        var
          Client: THTTPClient;
          Response: IHTTPResponse;
        begin
          Client := THTTPClient.Create;
          try
            Response := Client.Get('...');
          finally
            Client.Free;
          end;
          // process response in background thread
          ...
    
         // or pass it to the main thread   
          TThread.Queue(nil,
            procedure
            begin
              Memo.Lines.Add(Response.ContentAsString);
            end);
        end);

     

     

     

     

    • Like 1

  11. 1 hour ago, egnew said:

    Since I am using MethodAddress, I must publish methods I want to find by name.  This is easy and is the approach I am using.

    Well in that case you should do what the warning suggests. Add {M+} compiler directive before the class declaration or you can inherit from TPersistent which has RTTI generation turned on.

     

    type
      {$M+}
      TisEventHandler = class
      published
        procedure isPrintScreenClick(Sender: TObject);
      end;

     

    • Thanks 1

  12. Usually published directive is used in Delphi component streaming system for properties in components that will be set in design time and saved as part of form, frame or data module.

     

    You have used it for declaring a method and this is not a common use case (unless you want to find and invoke the method by using its name through RTTI). You probably wanted to use public directive instead of published in your code. This will remove the warning.


  13. To tell what is wrong, we need to see your class declaration and implementation - in this case most likely constructor is what matters. Showing TTimer code is not very helpful. So when you ask for help, you should post your code first.

     

    However, knowing that your code works when you construct component directly and it does not when you drop it on a form, narrows down potential cause and most likely culprit is that you didn't add override directive to your component constructor. When you call constructor directly specifying its class then your constructor will run and create timer component, but when you drop it on a form original virtual constructor chain from TComponent will run, but it will skip calling your constructor because it is not marked as part of the chain (missing override directive).

     

    type
      TMyComponent = class(...
      ...
      public
       constructor Create(AOwner: TComponent); override;
      ens;

     

    If that is the case, you would also see warning W1010 Method 'Create' hides virtual method of base type... This warning tells you that something is wrong with your declaration and that you most likely forgot to add override directive (there are other scenarios besides this one, but in your case this is the one that matters)

    • Like 2
    • Thanks 1

  14. 5 hours ago, Al T said:

    Every time I hover over a function, variable, procedure, etc.... I get "A valid license was not found. Please contact Embarcadero Support" .

    Maybe someone here had similar issue, but the fastest way to get help would be to contact support like the message says.

     

    You can do that through web. Make sure you choose Registration & Instalaltion https://www.embarcadero.com/support 


  15. 6 hours ago, Willicious said:

    So, what exactly does override do? From what I understand, it applies changes to procedures/functions with the same name that also exist in another unit/class. If override is needed here, then how can I prevent the error in that case?

    Override is used when you want to override virtual or dynamic method from the base class and change its behavior https://docwiki.embarcadero.com/RADStudio/Alexandria/en/Methods_(Delphi)#Virtual_and_Dynamic_Methods


  16. 14 hours ago, Ian Branch said:

    As was noted, att the App sits there until the process is finished.

    I would like, if needed, for the User to be able to move the form aside, out of the road, on the screen, or even another screen, while the process is running, if possible.  Particularly on long processes.

    Like @Remy Lebeau already said, both his and mine solution will do that. Your UI will be responsive as VCL main message pump will keep running while your thread is doing work in the background.

     

    If you are confused by DisableUI/EnableUI method names, code in those methods will not block that message pump either unless you put some infinite loop there on purpose. Both can be completely empty. You should put there code that will show/hide progress spinner or you can disable/enable some buttons so that user cannot start some other operation while backup is running.


  17. 17 minutes ago, programmerdelphi2k said:

    the use of variables to determine flow control, as used above, can lead to the false security of creating new threads/execution in the same unit, since any other "thread in the same unit" could change the value of these variables, allowing chaos is installed.

    While more complicated scenario with multiple threads could require more sophisticated code, this approach would work without issues even if there are multiple tasks that require running in background thread. There would be no chaos because only single thread can run at the time. This is what "if Processing then Exit" is here for. User will not be able to start another thread as long as some thread is already running. Only when thread finishes, new thread can be started.


  18. 11 hours ago, Ian Branch said:

    I have been advised that 'Application.ProcessMessages' is not thread safe.

    Is this the case?  If so, what is a better way to achieve the above?

    Application.ProcessMessages in not thread-safe means you cannot call it from the background thread. Since you are calling it from main thread that alone is not a problem in your code.

     

    However, one of the points of using threads is to keep application responsive to messages so you don't have to use Application.ProcessMessages which is not the best practice for many reasons.

     

    There are many ways for handling threads, and which one you choose depends on what particular thread does and the application logic. Since your code actually waits until the operation completes, then the simplest code that would achieve what you want with threads would be something like this:

     

      TMainForm = class(TForm)
      private
        Processing: Boolean;
        procedure EnableUI;
        procedure DisableUI;
        ...
      end;
    
    procedure TMainForm.BtnClick(Sender: TObject);
    begin
      if Processing then
        Exit;
      Processing := True;
      try
        DisableUI;
        TThread.CreateAnonymousThread(
          procedure
          begin
            try
              // do work here
            finally
              TThread.Queue(nil,
                procedure
                begin
                  EnableUI;
                  Processing := False;
                end);
            end;
          end).Start;
      except
        EnableUI;
        Processing := False;
      end;
    end;
    
    procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    begin
      CanClose := not Processing;
    end;

     

    In EnableUI and DisableUI methods you can put whatever code you need to show the user that you are doing some work or disable/enable some actions. The rest is pretty simple - if user tries to start processing again, code will not let it do that (that is something you should have even when you use Application.ProcessMessages as it allows reentrancy and user could start same operation while previous one is not finished). When thread is done (regardless of whether it completes successfully or not), it will enable UI (this needs to be called from the main thread and this is why there is TThread.Queue (or you can use TThread.Synchronize here - the main difference is that Queue is non blocking call and Synchronize is blocking). You can also catch exceptions and show message to user in similar manner. Thread will self-destruct after it is finished so you don't have to worry about its memory management.

     

    To avoid complicated cleanup and potentially breaking unfinished work thread is doing, the simplest way is to prevent user from closing form while thread is working in FormCloseQuery event. 

     

    Note: Running DBSBackup.Execute opens the question about thread-safety of that code - in other words whether main thread can use DBSBackup instance and anything directly related while thread is running - which would not be thread-safe to do. But that part is another story.

     


  19. 5 hours ago, Willicious said:

    I'm getting the E2137 error when trying to add a new procedure.

    This error happens when you add override directive to the method and method with same name does not exist in the class hierarchy from which you are inheriting. If you just wanted to add new method which does not exists, then remove override.

     

    The "check spelling" suggestion comes from the most common mistake when overriding existing method is that you mistype the name, so the name does not match to the name of existing method in base class which you want to override.


  20. 6 hours ago, c0d3r said:

    Using Delphi 10.4.2,  just wondering if TCompressionStream and TDecompressionStream in System.Zlib unit thread safe or not?

    Generally any kind of stream should be suitable for using in background threads, but also they cannot be simultaneously accessed and shared between threads. Just look at it logically - how can you use stream that holds a state (if nothing else than current position that changes as you read and write) be thread-safe? 

     

    While you could in theory protect simultaneous access with locks and share stream across threads, you wouldn't gain much from that as stream would have to be locked and accessible to one thread at the time. If you think you could speed some operation by sharing stream and using is in parallel, you will only slow the whole process down, not speed it up.

     

    When using TCompressionStream and TDecompressionStream in background thread, OpProgress event handler will run in the context of background thread and you need to take that into account if you want to show progress in the UI and synchronize code.

    • Like 3
×