Jump to content

Dalija Prasnikar

Members
  • Content Count

    729
  • Joined

  • Last visited

  • Days Won

    63

Everything posted by Dalija Prasnikar

  1. Dalija Prasnikar

    Many TTasks

    Tasks use threads from associated thread pool. If you don't explicitly specify thread pool, default one will be used. Maximum number of threads each pool can start (use) can be modified for each pool, and default value is number of CPUs * 2. (This value is valid for Delphi 11, it is possible that it was some other value in previous versions) So if you have 8 CPUs and you start 9 tasks, they will start 9 threads running in parallel, unless one of the tasks manages to finish before all 9 tasks are queued to tasks work queue. You should always assume that tasks will run in parallel, in other words you cannot count on particular task finishing before some other task.
  2. Dalija Prasnikar

    Asynchronous Programming Library

    IAsyncResult as abstract interface is not a problem. It is not a problem that you can choose the async backend. It is a problem because that backend support is directly backed inside the class that needs to do some work. It is a problem because that is all done via inheritance on classes that should not care about all that. It is abstraction imposed on a wrong level. Not necessarily. It is difference between debugging and inspecting your code, vs landing on some irrelevant framework pieces to inspect what is happening and where you have particular problem. Try debugging this, where it is questionable will you even land on RequestCompleted handles in case of exception in the request procedure TMainForm.Button1Click(Sender: TObject); begin HttpClient.Asynchronous := True; HttpClient.Get('http://httpbin.org/get'); end; procedure TMainForm.HTTPRequestRequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse); begin TThread.Synchronize(nil, procedure begin Memo.Lines.Add(AResponse.ContentAsString); end); end; versus debugging this procedure TMainForm.Button1Click(Sender: TObject); begin TTask.Run( procedure var Client: THTTPClient; Response: IHTTPResponse; begin Client := THTTPClient.Create; try Response := Client.Get('http://httpbin.org/get'); TThread.Synchronize(nil, procedure begin Memo.Lines.Add(Response.ContentAsString); end); finally Client.Free; end; end); end; Yes, the second example is longer, but it is all in code and does not require any design time wiring. Another problem here is that because THttpClient implements support for IAsyncResult, it is rather complex class. So stepping into such code regardless of how you ended up there will be hard to follow. Without IAsyncResult that class could be much simpler not just to debug and understand, but also easier to maintain.
  3. Dalija Prasnikar

    Asynchronous Programming Library

    Low code is still a thing, or at least that is what "marketing" is selling to us as greatest feature ever. It may be good for non programmers that want to put few things together, but it is really bad for any even remotely complex application. The fastest way to sink an application is to drown it in low code. I had my share of bad design choices some ten years ago... following similar route - slap everything on TComponent and drag and drop design... I am still maintaining that crap and I cannot easily untangle the mess.... I wish someone hit me on the head back then, it would spare me a lot of trouble.
  4. Dalija Prasnikar

    Asynchronous Programming Library

    Thanks! Look at the dates... he wrote about it in 2008 and the thing was included in 2015 in XE8. Why I have the feeling it was added just to add some check mark on number of new features, without thinking about the impact. At that point having proper cancellation token would be more useful feature. How is this useful in combination with PPL I still cannot figure out. Proof of concept is one thing, imposing it on TComponent is another. If it would be in domain of helper methods, then it would probably be more flexible (adjusting the behavior through inheritance is very rigid) Of course, I understand why it was implemented directly... because we can only have single helper in scope Maybe, it is handy for some quick RAD kind of development, where you need to bang up some example code fast and you need to make the application responsive. One place I know this is used is in System.Net. But, have you ever tried debugging such asynchronous HTTP requests. It is mission impossible, you have to jump through the hoops not knowing where you can safely put your breakpoint to land on some code you need to inspect because it is buried deep. Not to mention that handling request and response code is broken down scattered around in order to satisfy BeginInvoke/EndInvoke pattern. And if you take the look at the THTTPClient declaration, with zillion various Begin... methods, it becomes more obvious that implementing asynchronicity on this level is not the best idea. It brings needles complexity. Asynchronous frameworks should be implemented as wrappers around code and tasks (like PPL), not be implemented on the same level (from within the class that performs some operation). This simply does not scale well.
  5. Dalija Prasnikar

    Is Delphi's HTTPRIO thread-safe?

    No. When you use it, even if you don't change the properties, there are other state (encapsulated data) changes happening and this is where multiple threads can interfere with each other.
  6. Dalija Prasnikar

    Asynchronous Programming Library

    Nope. Frankly, it is abomination. My primary objection is that it slaps control flow into TComponent where it does not belong. TComponent is not thread safe class and asynchronous operations and multithreading imposed on that level open recipe for disaster. It does not give you more control, it takes it away from you and it is overly complicated. Maybe there are some small use cases, but I still haven't found any. At the same time we don't have cancellation token implemented in PPL which desperately needs it. Good implementation of cancelation tokens could then be used in other code even asynchronous one that does not run in threads. I have no idea what is original intent behind that library, so maybe I am missing something obvious.
  7. Dalija Prasnikar

    Is Delphi's HTTPRIO thread-safe?

    It is not thread safe. You cannot share one instance among threads.
  8. Dalija Prasnikar

    Exception classes implementing interfaces

    This sounds like a solution in search of a problem. As previously mentioned there are better options.
  9. Dalija Prasnikar

    debug IOS using instruments Leak detector

    There is no "unified" handling of the TOCLocal instances, because what you need to do depends on how you got the instance. So that part seems fine. destructor TFMXTextRange.Destroy; begin FStart.free; // added by bb FEnd.free; // added by bb inherited; end; As far as TFMXTextRange is concerned there should definitely be calls to Free, niling is redundant. But, those classes are reference counted, so they should have never been stored in object references to begin with FStart: TFMXTextPosition is wrong (and if they would be stored in interface references then Free would not be needed). This is where those changes in TOCInterfacedObject possibly come to play, to prevent issues with reference counting and allowing instances to be stored in object references. But in that case anything stored in interface reference would be broken. So the whole thing is a bit incomprehensible to me right now, because it also depends on how is each instance stored (interface or object reference), whether Free is called or not. The only way to easily see what is going on is through debugging and I cannot do that right now. Bottom line... FStart.Free and FEnd.Free should have some impact on reducing number of leaks, but it is quite possible that there are other leaks due to changes in TOCLocal. If you put breakpoint at FStart := nil you should be able to see whether TFMXTextPosition.Destroy is called. If it is then, niling is fine. If not, try with FStart.Free. If that calls destructor that is first fix you need to do. If you still have leaks, you can try calling FStart.Release before FStart.Free to explicitly release ObjectiveC object behind it. If you get segmentation fault afterwards, then this is wrong. I am sorry that I cannot be of more help.
  10. Dalija Prasnikar

    debug IOS using instruments Leak detector

    Maybe not.... I haven't looked at iOS since Rio was released and I haven't used it with 10.4 when ARC compiler was removed. There are some weird changes in Macapi.ObjectiveC specifically in TOCLocal class declaration which now uses TOCInterfacedObject. But that requires deeper look and I cannot do that right now, plus I don't have my Mac fully setup to work with Delphi. But, maybe it will give you some clues as where to look. It seems like changes are there to prevent reference counting destroying the object, but in that case there is definitely need for calling Free on anything constructed. Unless compiler is doing some magic behind the scenes for those classes.
  11. Dalija Prasnikar

    LSP - Alexandria

    No, there is no flag. Classic code insight has been completely removed in Delphi 11. I don't know the details, but there were some interference issues that could not be resolved otherwise.
  12. Dalija Prasnikar

    Delphi 11 TRestRequest Failure

    Probably related to this issue https://quality.embarcadero.com/browse/RSP-35341
  13. Dalija Prasnikar

    Calling inherited in Destroy

    Not really. It is the most lightweight list class that can be used there as dynamic arrays don't have capacity which prevents excessive memory reallocations when growing array. Question is, whether the whole algorithm could be implemented in a different way, but that is another story (I am not saying that it can be better as I haven't done any analysis)
  14. Dalija Prasnikar

    Calling inherited in Destroy

    Always call inherited in destructor. I would also add always call inherited in constructor, but if you miss to call constructor that does something, you will know soon enough. If you don't call destructor, you may have leaks which can be much harder to detect later on. This is extremely low level optimization and it makes sense only in some code that will be called a lot, but even then think four times before you write it as changes in class declaration - using different ancestor can break such code badly. I have this kind of optimization in 3 places in my code in my smart pointer, weak and lazy reference implementations. Inherited is there, it is just commented out and there is additional comment explaining why it is there. If anything breaks in TObject, I have only one file to change. But even now, while I am writing this I feel extremely uneasy about that code. So don't do write such code, unless you really have good reason, and don't write it even then.
  15. Dalija Prasnikar

    splitting interface + implementation

    The only thing that includes terms "interface" and "implementation" and that CAN be separated in different files are interface declarations and classes that implement those interfaces and implementing classes will again have interface and implementation part in the unit file itself. If it was Nick's book, it could be Coding in Delphi or Dependency Injection in Delphi, but again you will not find anything there about separating interface and implementation parts of the unit because that cannot be done (besides using include files), only how to use interfaces.
  16. Dalija Prasnikar

    Class Instance vs Object Instance

    Don't ever call NewInstance - this s internal method. https://docwiki.embarcadero.com/Libraries/Sydney/en/System.TObject.NewInstance You should call constructor to create (allocate) new object. vRef := TBaseForm(AFormClass.Create(aOwner)); or you can typecast with as operator which will fail at runtime if the AFormClass is not TBaseForm vRef := AFormClass.Create(aOwner) as TBaseForm; When you declare variable var vRef: TBaseForm; This will only automatically allocate pointer (reference) to your future form - if this is local variable it will be allocated on the stack, if it is field it will be allocated as part of object instance. This is automatic and you don't need to manage memory for that pointer, variable itself. Only when you call constructor actual object instance will be created and allocated - if you create form with owner, that owner will be responsible for releasing its memory. If you pass nil for owner, you need to release it yourself. There is no class instance here, only object instances. Class instance is constructed when you declare class variables in class declaration - and they are automatically allocated and deallocated and there is only single one per class. Again you are not using them in your code. Following is example of class variable declaration. Variable Foo is accessible as TBaseForm.Foo or as vRef.Foo but it will be the same shared integer variable. TBaseForm = class(TForm) public class var Foo: Integer; end;
  17. Dalija Prasnikar

    How to manage feature changes during release cycle?

    This can happen even if you don't use any VCS Not the conflict per-se, but changes in common code if projects are not separated can cause problems and other project may no longer work properly or even compile.
  18. Dalija Prasnikar

    How to manage feature changes during release cycle?

    Yes, of course. Point is that merge conflicts are often used as an argument against branches and to a point branches can escalate problem. For instance single developer developing on single branch will not have merge conflicts, but reckless use of branches can cause issues even for single developer, which often leads to the conclusions that branches are inherently bad, not the how they are used.
  19. Dalija Prasnikar

    How to manage feature changes during release cycle?

    Main issue with branches are merge conflicts. They tend to get worse with time because more code is added and there is potentially more conflict. So naturally, if you have short lived branches there is less possibility for conflicts to emerge. Also continuous delivery focuses on making small incremental changes (which is not always possible) so making small features that don't span across too much code (files) tend to be easily mergeable. Having said that, the lifetime alone actually means nothing. Nor the number of branches. First, you can easily shoot yourself in the foot with the branch old just a few hours, if you change some "hard" to merge file - think something like dproj file. On iOS, macOS that would be storyboards - you don't even need multiple developers to make a mess. So for some types of files, you will need to have specific instructions about who and how can change them without causing issues. Next, it is not how long branch lives, but whether you keep it up to date with other development. If you have branch and you merge it after a year, of course there will be trouble, but not because the branch is year old, but because you didn't keep it up. The most important rule is don't merge, rebase and rebase often. Not only that keeps history cleaner, but you will also have less conflicts - most of the time there will be none or they will be simple to resolve. And of course, in larger teams there has to be some coordination between what is done and when. It makes no sense to start some large refactoring in one part of the code few days before another large refactoring in overlapping area of the code is finished. It is better to wait than waste time on merging.
  20. Dalija Prasnikar

    New FMX project dose not launch

    I am not questioning your decision, I am merely explaining why you cannot easily use older Delphi versions for mobile development. No.
  21. Dalija Prasnikar

    New FMX project dose not launch

    I don't know what OS version S21 uses, but 10.2.3 is rather old version. There are API changes in Android every year that can have impact on compatibility. Just changing targetSdkVersion does not generally help, because it is not just about changing the number. When you change that number you are saying that your application supports particular Android OS and its capabilities, while application you are building with old Delphi version will not fully support new OS. This is why for mobile development you need to keep current with latest Delphi as it has added support for newer versions of mobile OS.
  22. Dalija Prasnikar

    Why empty dynamic arrays = NIL?

    I am few dimes short...
  23. Dalija Prasnikar

    Why empty dynamic arrays = NIL?

    True. But that is not the argument for nil string and arrays, on the contrary. I am all for having nullable types built in, together with full language support, but that does not mean I am for changing how strings and arrays work. Nullable types should be wrapper on around those and value types.
  24. Dalija Prasnikar

    Why empty dynamic arrays = NIL?

    But you can have your own implementation. I don't know about C#, but handling strings or arrays in Java is nightmare. You need to check for nil before you can do anything with it. Yes, if you have created it few lines of code before then you don't have, but usually you don't know from where it comes from and you need to check. If I had a dime for every time I though "this string cannot be nil here, so I don't need to check" and then I had exceptions at runtime, I would be on my tropical island right now, drinking pina colada.
  25. Dalija Prasnikar

    Why empty dynamic arrays = NIL?

    If you need that distinction you can always wrap it up in nullable type. Delphi implementation of strings and dynamic arrays is fine as it is. Adding that distinction into array or string itself would only complicate working with strings and arrays with no benefits in most of the code. What would be default value of the array or string? nil or empty? If it is nil, welcome to nil nightmare, if it is empty, you need nullable again, or you would just want the ability to nil after it is initialized to empty. This is the fastest road to hell.
×